Skip to content

Commit dfa9c92

Browse files
aduh95codebytere
andauthored
feat(git-node): add release promotion step (#835)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
1 parent 819e669 commit dfa9c92

File tree

4 files changed

+565
-15
lines changed

4 files changed

+565
-15
lines changed

components/git/release.js

+65-15
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,31 @@
1+
import auth from '../../lib/auth.js';
12
import CLI from '../../lib/cli.js';
23
import ReleasePreparation from '../../lib/prepare_release.js';
4+
import ReleasePromotion from '../../lib/promote_release.js';
5+
import TeamInfo from '../../lib/team_info.js';
6+
import Request from '../../lib/request.js';
37
import { runPromise } from '../../lib/run.js';
48

5-
export const command = 'release [newVersion|options]';
9+
export const command = 'release [prid|options]';
610
export const describe = 'Manage an in-progress release or start a new one.';
711

812
const PREPARE = 'prepare';
913
const PROMOTE = 'promote';
14+
const RELEASERS = 'releasers';
1015

1116
const releaseOptions = {
17+
filterLabel: {
18+
describe: 'Labels separated by "," to filter security PRs',
19+
type: 'string'
20+
},
21+
'gpg-sign': {
22+
describe: 'GPG-sign commits, will be passed to the git process',
23+
alias: 'S'
24+
},
25+
newVersion: {
26+
describe: 'Version number of the release to be prepared',
27+
type: 'string'
28+
},
1229
prepare: {
1330
describe: 'Prepare a new release of Node.js',
1431
type: 'boolean'
@@ -21,14 +38,16 @@ const releaseOptions = {
2138
describe: 'Default relase date when --prepare is used. It must be YYYY-MM-DD',
2239
type: 'string'
2340
},
41+
run: {
42+
describe: 'Run steps that involve touching more than the local clone, ' +
43+
'including `git push` commands. Might not work if a passphrase ' +
44+
'required to push to the remote clone.',
45+
type: 'boolean'
46+
},
2447
security: {
2548
describe: 'Demarcate the new security release as a security release',
2649
type: 'boolean'
2750
},
28-
filterLabel: {
29-
describe: 'Labels separated by "," to filter security PRs',
30-
type: 'string'
31-
},
3251
skipBranchDiff: {
3352
describe: 'Skips the initial branch-diff check when preparing releases',
3453
type: 'boolean'
@@ -49,11 +68,16 @@ let yargsInstance;
4968
export function builder(yargs) {
5069
yargsInstance = yargs;
5170
return yargs
52-
.options(releaseOptions).positional('newVersion', {
53-
describe: 'Version number of the release to be prepared or promoted'
71+
.options(releaseOptions).positional('prid', {
72+
describe: 'PR number or URL of the release proposal to be promoted',
73+
type: 'string'
5474
})
55-
.example('git node release --prepare 1.2.3',
56-
'Prepare a release of Node.js tagged v1.2.3')
75+
.example('git node release --prepare --security',
76+
'Prepare a new security release of Node.js with auto-determined version')
77+
.example('git node release --prepare --newVersion=1.2.3',
78+
'Prepare a new release of Node.js tagged v1.2.3')
79+
.example('git node release --promote 12345',
80+
'Promote a prepared release of Node.js with PR #12345')
5781
.example('git node --prepare --startLTS',
5882
'Prepare the first LTS release');
5983
}
@@ -88,17 +112,21 @@ function release(state, argv) {
88112
}
89113

90114
async function main(state, argv, cli, dir) {
115+
const prID = /^(?:https:\/\/github\.com\/nodejs\/node\/pull\/)?(\d+)$/.exec(argv.prid);
116+
if (prID) {
117+
argv.prid = Number(prID[1]);
118+
}
91119
if (state === PREPARE) {
92-
const prep = new ReleasePreparation(argv, cli, dir);
120+
const release = new ReleasePreparation(argv, cli, dir);
93121

94-
await prep.prepareLocalBranch();
122+
await release.prepareLocalBranch();
95123

96-
if (prep.warnForWrongBranch()) return;
124+
if (release.warnForWrongBranch()) return;
97125

98126
// If the new version was automatically calculated, confirm it.
99127
if (!argv.newVersion) {
100128
const create = await cli.prompt(
101-
`Create release with new version ${prep.newVersion}?`,
129+
`Create release with new version ${release.newVersion}?`,
102130
{ defaultAnswer: true });
103131

104132
if (!create) {
@@ -107,8 +135,30 @@ async function main(state, argv, cli, dir) {
107135
}
108136
}
109137

110-
return prep.prepare();
138+
return release.prepare();
111139
} else if (state === PROMOTE) {
112-
// TODO(codebytere): implement release promotion.
140+
const credentials = await auth({ github: true });
141+
const request = new Request(credentials);
142+
const release = new ReleasePromotion(argv, request, cli, dir);
143+
144+
cli.startSpinner('Verifying Releaser status');
145+
const info = new TeamInfo(cli, request, 'nodejs', RELEASERS);
146+
147+
const releasers = await info.getMembers();
148+
if (release.username === undefined) {
149+
cli.stopSpinner('Failed to verify Releaser status');
150+
cli.info(
151+
'Username was undefined - do you have your .ncurc set up correctly?');
152+
return;
153+
} else if (releasers.every(r => r.login !== release.username)) {
154+
cli.stopSpinner(`${release.username} is not a Releaser`, 'failed');
155+
if (!argv.dryRun) {
156+
throw new Error('aborted');
157+
}
158+
} else {
159+
cli.stopSpinner(`${release.username} is a Releaser`);
160+
}
161+
162+
return release.promote();
113163
}
114164
}

0 commit comments

Comments
 (0)