Skip to content

Commit 9b61a56

Browse files
committed
fix(git-node): check the tag signature and not the commit one
During release promotion, we want to verify the tag is signed with one of the releasers' key. The commit can be signed with a different signature (e.g. the bot's, or a soon-to-be releaser).
1 parent 021161b commit 9b61a56

File tree

2 files changed

+34
-16
lines changed

2 files changed

+34
-16
lines changed

lib/promote_release.js

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -226,20 +226,28 @@ export default class ReleasePromotion extends Session {
226226

227227
async verifyTagSignature() {
228228
const { cli, version } = this;
229-
const [needle, haystack] = await Promise.all([forceRunAsync(
229+
const verifyTagPattern = /gpg:[^\n]+\ngpg:\s+using RSA key ([^\n]+)\ngpg:\s+issuer "([^"]+)"\ngpg:\s+Good signature from "([^<]+) <\2>"/;
230+
const [verifyTagOutput, haystack] = await Promise.all([forceRunAsync(
230231
'git', ['--no-pager',
231-
'log', '-1',
232-
`refs/tags/v${version}`,
233-
'--format=* **%an** <<%ae>>\n `%GF`'
234-
], { captureStdout: true }), fs.readFile('README.md')]);
235-
if (haystack.includes(needle)) {
236-
return;
232+
'verify-tag',
233+
`v${version}`
234+
], { ignoreFailure: false, captureStderr: true }), fs.readFile('README.md')]);
235+
const match = verifyTagPattern.exec(verifyTagOutput);
236+
if (match == null) {
237+
cli.warn('git was not able to verify the tag:');
238+
cli.info(verifyTagOutput);
239+
} else {
240+
const [, keyID, email, name] = match;
241+
const needle = `* **${name}** <<${email}>>\n ${'`'}${keyID}${'`'}`;
242+
if (haystack.includes(needle)) {
243+
return;
244+
}
245+
cli.warn('Tag was signed with an undocumented identity/key pair!');
246+
cli.info('Expected to find the following entry in the README:');
247+
cli.info(needle);
248+
cli.info('If you are using a subkey, it might be OK.');
237249
}
238-
cli.warn('Tag was signed with an undocumented identity/key pair!');
239-
cli.info('Expected to find the following entry in the README:');
240-
cli.info(needle);
241-
cli.info('If you are using a subkey, it might be OK.');
242-
cli.info(`Otherwise consider removing the tag (git tag -d v${version
250+
cli.info(`If that doesn't sound right, consider removing the tag (git tag -d v${version
243251
}), check your local config, and start the process over.`);
244252
if (!await cli.prompt('Do you want to proceed anyway?', { defaultAnswer: false })) {
245253
throw new Error('Aborted');
@@ -383,7 +391,6 @@ export default class ReleasePromotion extends Session {
383391
{ cause: err }
384392
);
385393
}
386-
await forceRunAsync('git', ['tag', '--verify', `v${version}`], { ignoreFailure: false });
387394
this.cli.info('Using the existing tag');
388395
}
389396
}

lib/run.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,19 @@ function runAsyncBase(cmd, args, {
1212
ignoreFailure = true,
1313
spawnArgs,
1414
input,
15+
captureStderr = false,
1516
captureStdout = false
1617
} = {}) {
1718
if (cmd instanceof URL) {
1819
cmd = fileURLToPath(cmd);
1920
}
2021
let stdio = 'inherit';
21-
if (captureStdout || input != null) {
22-
stdio = [input == null ? 'inherit' : 'pipe', captureStdout ? 'pipe' : 'inherit', 'inherit'];
22+
if (captureStderr || captureStdout || input != null) {
23+
stdio = [
24+
input == null ? 'inherit' : 'pipe',
25+
captureStdout ? 'pipe' : 'inherit',
26+
captureStderr ? 'pipe' : 'inherit'
27+
];
2328
}
2429
return new Promise((resolve, reject) => {
2530
const opt = Object.assign({
@@ -30,6 +35,12 @@ function runAsyncBase(cmd, args, {
3035
debuglog('[Spawn]', `${cmd} ${(args || []).join(' ')}`, opt);
3136
}
3237
const child = spawn(cmd, args, opt);
38+
let stderr;
39+
if (!captureStdout && captureStderr) {
40+
stderr = '';
41+
child.stderr.setEncoding('utf8');
42+
child.stderr.on('data', (chunk) => { stderr += chunk; });
43+
}
3344
let stdout;
3445
if (captureStdout) {
3546
stdout = '';
@@ -51,7 +62,7 @@ function runAsyncBase(cmd, args, {
5162
stdout = stdout.split(/\r?\n/g);
5263
if (stdout[stdout.length - 1] === '') stdout.pop();
5364
}
54-
return resolve(stdout);
65+
return resolve(stdout ?? stderr);
5566
});
5667
if (input != null) child.stdin.end(input);
5768
});

0 commit comments

Comments
 (0)