import { repoDb } from "../scoped-db";
import { loadRepoById } from "../repos";
import { compileWorkflow } from "./compile";
import type { CompiledJob, CompileInput, NeedsEntry } from "./compile";
import { environmentIdFor, loadVariables } from "./config-values";
import { parseWorkflow } from "./spec";
import type { WorkflowJobRow, WorkflowRunRow } from "./jobs";
function aggregateResult(conclusions: Array<string | null>): string {
if (conclusions.some((value) => value === "failure")) return "failure";
if (conclusions.some((value) => value === "cancelled")) return "cancelled";
if (conclusions.length > 0 && conclusions.every((value) => value === "skipped")) return "skipped";
return "success";
}
export function needsContext(dependencies: string[], siblings: WorkflowJobRow[]): Record<string, NeedsEntry> {
const needs: Record<string, NeedsEntry> = {};
for (const dependency of dependencies) {
const rows = siblings.filter((row) => {
const spec: CompiledJob = JSON.parse(row.spec);
return spec.baseKey === dependency;
});
const outputs: Record<string, string> = {};
for (const row of rows) Object.assign(outputs, row.outputs ? JSON.parse(row.outputs) : {});
needs[dependency] = { result: aggregateResult(rows.map((row) => row.conclusion)), outputs };
}
return needs;
}
/**
* Recompiles a job once its dependencies finished so `needs.<job>.outputs`, `needs.<job>.result` and
* environment-scoped `vars` resolve, persisting the result as the job's spec.
*/
export async function recompileJob(runRow: WorkflowRunRow, job: WorkflowJobRow): Promise<CompiledJob> {
const current: CompiledJob = JSON.parse(job.spec);
if (!runRow.workflow_source || !runRow.context) return current;
const parsed = parseWorkflow(runRow.workflow_source);
if (!parsed.ok) throw new Error(parsed.error);
const base: CompileInput = JSON.parse(runRow.context);
const db = repoDb(runRow.repo_id);