-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
177 lines (155 loc) · 5.12 KB
/
index.js
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
const path = require('path')
const fs = require('fs')
const crypto = require('crypto')
const mkdirp = require('mkdirp')
const rimraf = require('rimraf')
const UUID = require('uuid')
const request = require('request')
// assert
const isSHA256 = hash => /[a-f0-9]{64}/.test(hash)
// constants
const branch = process.argv.includes('--staging') ? 'staging' : 'release'
console.log(`INFO using ${branch} branch`)
const githubPrepend = `https://raw.githubusercontent.com/wisnuc/wisnuc-bootstrap/${branch}/`
const mirrorPrepend = `https://mirrors.wisnuc.com/wisnuc-bootstrap/${branch}/`
/**
/wisnuc/wisnuc-bootstrap <- target
/wisnuc/wisnuc-bootstrap-update-tmp <- tmpDir
*/
// override with --path, for testing
const root = process.argv.find((_, index, arr) => arr[index - 1] === '--root') || '/wisnuc'
const targetPath = path.join(path.resolve(root), 'wisnuc-bootstrap')
const tmpDir = path.join(path.resolve(root), 'wisnuc-bootstrap-update-tmp')
/**
Returns sha256 of given file, null if non-exist
*/
const hashFile = (filePath, callback) =>
fs.stat(filePath, (err, stat) => {
if (err && err.code === 'ENOENT') {
callback(null, null)
} else if (err) {
callback(err)
} else {
if (stat.isFile()) {
let hash = crypto.createHash('sha256')
let rs = fs.createReadStream(filePath)
rs.on('error', err => {
rs.removeAllListeners()
rs.on('error', () => {})
rs.destroy()
callback(err)
})
rs.on('data', data => hash.update(data))
rs.on('close', () => callback(null, hash.digest('hex')))
} else {
callback(new Error('target is not a file'))
}
}
})
/**
Download url to filePath
*/
const downloadFile = (url, filePath, callback) => {
let req = request.get(url)
let ws = fs.createWriteStream(filePath)
const destroy = () => {
req.removeAllListeners()
req.on('error', () => {})
ws.removeAllListeners()
ws.on('error', () => {})
ws.destroy()
}
req.on('error', err => (destroy(), callback(err)))
ws.on('error', err => (destroy(), callback(err)))
ws.on('close', () => callback(null))
req.pipe(ws)
}
const retrieveHash = (mirror, callback) => {
let prepend = mirror ? mirrorPrepend : githubPrepend
let filename = process.arch === 'x64'
? 'wisnuc-bootstrap-linux-x64-sha256'
: 'wisnuc-bootstrap-linux-a64-sha256'
let remoteUrl = prepend + filename
let tmpFile = path.join(tmpDir, UUID.v4())
console.log(`INFO retrieving remote hash from ${remoteUrl}`)
downloadFile(remoteUrl, tmpFile, err => {
if (err) return rimraf(tmpFile, () => callback(err))
fs.readFile(tmpFile, (err, data) => {
rimraf(tmpFile, () => {})
if (err) return callback(err)
let text = data.toString().trim()
if (!isSHA256(text)) {
callback(new Error('invalid sha256 string'))
} else {
callback(null, text)
}
})
})
}
/**
Download, verify checksum and update local file
*/
const update = (mirror, expectedHash) => {
let prepend = mirror ? mirrorPrepend : githubPrepend
let filename = process.arch === 'x64'
? 'wisnuc-bootstrap-linux-x64'
: 'wisnuc-bootstrap-linux-a64'
let remoteUrl = prepend + filename
let tmpPath = path.join(tmpDir, UUID.v4())
const error = text => (console.log('ERROR ' + text), rimraf(tmpPath, () => process.exit()))
const info = text => console.log('INFO ' + text)
info(`retrieving remote file from ${remoteUrl}`)
downloadFile(remoteUrl, tmpPath, err => {
if (err) {
error(`failed to download remote file`)
} else {
info(`remote file downloaded`)
hashFile(tmpPath, (err, actualHash) => {
if (err) {
error(`failed to hash downloaded file`)
} else if (actualHash !== expectedHash) {
error(`downloaded file hash mismatches`)
} else {
fs.chmod(tmpPath, '755', err => err
? error(`failed to set executable permission on downloaded file`)
: fs.rename(tmpPath, targetPath, err => err
? error(`failed to overwrite target file`)
: (info(`update succeeds`), process.exit())))
}
})
}
})
}
// for convenience
const tryUpdate = (remote, local, mirror) =>
(console.log(`INFO remote hash ${remote}`), remote === local
? (console.log('INFO remote hash and local hash matches'), process.exit())
: update(mirror, remote))
/**
main
*/
rimraf.sync(tmpDir)
mkdirp.sync(tmpDir)
hashFile(targetPath, (err, localHash) => {
if (err) {
console.log('ERROR failed to hash local file', err)
return process.exit(1)
} else {
console.log(`INFO local hash ${localHash}`)
retrieveHash(true, (err, remoteHash) => {
if (err) {
console.log('WARNING failed to retrieve remote hash from mirror site', err)
retrieveHash(false, (err, remoteHash) => {
if (err) {
console.log('ERROR failed to retrieve remote hash from github', err)
process.exit()
} else {
tryUpdate(remoteHash, localHash, false)
}
})
} else {
tryUpdate(remoteHash, localHash, true)
}
})
}
})