Skip to content

Commit 643ff69

Browse files
committed
feat(workflow-result): add a result rollup job to better enable required checks
1 parent c047026 commit 643ff69

7 files changed

Lines changed: 136 additions & 2 deletions

File tree

src/jobs/missing-job-inserter.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1+
import {scaffold as scaffoldWorkflowResultJob} from './workflow-result/index.js';
12
import {matrixVerification} from './scaffolder.js';
23

34
function noMatrixJobExistsIn(jobs) {
45
return !jobs.filter(([, job]) => job.strategy?.matrix?.node).length;
56
}
67

8+
function resultJobAlreadyExists(jobs) {
9+
return jobs.some(([jobName]) => 'workflow-result' === jobName);
10+
}
11+
712
export default function ({versions: matrixOfNodeVersions, jobs, runner}) {
813
if (matrixOfNodeVersions && noMatrixJobExistsIn(jobs)) {
914
return [['verify-matrix', matrixVerification({versions: matrixOfNodeVersions, runner})], ...jobs];
1015
}
1116

17+
if (!resultJobAlreadyExists(jobs)) return [...jobs, ['workflow-result', scaffoldWorkflowResultJob()]];
18+
1219
return jobs;
1320
}

src/jobs/missing-job-inserter.test.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import {when} from 'jest-when';
44

55
import {matrixVerification} from './scaffolder.js';
66
import insertMissingJobs from './missing-job-inserter.js';
7+
import {scaffold as scaffoldWorkflowResultJob} from './workflow-result/index.js';
78

89
vi.mock('./scaffolder.js');
10+
vi.mock('./workflow-result/index.js');
911

1012
describe('missing job inserter', () => {
1113
describe('matrix verification', () => {
@@ -51,16 +53,26 @@ describe('missing job inserter', () => {
5153
it('should not insert a matrix verification job if a matrix job already exists for node', async () => {
5254
const jobs = [
5355
...any.listOf(() => ([any.word(), any.simpleObject()])),
54-
[any.word(), {strategy: {matrix: {node: {}}}}]
56+
[any.word(), {strategy: {matrix: {node: {}}}}],
57+
['workflow-result', any.simpleObject()]
5558
];
5659

5760
expect(insertMissingJobs({versions: matrixOfNodeVersions, jobs})).toEqual(jobs);
5861
});
5962

6063
it('should not insert a matrix verification job if no matrix of node versions is provided', async () => {
61-
const jobs = any.listOf(() => ([any.word(), any.simpleObject()]));
64+
const jobs = [...any.listOf(() => ([any.word(), any.simpleObject()])), ['workflow-result', any.simpleObject()]];
6265

6366
expect(insertMissingJobs({versions: undefined, jobs})).toEqual(jobs);
6467
});
68+
69+
it('should insert a `workflow-result` job when one doesnt already exist', async () => {
70+
const jobs = any.listOf(() => ([any.word(), any.simpleObject()]));
71+
const resultJob = any.simpleObject();
72+
when(scaffoldWorkflowResultJob).calledWith().mockReturnValue(resultJob);
73+
74+
expect(insertMissingJobs({jobs}))
75+
.toEqual([...jobs, ['workflow-result', resultJob]]);
76+
});
6577
});
6678
});

src/jobs/workflow-result/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {default as scaffold} from './scaffolder.js';
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export default function () {
2+
return {
3+
'runs-on': 'ubuntu-latest',
4+
needs: ['verify'],
5+
// eslint-disable-next-line no-template-curly-in-string
6+
if: '${{ !cancelled() }}',
7+
steps: [
8+
{
9+
name: 'All matrix versions passed',
10+
// eslint-disable-next-line no-template-curly-in-string
11+
if: "${{ !(contains(needs.*.result, 'failure')) }}",
12+
run: 'exit 0'
13+
},
14+
{
15+
name: 'Some matrix version failed',
16+
// eslint-disable-next-line no-template-curly-in-string
17+
if: "${{ contains(needs.*.result, 'failure') }}",
18+
run: 'exit 1'
19+
}
20+
]
21+
};
22+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {it, expect, describe} from 'vitest';
2+
3+
import scaffoldWorkflowResultJob from './scaffolder.js';
4+
5+
describe('workflow-result job scaffolder', () => {
6+
it('should determine exit code based on previous job results', () => {
7+
expect(scaffoldWorkflowResultJob()).toEqual({
8+
'runs-on': 'ubuntu-latest',
9+
needs: ['verify'],
10+
// eslint-disable-next-line no-template-curly-in-string
11+
if: '${{ !cancelled() }}',
12+
steps: [
13+
{
14+
name: 'All matrix versions passed',
15+
// eslint-disable-next-line no-template-curly-in-string
16+
if: "${{ !(contains(needs.*.result, 'failure')) }}",
17+
run: 'exit 0'
18+
},
19+
{
20+
name: 'Some matrix version failed',
21+
// eslint-disable-next-line no-template-curly-in-string
22+
if: "${{ contains(needs.*.result, 'failure') }}",
23+
run: 'exit 1'
24+
}
25+
]
26+
});
27+
});
28+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Feature: Overall Workflow Result
2+
3+
Scenario: Existing result job
4+
Given a CI workflow exists
5+
And a "workflow-result" job exists
6+
When the project is lifted
7+
Then the workflow-result job exists
8+
And the "workflow-result" job is unchanged
9+
10+
Scenario: No existing result job, existing verify
11+
Given a CI workflow exists
12+
And a "verify" job exists
13+
When the project is lifted
14+
Then the workflow-result job exists
15+
And the workflow-result job depends on "verify"
16+
17+
@wip
18+
Scenario: No existing result job, existing verify and verify-matrix
19+
Given a CI workflow exists
20+
And a "verify" job exists
21+
And a "verify-matrix" job exists
22+
When the project is lifted
23+
Then the workflow-result job exists
24+
And the workflow-result job depends on "verify"
25+
And the workflow-result job depends on "verify-matrix"

test/integration/features/step_definitions/ci-steps.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import any from '@travi/any';
1414

1515
Before(async function () {
1616
this.prTriggerConfig = any.simpleObject();
17+
18+
this.injectedJobs = [];
1719
});
1820

1921
Given('a CI workflow exists', async function () {
@@ -32,6 +34,25 @@ Given('a CI workflow exists', async function () {
3234
});
3335
});
3436

37+
Given('a {string} job exists', async function (jobName) {
38+
const existingWorkflowContents = await loadWorkflowFile({projectRoot: this.projectRoot, name: 'node-ci'});
39+
const job = any.simpleObject();
40+
41+
this.injectedJobs[jobName] = job;
42+
43+
await writeWorkflowFile({
44+
projectRoot: this.projectRoot,
45+
name: 'node-ci',
46+
config: {
47+
...existingWorkflowContents,
48+
jobs: {
49+
...existingWorkflowContents.jobs,
50+
[jobName]: job
51+
}
52+
}
53+
});
54+
});
55+
3556
Then('the ci config remains unchanged', async function () {
3657
const {on: triggers, jobs} = await loadWorkflowFile({projectRoot: this.projectRoot, name: 'node-ci'});
3758

@@ -79,3 +100,21 @@ Then('the verification workflow is created', async function () {
79100
]
80101
);
81102
});
103+
104+
Then('the workflow-result job exists', async function () {
105+
const {jobs} = await loadWorkflowFile({projectRoot: this.projectRoot, name: 'node-ci'});
106+
107+
assert.include(Object.keys(jobs), 'workflow-result');
108+
});
109+
110+
Then('the {string} job is unchanged', async function (jobName) {
111+
const {jobs} = await loadWorkflowFile({projectRoot: this.projectRoot, name: 'node-ci'});
112+
113+
assert.deepEqual(jobs[jobName], this.injectedJobs[jobName]);
114+
});
115+
116+
Then('the workflow-result job depends on {string}', async function (jobName) {
117+
const {jobs} = await loadWorkflowFile({projectRoot: this.projectRoot, name: 'node-ci'});
118+
119+
assert.include(jobs['workflow-result'].needs, jobName);
120+
});

0 commit comments

Comments
 (0)