Skip to content

Commit 113abf8

Browse files
Copilotsam-byng
andcommitted
Add script to close all open PRs with documentation
Co-authored-by: sam-byng <43856946+sam-byng@users.noreply.github.com>
1 parent 871305e commit 113abf8

2 files changed

Lines changed: 247 additions & 0 deletions

File tree

CLOSE_PRS_README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Close Pull Requests Script
2+
3+
This script closes all open pull requests in the `github/haikus-for-codespaces` repository with a comment explaining that this is a template repository maintained by the Codespaces team.
4+
5+
## Prerequisites
6+
7+
- Node.js installed
8+
- A GitHub Personal Access Token with `repo` permissions
9+
10+
## How to Get a GitHub Token
11+
12+
1. Go to https://github.com/settings/tokens
13+
2. Click "Generate new token" (classic)
14+
3. Give it a descriptive name like "Close PRs Script"
15+
4. Select the `repo` scope (full control of private repositories)
16+
5. Click "Generate token"
17+
6. **Copy the token immediately** (you won't be able to see it again)
18+
19+
## Usage
20+
21+
```bash
22+
# Set your GitHub token as an environment variable
23+
export GITHUB_TOKEN=your_github_token_here
24+
25+
# Run the script
26+
node close-prs.js
27+
```
28+
29+
Or in a single command:
30+
31+
```bash
32+
GITHUB_TOKEN=your_github_token_here node close-prs.js
33+
```
34+
35+
## What the Script Does
36+
37+
1. Fetches all open pull requests in the repository
38+
2. For each open PR:
39+
- Adds a comment: "This is a template repo, with changes owned by the Codespaces team."
40+
- Closes the pull request
41+
3. Reports the status of each operation
42+
43+
## Example Output
44+
45+
```
46+
Fetching open PRs for github/haikus-for-codespaces...
47+
Found 8 open pull request(s).
48+
49+
Processing PR #463: [WIP] Force close all open PRs in template repo
50+
Adding comment...
51+
Closing PR...
52+
✓ Successfully closed PR #463
53+
54+
Processing PR #462: Create from wahyu to ania
55+
Adding comment...
56+
Closing PR...
57+
✓ Successfully closed PR #462
58+
59+
...
60+
61+
Done!
62+
```
63+
64+
## Security Notes
65+
66+
- Never commit your GitHub token to the repository
67+
- Use environment variables to pass sensitive credentials
68+
- The token should have minimal required permissions (repo scope)
69+
- Consider deleting the token after use
70+
71+
## Alternative: Using GitHub CLI
72+
73+
If you have `gh` (GitHub CLI) installed, you can also use it:
74+
75+
```bash
76+
# List all open PRs
77+
gh pr list --repo github/haikus-for-codespaces --state open
78+
79+
# Close each PR with a comment (manual approach)
80+
gh pr close <PR_NUMBER> --repo github/haikus-for-codespaces --comment "This is a template repo, with changes owned by the Codespaces team."
81+
```
82+
83+
## Notes
84+
85+
- This script uses the native Node.js `https` module (no external dependencies required)
86+
- The script will process all open PRs, including the one it was created from
87+
- Failed operations are logged but don't stop the script from processing remaining PRs

close-prs.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Script to close all open Pull Requests in the repository
5+
* with a specific comment explaining why they are being closed.
6+
*
7+
* This repository is a template repo maintained by the Codespaces team.
8+
*
9+
* Usage:
10+
* GITHUB_TOKEN=your_token node close-prs.js
11+
*
12+
* Note: Requires a GitHub token with repo permissions
13+
*/
14+
15+
const https = require('https');
16+
17+
const OWNER = 'github';
18+
const REPO = 'haikus-for-codespaces';
19+
const COMMENT_MESSAGE = 'This is a template repo, with changes owned by the Codespaces team.';
20+
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
21+
22+
if (!GITHUB_TOKEN) {
23+
console.error('Error: GITHUB_TOKEN environment variable is required');
24+
console.error('Usage: GITHUB_TOKEN=your_token node close-prs.js');
25+
process.exit(1);
26+
}
27+
28+
/**
29+
* Make an HTTPS request to GitHub API
30+
*/
31+
function makeRequest(options, data = null) {
32+
return new Promise((resolve, reject) => {
33+
const req = https.request(options, (res) => {
34+
let body = '';
35+
36+
res.on('data', (chunk) => {
37+
body += chunk;
38+
});
39+
40+
res.on('end', () => {
41+
if (res.statusCode >= 200 && res.statusCode < 300) {
42+
try {
43+
resolve(JSON.parse(body || '{}'));
44+
} catch (e) {
45+
resolve(body);
46+
}
47+
} else {
48+
reject(new Error(`Request failed with status ${res.statusCode}: ${body}`));
49+
}
50+
});
51+
});
52+
53+
req.on('error', reject);
54+
55+
if (data) {
56+
req.write(JSON.stringify(data));
57+
}
58+
59+
req.end();
60+
});
61+
}
62+
63+
/**
64+
* Get all open pull requests
65+
*/
66+
async function getOpenPRs() {
67+
const options = {
68+
hostname: 'api.github.com',
69+
path: `/repos/${OWNER}/${REPO}/pulls?state=open&per_page=100`,
70+
method: 'GET',
71+
headers: {
72+
'User-Agent': 'close-prs-script',
73+
'Authorization': `Bearer ${GITHUB_TOKEN}`,
74+
'Accept': 'application/vnd.github.v3+json'
75+
}
76+
};
77+
78+
return makeRequest(options);
79+
}
80+
81+
/**
82+
* Add a comment to a pull request
83+
*/
84+
async function addComment(prNumber, message) {
85+
const options = {
86+
hostname: 'api.github.com',
87+
path: `/repos/${OWNER}/${REPO}/issues/${prNumber}/comments`,
88+
method: 'POST',
89+
headers: {
90+
'User-Agent': 'close-prs-script',
91+
'Authorization': `Bearer ${GITHUB_TOKEN}`,
92+
'Accept': 'application/vnd.github.v3+json',
93+
'Content-Type': 'application/json'
94+
}
95+
};
96+
97+
return makeRequest(options, { body: message });
98+
}
99+
100+
/**
101+
* Close a pull request
102+
*/
103+
async function closePR(prNumber) {
104+
const options = {
105+
hostname: 'api.github.com',
106+
path: `/repos/${OWNER}/${REPO}/pulls/${prNumber}`,
107+
method: 'PATCH',
108+
headers: {
109+
'User-Agent': 'close-prs-script',
110+
'Authorization': `Bearer ${GITHUB_TOKEN}`,
111+
'Accept': 'application/vnd.github.v3+json',
112+
'Content-Type': 'application/json'
113+
}
114+
};
115+
116+
return makeRequest(options, { state: 'closed' });
117+
}
118+
119+
/**
120+
* Main function
121+
*/
122+
async function main() {
123+
try {
124+
console.log(`Fetching open PRs for ${OWNER}/${REPO}...`);
125+
const prs = await getOpenPRs();
126+
127+
if (prs.length === 0) {
128+
console.log('No open pull requests found.');
129+
return;
130+
}
131+
132+
console.log(`Found ${prs.length} open pull request(s).`);
133+
134+
for (const pr of prs) {
135+
console.log(`\nProcessing PR #${pr.number}: ${pr.title}`);
136+
137+
try {
138+
// Add comment
139+
console.log(` Adding comment...`);
140+
await addComment(pr.number, COMMENT_MESSAGE);
141+
142+
// Close the PR
143+
console.log(` Closing PR...`);
144+
await closePR(pr.number);
145+
146+
console.log(` ✓ Successfully closed PR #${pr.number}`);
147+
} catch (error) {
148+
console.error(` ✗ Failed to close PR #${pr.number}:`, error.message);
149+
}
150+
}
151+
152+
console.log('\nDone!');
153+
} catch (error) {
154+
console.error('Error:', error.message);
155+
process.exit(1);
156+
}
157+
}
158+
159+
// Run the script
160+
main();

0 commit comments

Comments
 (0)