Skip to content

Commit fa3ae29

Browse files
committed
feat(lift): migrated the legacy release job to instead trigger the separate workflow
1 parent 3752b51 commit fa3ae29

File tree

9 files changed

+206
-14
lines changed

9 files changed

+206
-14
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ import {scaffold, test, lift} from '@form8ion/commit-convention';
5454
await scaffold({projectRoot, configs: {}});
5555

5656
if (await test({projectRoot})) {
57-
await lift({projectRoot, packageManager: packageManagers.NPM});
57+
await lift({projectRoot, packageManager: packageManagers.NPM, vcs: {owner: 'foo', name: 'bar'}});
5858
}
5959
})();
6060
```

src/semantic-release/ci-providers/github-workflows/lifter-test.js

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {promises as fs} from 'fs';
2+
import jsYaml from 'js-yaml';
13
import * as core from '@form8ion/core';
24

35
import any from '@travi/any';
@@ -10,29 +12,83 @@ import lift from './lifter';
1012
suite('github-workflows lifter for semantic-release', () => {
1113
let sandbox;
1214
const projectRoot = any.string();
15+
const workflowsDirectory = `${projectRoot}/.github/workflows`;
16+
const vcsName = any.word();
17+
const vcsOwner = any.word();
18+
const vcsDetails = {name: vcsName, owner: vcsOwner};
1319

1420
setup(() => {
1521
sandbox = sinon.createSandbox();
1622

23+
sandbox.stub(fs, 'readFile');
24+
sandbox.stub(fs, 'writeFile');
25+
sandbox.stub(jsYaml, 'load');
26+
sandbox.stub(jsYaml, 'dump');
1727
sandbox.stub(core, 'fileExists');
1828
sandbox.stub(scaffolder, 'default');
29+
30+
const commonVerificationWorkflowContents = any.string();
31+
fs.readFile.withArgs(`${workflowsDirectory}/node-ci.yml`, 'utf-8').resolves(commonVerificationWorkflowContents);
32+
jsYaml.load.withArgs(commonVerificationWorkflowContents).returns({jobs: {}});
1933
});
2034

2135
teardown(() => sandbox.restore());
2236

2337
test('that the release workflow is added if it doesnt already exist', async () => {
2438
core.fileExists.resolves(false);
2539

26-
await lift({projectRoot});
40+
await lift({projectRoot, vcs: vcsDetails});
2741

2842
assert.calledWith(scaffolder.default, {projectRoot});
2943
});
3044

3145
test('that the release workflow is not added if it already exists', async () => {
32-
core.fileExists.withArgs(`${projectRoot}/.github/workflows/release.yml`).resolves(true);
46+
core.fileExists.withArgs(`${workflowsDirectory}/release.yml`).resolves(true);
3347

34-
await lift({projectRoot});
48+
await lift({projectRoot, vcs: vcsDetails});
3549

3650
assert.notCalled(scaffolder.default);
3751
});
52+
53+
test('that the legacy release job is removed', async () => {
54+
const verificationWorkflowContents = any.string();
55+
const parsedVerificationWorkflowContents = any.simpleObject();
56+
const jobs = any.simpleObject();
57+
const legacyReleaseJob = any.simpleObject();
58+
const updatedVerificationWorkflowContents = any.string();
59+
core.fileExists.resolves(true);
60+
fs.readFile.withArgs(`${workflowsDirectory}/node-ci.yml`, 'utf-8').resolves(verificationWorkflowContents);
61+
jsYaml.load
62+
.withArgs(verificationWorkflowContents)
63+
.returns({...parsedVerificationWorkflowContents, jobs: {...jobs, release: legacyReleaseJob}});
64+
jsYaml.dump
65+
.withArgs({
66+
...parsedVerificationWorkflowContents,
67+
jobs: {
68+
...jobs,
69+
'trigger-release': {
70+
'runs-on': 'ubuntu-latest',
71+
if: "github.event_name == 'push'",
72+
steps: [{
73+
uses: 'octokit/[email protected]',
74+
with: {
75+
route: 'POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches',
76+
owner: vcsOwner,
77+
repo: vcsName,
78+
ref: '${{ github.ref }}', // eslint-disable-line no-template-curly-in-string
79+
workflow_id: 'release.yml',
80+
env: {
81+
GITHUB_TOKEN: '${{ secrets.GH_PAT }}' // eslint-disable-line no-template-curly-in-string
82+
}
83+
}
84+
}]
85+
}
86+
}
87+
})
88+
.returns(updatedVerificationWorkflowContents);
89+
90+
await lift({projectRoot, vcs: vcsDetails});
91+
92+
assert.calledWith(fs.writeFile, `${workflowsDirectory}/node-ci.yml`, updatedVerificationWorkflowContents);
93+
});
3894
});
Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,39 @@
1+
import {promises as fs} from 'fs';
2+
import {load, dump} from 'js-yaml';
13
import {fileExists} from '@form8ion/core';
24

35
import scaffoldReleaseWorkflow from './scaffolder';
46

5-
export default async function ({projectRoot}) {
7+
export default async function ({projectRoot, vcs: {name: vcsProjectName, owner: vcsOwner}}) {
8+
const pathToVerificationWorkflow = `${projectRoot}/.github/workflows/node-ci.yml`;
9+
610
if (!await fileExists(`${projectRoot}/.github/workflows/release.yml`)) {
711
await scaffoldReleaseWorkflow({projectRoot});
812
}
13+
14+
const parsedVerificationWorkflowDetails = load(await fs.readFile(pathToVerificationWorkflow, 'utf-8'));
15+
const {release, ...otherJobs} = parsedVerificationWorkflowDetails.jobs;
16+
parsedVerificationWorkflowDetails.jobs = {
17+
...otherJobs,
18+
'trigger-release': {
19+
'runs-on': 'ubuntu-latest',
20+
if: "github.event_name == 'push'",
21+
steps: [{
22+
uses: 'octokit/[email protected]',
23+
with: {
24+
route: 'POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches',
25+
owner: vcsOwner,
26+
repo: vcsProjectName,
27+
ref: '${{ github.ref }}', // eslint-disable-line no-template-curly-in-string
28+
workflow_id: 'release.yml',
29+
env: {
30+
GITHUB_TOKEN: '${{ secrets.GH_PAT }}' // eslint-disable-line no-template-curly-in-string
31+
}
32+
}
33+
}]
34+
35+
}
36+
};
37+
38+
await fs.writeFile(pathToVerificationWorkflow, dump(parsedVerificationWorkflowDetails));
939
}

src/semantic-release/lifter-test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ suite('semantic-release lifter', () => {
2727
});
2828

2929
test('that the ci provider is lifted when supported', async () => {
30+
const vcsDetails = any.simpleObject();
3031
githubWorkflowsTester.default.withArgs({projectRoot}).resolves(true);
3132

32-
assert.deepEqual(await lift({projectRoot}), {});
33-
assert.calledWith(githubWorkflowsLifter.default, {projectRoot});
33+
assert.deepEqual(await lift({projectRoot, vcs: vcsDetails}), {});
34+
assert.calledWith(githubWorkflowsLifter.default, {projectRoot, vcs: vcsDetails});
3435
});
3536
});

src/semantic-release/lifter.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {test as ciProviderCanBeLifted, lift as liftCiProvider} from './ci-providers';
22

3-
export default async function ({projectRoot}) {
4-
if (await ciProviderCanBeLifted({projectRoot})) await liftCiProvider({projectRoot});
3+
export default async function ({projectRoot, vcs}) {
4+
if (await ciProviderCanBeLifted({projectRoot})) await liftCiProvider({projectRoot, vcs});
55

66
return {};
77
}

test/integration/features/lifter.feature

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,24 @@ Feature: Lift
44
Given semantic-release is configured
55
And legacy releases are configured in a GitHub workflow
66
When the project is lifted
7-
Then the release workflow if defined
7+
Then the release workflow is defined
8+
And the verification workflow triggers the release workflow
9+
10+
Scenario: modern semantic-release in GitHub workflows
11+
Given semantic-release is configured
12+
And modern releases are configured in a GitHub workflow
13+
When the project is lifted
14+
Then the release workflow is defined
15+
16+
Scenario: no existing release
17+
Given semantic-release is not configured
18+
And no release is configured in a GitHub workflow
19+
When the project is lifted
20+
Then the release workflow is not defined
21+
And the verification workflow does not trigger the release workflow
22+
23+
Scenario: no GitHub workflows
24+
Given semantic-release is configured
25+
But no GitHub workflows exist
26+
When the project is lifted
27+
Then the release workflow is not defined

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {resolve} from 'path';
2+
import {dump} from 'js-yaml';
23

34
import {After, When} from '@cucumber/cucumber';
45
import stubbedFs from 'mock-fs';
@@ -26,8 +27,31 @@ When('the project is lifted', async function () {
2627
// eslint-disable-next-line import/no-extraneous-dependencies,import/no-unresolved
2728
const {test, lift} = require('@form8ion/commit-convention');
2829

30+
this.projectName = any.word();
31+
this.vcsOwner = any.word();
32+
2933
stubbedFs({
30-
...this.semanticReleaseGithubWorkflow && {'.github': {workflows: {}}},
34+
...this.githubWorkflows && {
35+
'.github': {
36+
workflows: {
37+
...this.verificationWorkflow && {
38+
'node-ci.yml': dump({
39+
jobs: {
40+
...this.nodeCiWithReleaseJob && {
41+
release: {}
42+
},
43+
...this.nodeCiWithTriggerReleaseJob && {
44+
'trigger-release': {}
45+
}
46+
}
47+
})
48+
},
49+
...this.releaseWorkflow && {
50+
'release.yml': dump({})
51+
}
52+
}
53+
}
54+
},
3155
node_modules: stubbedNodeModules,
3256
'package.json': JSON.stringify({
3357
...any.simpleObject(),
@@ -36,6 +60,6 @@ When('the project is lifted', async function () {
3660
});
3761

3862
if (await test({projectRoot})) {
39-
await lift({projectRoot});
63+
await lift({projectRoot, vcs: {owner: this.vcsOwner, name: this.projectName}});
4064
}
4165
});
Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,69 @@
1+
import {promises as fs} from 'fs';
12
import {fileExists} from '@form8ion/core';
23

34
import {Given, Then} from '@cucumber/cucumber';
45
import {assert} from 'chai';
6+
import {load} from 'js-yaml';
57

68
Given('legacy releases are configured in a GitHub workflow', async function () {
7-
this.semanticReleaseGithubWorkflow = 'legacy';
9+
this.githubWorkflows = true;
10+
this.verificationWorkflow = true;
11+
this.nodeCiWithReleaseJob = true;
812
});
913

10-
Then('the release workflow if defined', async function () {
14+
Given('modern releases are configured in a GitHub workflow', async function () {
15+
this.githubWorkflows = true;
16+
this.verificationWorkflow = true;
17+
this.releaseWorkflow = true;
18+
this.nodeCiWithTriggerReleaseJob = true;
19+
});
20+
21+
Given('no release is configured in a GitHub workflow', async function () {
22+
this.githubWorkflows = true;
23+
this.verificationWorkflow = true;
24+
this.nodeCiWithReleaseJob = false;
25+
this.nodeCiWithTriggerReleaseJob = false;
26+
});
27+
28+
Given('no GitHub workflows exist', async function () {
29+
this.githubWorkflows = false;
30+
});
31+
32+
Then('the release workflow is defined', async function () {
1133
assert.isTrue(await fileExists(`${process.cwd()}/.github/workflows/release.yml`));
1234
});
35+
36+
Then('the release workflow is not defined', async function () {
37+
assert.isFalse(await fileExists(`${process.cwd()}/.github/workflows/release.yml`));
38+
});
39+
40+
Then('the verification workflow triggers the release workflow', async function () {
41+
const verificationWorkflowDefinition = load(await fs.readFile(
42+
`${process.cwd()}/.github/workflows/node-ci.yml`,
43+
'utf-8'
44+
));
45+
46+
const verificationWorkflowJobs = verificationWorkflowDefinition.jobs;
47+
const triggerReleaseJob = verificationWorkflowJobs['trigger-release'];
48+
49+
assert.isUndefined(verificationWorkflowJobs.release);
50+
assert.equal(triggerReleaseJob.if, "github.event_name == 'push'");
51+
52+
const releaseTriggerRequest = triggerReleaseJob.steps[0];
53+
assert.equal(releaseTriggerRequest.uses, 'octokit/[email protected]');
54+
assert.equal(
55+
releaseTriggerRequest.with.route,
56+
'POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches'
57+
);
58+
assert.equal(releaseTriggerRequest.with.owner, this.vcsOwner);
59+
assert.equal(releaseTriggerRequest.with.repo, this.projectName);
60+
});
61+
62+
Then('the verification workflow does not trigger the release workflow', async function () {
63+
const verificationWorkflowDefinition = load(await fs.readFile(
64+
`${process.cwd()}/.github/workflows/node-ci.yml`,
65+
'utf-8'
66+
));
67+
68+
assert.isUndefined(verificationWorkflowDefinition.jobs['trigger-release']);
69+
});

test/integration/features/step_definitions/semantic-release-steps.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ import {Given} from '@cucumber/cucumber';
33
Given('semantic-release is configured', async function () {
44
this.semanticReleaseConfigured = true;
55
});
6+
7+
Given('semantic-release is not configured', async function () {
8+
this.semanticReleaseConfigured = false;
9+
});

0 commit comments

Comments
 (0)