Skip to content

Commit 2b60771

Browse files
committed
Add support for additional, validation jobs
1 parent 95fc2f1 commit 2b60771

File tree

1 file changed

+102
-6
lines changed

1 file changed

+102
-6
lines changed

pr-checks/sync.ts

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ type WorkflowInputs = Partial<Record<KnownInputName, WorkflowInput>>;
3232
* Represents PR check specifications.
3333
*/
3434
interface Specification extends JobSpecification {
35-
/** The display name for the check. */
36-
name: string;
3735
/** Workflow-level input definitions forwarded to `workflow_dispatch`/`workflow_call`. */
3836
inputs?: Record<string, WorkflowInput>;
3937
/** CodeQL bundle versions to test against. Defaults to `DEFAULT_TEST_VERSIONS`. */
@@ -50,12 +48,17 @@ interface Specification extends JobSpecification {
5048
/** Service containers for the job. */
5149
services?: any;
5250

51+
/** Additional jobs to run after the main PR check job. */
52+
validationJobs?: Record<string, JobSpecification>;
53+
5354
/** If set, this check is part of a named collection that gets its own caller workflow. */
5455
collection?: string;
5556
}
5657

5758
/** Represents job specifications. */
5859
interface JobSpecification {
60+
/** The display name for the check. */
61+
name: string;
5962
/** Custom permissions override for the job. */
6063
permissions?: Record<string, string>;
6164
/** Extra environment variables for the job. */
@@ -469,6 +472,92 @@ function generateJob(
469472
return { checkJob, workflowInputs };
470473
}
471474

475+
/** Generates a validation job. */
476+
function generateValidationJob(
477+
specDocument: yaml.Document,
478+
jobSpecification: JobSpecification,
479+
checkName: string,
480+
name: string,
481+
) {
482+
// Determine which languages or frameworks have to be installed.
483+
const { inputs, steps } = getSetupSteps(jobSpecification);
484+
485+
// Extract the sequence of steps from the YAML document to persist as much formatting as possible.
486+
const specSteps = specDocument.getIn([
487+
"validationJobs",
488+
name,
489+
"steps",
490+
]) as yaml.YAMLSeq;
491+
492+
// Add the generated steps in front of the ones from the specification.
493+
specSteps.items.unshift(...steps);
494+
495+
const validationJob: Record<string, any> = {
496+
name: jobSpecification.name,
497+
if: "github.triggering_actor != 'dependabot[bot]'",
498+
needs: [checkName],
499+
permissions: {
500+
contents: "read",
501+
"security-events": "read",
502+
},
503+
"timeout-minutes": 5,
504+
"runs-on": "ubuntu-slim",
505+
steps: specSteps,
506+
};
507+
508+
if (jobSpecification.permissions) {
509+
validationJob.permissions = jobSpecification.permissions;
510+
}
511+
512+
for (const key of ["env"] as const) {
513+
if (jobSpecification[key] !== undefined) {
514+
validationJob[key] = jobSpecification[key];
515+
}
516+
}
517+
518+
validationJob.env = validationJob.env ?? {};
519+
if (!("CODEQL_ACTION_TEST_MODE" in validationJob.env)) {
520+
validationJob.env.CODEQL_ACTION_TEST_MODE = true;
521+
}
522+
523+
return { validationJob, inputs };
524+
}
525+
526+
/** Generates additional jobs that run after the main check job, based on the `validationJobs` property. */
527+
function generateValidationJobs(
528+
specDocument: yaml.Document,
529+
checkSpecification: Specification,
530+
checkName: string,
531+
): Record<string, any> {
532+
if (checkSpecification.validationJobs === undefined) {
533+
return {};
534+
}
535+
536+
const validationJobs: Record<string, any> = {};
537+
let workflowInputs: WorkflowInputs = {};
538+
539+
for (const [jobName, jobSpec] of Object.entries(
540+
checkSpecification.validationJobs,
541+
)) {
542+
if (checkName === jobName) {
543+
throw new Error(
544+
`Validation job '${jobName}' cannot have the same name as the main job.`,
545+
);
546+
}
547+
548+
const { validationJob, inputs } = generateValidationJob(
549+
specDocument,
550+
jobSpec,
551+
checkName,
552+
jobName,
553+
);
554+
validationJobs[jobName] = validationJob;
555+
workflowInputs = { ...workflowInputs, ...inputs };
556+
}
557+
558+
return { validationJobs, workflowInputs };
559+
}
560+
472561
/**
473562
* Main entry point for the sync script.
474563
*/
@@ -505,6 +594,12 @@ function main(): void {
505594
specDocument,
506595
checkSpecification,
507596
);
597+
const { validationJobs, validationJobInputs } = generateValidationJobs(
598+
specDocument,
599+
checkSpecification,
600+
checkName,
601+
);
602+
const combinedInputs = { ...workflowInputs, ...validationJobInputs };
508603

509604
// If this check belongs to a named collection, record it.
510605
if (checkSpecification.collection) {
@@ -515,12 +610,12 @@ function main(): void {
515610
collections[collectionName].push({
516611
specification: checkSpecification,
517612
checkName,
518-
inputs: workflowInputs,
613+
inputs: combinedInputs,
519614
});
520615
}
521616

522617
let extraGroupName = "";
523-
for (const inputName of Object.keys(workflowInputs)) {
618+
for (const inputName of Object.keys(combinedInputs)) {
524619
extraGroupName += "-${{inputs." + inputName + "}}";
525620
}
526621

@@ -545,10 +640,10 @@ function main(): void {
545640
},
546641
schedule: [{ cron }],
547642
workflow_dispatch: {
548-
inputs: workflowInputs,
643+
inputs: combinedInputs,
549644
},
550645
workflow_call: {
551-
inputs: workflowInputs,
646+
inputs: combinedInputs,
552647
},
553648
},
554649
defaults: {
@@ -563,6 +658,7 @@ function main(): void {
563658
},
564659
jobs: {
565660
[checkName]: checkJob,
661+
...validationJobs,
566662
},
567663
};
568664

0 commit comments

Comments
 (0)