Skip to content

Commit 0965964

Browse files
committed
fix: adjust --cleanup to close PRs
1 parent 406a649 commit 0965964

4 files changed

Lines changed: 89 additions & 22 deletions

File tree

lib/prepare_security.js

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import fs from 'node:fs';
22
import path from 'node:path';
33
import auth from './auth.js';
44
import Request from './request.js';
5+
import { parsePRFromURL } from './links.js';
56
import {
67
NEXT_SECURITY_RELEASE_BRANCH,
78
NEXT_SECURITY_RELEASE_FOLDER,
@@ -196,8 +197,7 @@ export default class PrepareSecurityRelease extends SecurityRelease {
196197
await this.closeAndRequestDisclosure(vulnerabilityJSON.reports);
197198

198199
this.cli.info('Closing pull requests');
199-
// For now, close the ones with Security Release label
200-
await this.closePRWithLabel('Security Release');
200+
await this.closePullRequests(vulnerabilityJSON);
201201

202202
if (vulnerabilityJSON.buildIssue) {
203203
this.cli.info('Commenting on nodejs/build issue');
@@ -637,26 +637,25 @@ export default class PrepareSecurityRelease extends SecurityRelease {
637637
this.cli.stopSpinner('Done closing H1 Reports and requesting disclosure');
638638
}
639639

640-
async closePRWithLabel(labels) {
641-
if (typeof labels === 'string') {
642-
labels = [labels];
643-
}
644-
645-
const url = 'https://github.com/nodejs-private/node-private/pull';
640+
async closePullRequests(vulnerabilityJSON) {
646641
this.cli.startSpinner('Closing GitHub Pull Requests...');
647-
// At this point, GitHub does not provide filters through their REST API
648-
const prs = await this.req.getPullRequest(url);
649-
for (const pr of prs) {
650-
if (pr.labels.some((l) => labels.includes(l.name))) {
651-
this.cli.updateSpinner(`Closing Pull Request: ${pr.number}`);
652-
await confirmSecurityStep(
653-
this.cli,
654-
`close GitHub pull request \`nodejs-private/node-private#${pr.number}\``,
655-
'This closes a pull request labeled for the security release.'
656-
);
657-
await this.req.closePullRequest(pr.number,
658-
{ owner: 'nodejs-private', repo: 'node-private' });
659-
}
642+
const entries = [
643+
...vulnerabilityJSON.reports,
644+
...Object.values(vulnerabilityJSON.dependencies ?? {})
645+
];
646+
const urls = new Set(entries.flatMap(
647+
({ affectedVersions }) => Object.values(affectedVersions)));
648+
649+
for (const url of urls) {
650+
const { owner, repo, prid } = parsePRFromURL(url);
651+
const name = `${owner}/${repo}#${prid}`;
652+
this.cli.updateSpinner(`Closing Pull Request: ${name}`);
653+
await confirmSecurityStep(
654+
this.cli,
655+
`close GitHub pull request \`${name}\``,
656+
'This closes a pull request listed in vulnerabilities.json.'
657+
);
658+
await this.req.closePullRequest(prid, { owner, repo });
660659
}
661660
this.cli.stopSpinner('Closed GitHub Pull Requests.');
662661
}

lib/request.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ export default class Request {
158158
async closePullRequest(id, { owner, repo }) {
159159
const url = `/repos/${owner}/${repo}/pulls/${id}`;
160160
const options = {
161-
method: 'POST',
161+
method: 'PATCH',
162162
headers: {
163163
'Content-Type': 'application/json'
164164
},

test/unit/request.test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,25 @@ function createRequest(response) {
1212
}
1313

1414
describe('Request', () => {
15+
describe('closePullRequest', () => {
16+
it('updates the pull request state with PATCH', async() => {
17+
const request = createRequest({});
18+
let requestOptions;
19+
request.json = async(url, options) => {
20+
assert.strictEqual(url, '/repos/nodejs/node/pulls/123');
21+
requestOptions = options;
22+
return {};
23+
};
24+
25+
await request.closePullRequest(123, { owner: 'nodejs', repo: 'node' });
26+
27+
assert.strictEqual(requestOptions.method, 'PATCH');
28+
assert.deepStrictEqual(JSON.parse(requestOptions.body), {
29+
state: 'closed'
30+
});
31+
});
32+
});
33+
1534
describe('query', () => {
1635
it('preserves detailed GraphQL errors', async() => {
1736
const variables = { owner: 'nodejs', repo: 'node', prid: 65130 };

test/unit/security_release.test.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,55 @@ import {
1616
getHighestSeverityAnnouncement
1717
} from '../../lib/security-release/security-release.js';
1818

19+
describe('security_release: cleanup pull requests', () => {
20+
const vulnerabilities = {
21+
reports: [
22+
{
23+
affectedVersions: {
24+
main: 'https://github.com/nodejs-private/node-private/pull/2',
25+
'24.x': 'https://github.com/nodejs-private/node-private/pull/3',
26+
'22.x': 'https://github.com/nodejs-private/node-private/pull/3'
27+
}
28+
}
29+
],
30+
dependencies: {
31+
undici: {
32+
affectedVersions: {
33+
main: 'https://github.com/nodejs/node/pull/5'
34+
}
35+
}
36+
}
37+
};
38+
39+
it('closes each PR from affectedVersions once', async() => {
40+
const prompts = [];
41+
const closed = [];
42+
const release = new PrepareSecurityRelease({
43+
startSpinner() {},
44+
updateSpinner() {},
45+
stopSpinner() {},
46+
prompt(message) {
47+
prompts.push(message);
48+
return true;
49+
}
50+
});
51+
release.req = {
52+
closePullRequest(prid, repository) {
53+
closed.push({ prid, ...repository });
54+
}
55+
};
56+
57+
await release.closePullRequests(vulnerabilities);
58+
59+
assert.deepStrictEqual(closed, [
60+
{ owner: 'nodejs-private', repo: 'node-private', prid: 2 },
61+
{ owner: 'nodejs-private', repo: 'node-private', prid: 3 },
62+
{ owner: 'nodejs', repo: 'node', prid: 5 }
63+
]);
64+
assert.strictEqual(prompts.length, closed.length);
65+
});
66+
});
67+
1968
function report(id, rating, affectedVersions = ['24.x']) {
2069
return {
2170
id,

0 commit comments

Comments
 (0)