121 lines · 5.4 KB
import { all, run } from "../db";
import { randomId } from "../crypto";
import { authenticatedRemote, mintRepoToken } from "../git/artifacts";
import { completeJob, isCloudJob, loadJobExecution, markJobStarted, resolveSecrets, runnerStub } from "./jobs";
import type { WorkflowJobRow } from "./jobs";
import { buildJobScript } from "./script";
import { stepSecretNames } from "./compile";
import { jobTokenEnv, mintJobToken } from "./job-token";
import { CLOUD_RUNS_ON_VALUES, cloudRunsOnPlaceholders } from "./runner-sizes";
import { MAX_RUNNER_JOBS } from "./runner-registration";
import type { RunnerRow } from "./runner-registration";
import { LABELS_MATCH_SQL } from "./runner-policy";

const TOKEN_GRACE_SECONDS = 600;
const CLAIM_CANDIDATES = 25;
/** Values shorter than this are too common to mask without redacting ordinary log text. */
const MIN_MASK_LENGTH = 4;

/** Labels come only from the registration record; whatever the daemon sends with a claim is ignored. */
function runnerLabels(runner: RunnerRow): Set<string> {
  return new Set([...(JSON.parse(runner.labels) as string[]), "self-hosted"].map((label) => label.toLowerCase()));
}

function labelsMatch(required: string[], available: Set<string>): boolean {
  return required.length > 0 && required.every((label) => available.has(label.toLowerCase()));
}

/**
 * Candidate queued jobs inside the runner's scope. Fork pull request (untrusted) runs are only eligible when a
 * repo admin opted in, and then only for runners registered to that repo with admin-attested container isolation, so a shared
 * owner-wide or unisolated runner never executes fork code.
 */
async function candidateJobs(runner: RunnerRow): Promise<WorkflowJobRow[]> {
  const cloud = cloudRunsOnPlaceholders(6);
  return all<WorkflowJobRow>(
    `SELECT j.* FROM workflow_jobs j JOIN repos repo ON repo.id = j.repo_id JOIN workflow_runs w ON w.id = j.run_id
     JOIN (SELECT ?5 AS labels) r
     LEFT JOIN runner_policies p ON p.repo_id = repo.id
     WHERE j.status = 'queued' AND (repo.id = ?1 OR repo.owner_id = ?2) AND j.runs_on NOT IN (${cloud})
       AND ${LABELS_MATCH_SQL}