196 lines · 8.7 KB
import { repoDb } from "../scoped-db";
import { repoOwnerId, runnerAccountId, runnerDb } from "./scope";
import { randomId } from "../crypto";
import { authenticatedRemote } from "../git/artifacts";
import {
  completeJob,
  isCloudJob,
  loadJobExecution,
  markJobStarted,
  mintJobCloneToken,
  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 { busyRunnerCounts, MAX_RUNNER_JOBS, runnerRepoIds } 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.