import { accountDb, repoDb } from "../scoped-db";
import { repoOwnerId } from "./scope";
import { completeJob, runnerStub } from "./jobs";
import { CLOUD_RUNS_ON_VALUES, cloudRunsOnPlaceholders } from "./runner-sizes";
const MAX_FORK_JOBS_SWEPT = 500;
/** SQL predicate: every runs-on label of `j` is among the runner's labels (`r.labels`) or `self-hosted`. */
export const LABELS_MATCH_SQL = `json_array_length(j.runs_on) > 0 AND NOT EXISTS (
SELECT 1 FROM json_each(j.runs_on) AS wanted
WHERE lower(wanted.value) != 'self-hosted'
AND lower(wanted.value) NOT IN (SELECT lower(have.value) FROM json_each(r.labels) AS have))`;
export async function forkRunsAllowed(repoId: string): Promise<boolean> {
const row = await repoDb(repoId).first<{ allow_fork_pull_requests: number }>(
"SELECT allow_fork_pull_requests FROM runner_policies WHERE repo_id = ?",
repoId,
);
return row?.allow_fork_pull_requests === 1;
}
/**
* Fork jobs only run on runners registered to the repo itself whose container isolation was declared by the admin
* who minted the registration token (never by the daemon), and that carry every runs-on label.
*/
export async function hasForkEligibleRunner(repoId: string, runsOn: string[]): Promise<boolean> {
const ownerId = await repoOwnerId(repoId);
if (!ownerId) return false;
const row = await accountDb(ownerId).first<{ ok: number }>(
`SELECT 1 AS ok FROM runners r, (SELECT ? AS runs_on) j
WHERE r.repo_id = ? AND r.isolation_attested = 1 AND ${LABELS_MATCH_SQL} LIMIT 1`,
JSON.stringify(runsOn),
repoId,
);
return row !== null;
}
type ForkJob = { id: string; runs_on: string; status: string };
/** Self-hosted (non-cloud) jobs of fork or untrusted runs in the repo. */