forked from desktop/dugite-native
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathupdate-git.ts
218 lines (180 loc) Β· 5.89 KB
/
update-git.ts
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
import path from 'path'
import crypto from 'crypto'
import ChildProcess from 'child_process'
import { Octokit, RestEndpointMethodTypes } from '@octokit/rest'
import semver from 'semver'
import { updateGitDependencies } from './lib/dependencies'
import yargs from 'yargs'
import fetch from 'node-fetch'
process.on('unhandledRejection', reason => {
console.log(reason)
})
const root = path.dirname(__dirname)
const gitDir = path.join(root, 'git')
// OMG
type ReleaseAssets = RestEndpointMethodTypes['repos']['getLatestRelease']['response']['data']['assets']
function spawn(cmd: string, args: Array<string>, cwd: string): Promise<string> {
return new Promise((resolve, reject) => {
const child = ChildProcess.spawn(cmd, args, { cwd })
let receivedData = ''
child.on('error', reject)
if (child.stdout === null) {
reject(new Error('Unable to read stdout of child process'))
return
}
child.stdout.on('data', (data: any) => {
receivedData += data
})
child.on('close', (code: number, signal: string) => {
if (code === 0) {
resolve(receivedData)
} else {
reject(
new Error(
`'${cmd} ${args.join(
' '
)}' exited with code ${code}, signal ${signal}`
)
)
}
})
})
}
async function refreshGitSubmodule() {
await spawn('git', ['submodule', 'update', '--init'], root)
await spawn('git', ['fetch', '--tags'], gitDir)
}
async function checkout(tag: string) {
await spawn('git', ['checkout', tag], gitDir)
}
async function getLatestStableRelease() {
const allTags = await spawn('git', ['tag', '--sort=v:refname'], gitDir)
const releaseTags = allTags
.split('\n')
.filter(tag => tag.indexOf('-rc') === -1)
.filter(tag => semver.valid(tag) !== null)
const sortedTags = semver.sort(releaseTags)
const latestTag = sortedTags[sortedTags.length - 1]
return latestTag.toString()
}
async function calculateAssetChecksum(uri: string) {
return new Promise<string>((resolve, reject) => {
const hs = crypto.createHash('sha256', { encoding: 'hex' })
hs.on('finish', () => resolve(hs.read()))
const headers: Record<string, string> = {
'User-Agent': 'dugite-native',
accept: 'application/octet-stream',
}
fetch(uri, { headers })
.then(x =>
x.ok
? Promise.resolve(x)
: Promise.reject(new Error(`Server responded with ${x.status}`))
)
.then(x => x.buffer())
.then(x => hs.end(x))
})
}
async function getPackageDetails(
assets: ReleaseAssets,
body: string,
arch: string
) {
const archValue = arch === 'amd64' ? '64-bit' : '32-bit'
const minGitFile = assets.find(
a => a.name.indexOf('MinGit') !== -1 && a.name.indexOf(archValue) !== -1
)
if (minGitFile == null) {
const foundFiles = assets.map(a => a.name)
console.log(
`π΄ Could not find ${archValue} archive. Found these files instead: ${JSON.stringify(
foundFiles
)}`
)
return null
}
const filename = minGitFile.name
const checksumRe = new RegExp(`${filename}\\s*\\|\\s*([0-9a-f]{64})`)
const match = checksumRe.exec(body)
let checksum: string
if (match == null || match.length !== 2) {
console.log(`π΄ No checksum for ${archValue} in release notes body`)
checksum = await calculateAssetChecksum(minGitFile.browser_download_url)
console.log(`β
Calculated checksum for ${archValue} from downloaded asset`)
} else {
console.log(`β
Got checksum for ${archValue} from release notes body`)
checksum = match[1]
}
return {
platform: 'windows',
arch,
filename,
url: minGitFile.browser_download_url,
checksum,
}
}
async function run() {
const argv = yargs
.usage('Usage: update-git [options]')
.version(false)
.option('tag', { default: 'latest', desc: 'The Git tag to use' })
.option('g4w-tag', {
alias: 'g4w',
default: 'latest',
desc: 'The Git for Windows tag to use',
})
.option('ignore-version-mismatch', {
desc:
"Continue update even if the Git for Windows version and the Git submodule (macOS, Linux) don't match. " +
'Use with caution.',
default: false,
boolean: true,
}).argv
await refreshGitSubmodule()
const latestGitVersion =
argv['tag'] === 'latest' ? await getLatestStableRelease() : argv['tag']
console.log(`β
Using Git version '${latestGitVersion}'`)
await checkout(latestGitVersion)
const token = process.env.GITHUB_ACCESS_TOKEN
const octokit = new Octokit(token ? { auth: `token ${token}` } : {})
if (token) {
const user = await octokit.users.getAuthenticated({})
const me = user.data.login
console.log(`β
Token found for ${me}`)
} else {
console.log(
`π΄ No GITHUB_ACCESS_TOKEN environment variable set. Requests may be rate limited.`
)
}
const owner = 'git-for-windows'
const repo = 'git'
const release =
argv['g4w-tag'] === 'latest'
? await octokit.repos.getLatestRelease({ owner, repo })
: await octokit.repos.getReleaseByTag({
owner,
repo,
tag: argv['g4w-tag'],
})
const { tag_name, body, assets } = release.data
const version = tag_name
console.log(`β
Using Git for Windows version '${version}'`)
if (!version.startsWith(latestGitVersion)) {
console.log(
`π΄ Latest Git for Windows version is ${version} which is a different series to Git version ${latestGitVersion}`
)
if (argv['ignore-version-mismatch'] !== true) {
return
}
}
const package64bit = await getPackageDetails(assets, body, 'amd64')
const package32bit = await getPackageDetails(assets, body, 'x86')
if (package64bit == null || package32bit == null) {
return
}
updateGitDependencies(latestGitVersion, [package64bit, package32bit])
console.log(
`β
Updated dependencies metadata to Git ${latestGitVersion} (Git for Windows ${version})`
)
}
run()