-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.ts
More file actions
89 lines (73 loc) · 2.83 KB
/
index.ts
File metadata and controls
89 lines (73 loc) · 2.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#!/usr/bin/env bun
import { $ } from 'bun'
const GITHUB_TOKEN_URL = 'https://github.com/settings/tokens/new?scopes=gist,repo&description=GitHub%20Cleaner'
const headers = (token: string) => ({
Authorization: `token ${token}`,
Accept: 'application/vnd.github+json',
'User-Agent': 'bun-github-cleaner',
})
const fetchAll = async (url: string, token: string): Promise<any[]> => {
let results: any[] = []
let next = url
while (next) {
const res = await fetch(next, { headers: headers(token) })
if (!res.ok) throw new Error(`Failed to fetch ${next}`)
const data = await res.json()
results.push(...data)
const link = res.headers.get('link')
const match = link?.match(/<([^>]+)>;\s*rel="next"/)
next = match?.[1] ?? ''
}
return results
}
const deleteGists = async (token: string, filter: 'public' | 'private' | 'forks' | 'all') => {
const gists = await fetchAll('https://api.github.com/gists', token)
const toDelete = gists.filter((gist: any) => {
if (filter === 'all') return true
if (filter === 'public') return gist.public
if (filter === 'private') return !gist.public
if (filter === 'forks') return gist.forks?.length > 0
return false
})
for (const gist of toDelete) {
const res = await fetch(gist.url, { method: 'DELETE', headers: headers(token) })
res.status === 204
? console.log(`Deleted gist: ${gist.id}`)
: console.warn(`Failed to delete gist: ${gist.id}`)
}
}
const unstarAllRepos = async (token: string) => {
const starred = await fetchAll('https://api.github.com/user/starred', token)
for (const repo of starred) {
const url = `https://api.github.com/user/starred/${repo.owner.login}/${repo.name}`
const res = await fetch(url, { method: 'DELETE', headers: headers(token) })
res.status === 204
? console.log(`Unstarred: ${repo.full_name}`)
: console.warn(`Failed to unstar: ${repo.full_name}`)
}
}
const ask = async (msg: string): Promise<string> => {
process.stdout.write(`${msg.trim()} `)
return new Promise(resolve => {
process.stdin.once('data', data => {
resolve(data.toString().trim())
})
})
}
const main = async () => {
console.log('\n--- GitHub Cleaner ---\n')
console.log(`> Open this link to create a token with required permissions:\n${GITHUB_TOKEN_URL}\n`)
const token = await ask('Paste your GitHub token here:')
if (!token) {
console.error('Token is required.')
process.exit(1)
}
const action = (await ask('\nChoose action: [gists, stars, all]:')).toLowerCase()
let gistType = 'all'
if (action === 'gists' || action === 'all') {
gistType = (await ask('Gists to delete: [public, private, forks, all]:')).toLowerCase()
}
if (action === 'gists' || action === 'all') await deleteGists(token, gistType as any)
if (action === 'stars' || action === 'all') await unstarAllRepos(token)
}
main()