Skip to content

Commit ac089c9

Browse files
committed
feat: improve UX
1 parent 6b4962e commit ac089c9

6 files changed

Lines changed: 127 additions & 91 deletions

File tree

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# Delete GitHub Forks
22

3-
Delete your forked GitHub repositories easily in two steps.
3+
Delete your forked GitHub repositories easily in two steps (takes less than 5 minutes).
44

5-
**Note:** Node 8+ is required because some new language features are used.
5+
## Motivation
66

7-
## Motivations
7+
Deleting GitHub repositories via the GitHub interface is a hassle; you have to enter your password followed by the name of the repository. This is not scalable if you contribute to open source a fair bit and have many forked repositories that you may not necessarily want to keep.
88

9-
Deleting GitHub repositories via the GitHub interface is a hassle; you have to enter your password followed by the name of the repository. This is not scalable if you contribute to open source a fair bit and have many forked repositories that you may not necessarily want to keep. Using this script, you can fetch a list of your GitHub repositories and delete the unwanted repositories in one go.
9+
Using these scripts, you can fetch a list of your GitHub repositories and delete all the unwanted repositories in one go.
1010

1111
## Getting Started
1212

@@ -24,13 +24,13 @@ Add your GitHub username and access token to `config.json`. To get the access to
2424
Firstly, run the following command to fetch all your forked repositories.
2525

2626
```sh
27-
$ npm run fetch-repos # Writes to a src/repos.json file
27+
$ npm run fetch # Writes to a src/repos.json file
2828
```
2929

3030
A JSON file, `src/repos.json` containing an array of your repositories will be written into the same directory. Manually inspect it and remove the forked repositories that you want to keep. **The repositories that remain inside `src/repos.json` will be deleted on the next command. It is an irreversible operation. Use with great caution!**.
3131

3232
```sh
33-
$ npm run delete-repos # Reads from src/repos.json and deletes the repos inside it.
33+
$ npm run delete # Reads from src/repos.json and deletes the repos inside it.
3434
```
3535

3636
And all the repositories within `src/repos.json` will be deleted! It's that easy.

package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
{
22
"name": "delete-github-forks",
33
"version": "0.1.0",
4-
"description": "Delete your Github forks.",
5-
"main": "fetch-repos.js",
4+
"author": "Yangshun Tay",
5+
"description": "Delete your GitHub forks in bulk.",
6+
"scripts": {
7+
"fetch": "node src/fetchRepos.js",
8+
"delete": "node src/deleteRepos.js"
9+
},
610
"dependencies": {
711
"axios": "^0.21.2"
812
},
9-
"devDependencies": {},
10-
"scripts": {
11-
"fetch-repos": "node src/fetch-repos.js",
12-
"delete-repos": "node src/delete-repos.js",
13-
"test": "echo \"Error: no test specified\" && exit 1"
13+
"engines": {
14+
"node": ">=12.0.0"
1415
},
15-
"author": "Yangshun Tay",
1616
"license": "MIT"
1717
}

src/delete-repos.js

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/deleteRepos.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
const axios = require('axios');
2+
const config = require('./config');
3+
const readline = require('readline');
4+
5+
const reposForDeletion = require('./repos');
6+
7+
async function deleteRepos(repos) {
8+
let deleted = 0;
9+
let failed = 0;
10+
11+
for (let i = 0; i < repos.length; i++) {
12+
const repo = repos[i];
13+
const URL = `${config.api_url}/repos/${repo}`;
14+
15+
try {
16+
await axios.delete(URL, {
17+
headers: {
18+
Accept: 'application/vnd.github.v3+json',
19+
Authorization: `token ${config.access_token}`,
20+
},
21+
});
22+
deleted++;
23+
console.log(`${repo} deleted!`);
24+
} catch (err) {
25+
failed++;
26+
console.error(`Error deleting ${repo}`);
27+
}
28+
}
29+
30+
if (deleted > 0) {
31+
console.log(`${deleted} repo(s) deleted!`);
32+
}
33+
34+
if (failed > 0) {
35+
console.log(`Failed to delete ${failed} repo(s)`);
36+
}
37+
}
38+
39+
const rl = readline.createInterface({
40+
input: process.stdin,
41+
output: process.stdout,
42+
terminal: false,
43+
});
44+
45+
console.log('The following repos will be deleted:');
46+
console.log(reposForDeletion.map((repo) => `- ${repo}`).join('\n'));
47+
console.log();
48+
console.log('Are you sure you want to delete the following repos? y/n');
49+
50+
rl.on('line', async function (line) {
51+
if (line.trim().toLowerCase() === 'y') {
52+
console.log();
53+
await deleteRepos(reposForDeletion);
54+
}
55+
56+
// Terminate the process.
57+
rl.close();
58+
});

src/fetch-repos.js

Lines changed: 0 additions & 52 deletions
This file was deleted.

src/fetchRepos.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
const axios = require('axios');
2+
const fs = require('fs');
3+
const path = require('path');
4+
5+
const config = require('./config');
6+
7+
const username = config.github_username;
8+
const URL = `${config.api_url}/users/${username}/repos`;
9+
const OUT_FILE = 'src/repos.json';
10+
11+
async function fetchRepos(url) {
12+
const repos = [];
13+
let page = 1;
14+
15+
while (true) {
16+
try {
17+
const res = await axios.get(url, {
18+
headers: {
19+
Accept: 'application/vnd.github.v3+json',
20+
Authorization: `token ${config.access_token}`,
21+
},
22+
params: {
23+
page,
24+
},
25+
});
26+
27+
if (res.data.length === 0) {
28+
break;
29+
}
30+
31+
const forkedRepos = res.data
32+
.filter((repo) => repo.fork)
33+
.map((repo) => repo.full_name);
34+
console.log(`[Page ${page}] Found ${forkedRepos.length} forked repo(s):`);
35+
console.log(forkedRepos.map((repo) => `- ${repo}`).join('\n') + '\n');
36+
repos.push(...forkedRepos);
37+
page++;
38+
} catch (err) {
39+
console.error(`Error fetching page ${page}: ${err}`);
40+
break;
41+
}
42+
}
43+
44+
return repos;
45+
}
46+
47+
fetchRepos(URL).then((result) => {
48+
console.log('Forked repos found:', result.length);
49+
console.log(result.map((repo) => `- ${repo}`).join('\n'));
50+
console.log();
51+
fs.writeFileSync(OUT_FILE, JSON.stringify(result, null, 2));
52+
console.log(
53+
`Wrote forked repos into "${path.join(process.cwd(), OUT_FILE)}"`,
54+
);
55+
});

0 commit comments

Comments
 (0)