import { all, first, run } from "../db";
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 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 row = await 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. */
async function forkJobs(repoId: string, statuses: string): Promise<ForkJob[]> {
return all<ForkJob>(
`SELECT j.id, j.runs_on, j.status FROM workflow_jobs j JOIN workflow_runs w ON w.id = j.run_id