UNPKG

6.88 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3const childProcess = require('child_process')
4const fs = require('fs')
5const path = require('path')
6const util = require('util')
7const request = require('request')
8const promiseRetryer = require('promise-retryer')(Promise)
9
10const token = process.env.GITHUB_ACCESS_TOKEN
11const version = require('../package').version
12const repo = 'yoshuawuyts/vmd'
13
14checkToken()
15 .then(archiveAssets)
16 .then(createRelease)
17 .then(uploadAssets)
18 .then(publishRelease)
19 .catch((err) => {
20 console.error(err.message || err)
21 process.exit(1)
22 })
23
24function checkToken () {
25 if (!token) {
26 return Promise.reject('GITHUB_ACCESS_TOKEN environment variable not set\nSet it to a token with repo scope created from https://github.com/settings/tokens/new')
27 } else {
28 return Promise.resolve(token)
29 }
30}
31
32function archiveAssets () {
33 const outPath = path.join(__dirname, '..', 'build')
34
35 const assets = [
36 {
37 name: `vmd-${version}-mac.zip`,
38 path: path.join(outPath, 'vmd-darwin-x64', 'vmd.app')
39 },
40 {
41 name: `vmd-${version}-mac.tar.gz`,
42 path: path.join(outPath, 'vmd-darwin-x64', 'vmd.app')
43 },
44 {
45 name: `vmd-${version}-linux-ia32.zip`,
46 path: path.join(outPath, 'vmd-linux-ia32')
47 },
48 {
49 name: `vmd-${version}-linux-ia32.tar.gz`,
50 path: path.join(outPath, 'vmd-linux-ia32')
51 },
52 {
53 name: `vmd-${version}-linux-x64.zip`,
54 path: path.join(outPath, 'vmd-linux-x64')
55 },
56 {
57 name: `vmd-${version}-linux-x64.tar.gz`,
58 path: path.join(outPath, 'vmd-linux-x64')
59 },
60 {
61 name: `vmd-${version}-win32-ia32.zip`,
62 path: path.join(outPath, 'vmd-win32-ia32')
63 },
64 {
65 name: `vmd-${version}-win32-ia32.tar.gz`,
66 path: path.join(outPath, 'vmd-win32-ia32')
67 },
68 {
69 name: `vmd-${version}-win32-x64.zip`,
70 path: path.join(outPath, 'vmd-win32-x64')
71 },
72 {
73 name: `vmd-${version}-win32-x64.tar.gz`,
74 path: path.join(outPath, 'vmd-win32-x64')
75 }
76 ]
77
78 function archiveAsset (asset) {
79 if (/\.zip$/.test(asset.name)) {
80 return zipAsset(asset)
81 }
82
83 if (/\.tar\.gz$/.test(asset.name)) {
84 return targzAsset(asset)
85 }
86 }
87
88 return Promise.all(assets.map(archiveAsset))
89}
90
91function zipAsset (asset) {
92 return new Promise((resolve, reject) => {
93 const assetBase = path.basename(asset.path)
94 const assetDirectory = path.dirname(asset.path)
95 const outPath = path.join(__dirname, '..', 'build', asset.name)
96
97 console.log(`zipping ${assetBase} to ${asset.name}`)
98
99 if (!fs.existsSync(asset.path)) {
100 return reject(new Error(`${asset.path} does not exist`))
101 }
102
103 const command = `zip --recurse-paths --symlinks '${outPath}' '${assetBase}'`
104 const options = {
105 cwd: assetDirectory,
106 maxBuffer: Infinity
107 }
108
109 childProcess.exec(command, options, (err) => {
110 if (err) {
111 reject(err)
112 } else {
113 asset.path = outPath
114 resolve(asset)
115 }
116 })
117 })
118}
119
120function targzAsset (asset) {
121 return new Promise((resolve, reject) => {
122 const assetBase = path.basename(asset.path)
123 const assetDirectory = path.dirname(asset.path)
124 const outPath = path.join(__dirname, '..', 'build', asset.name)
125
126 console.log(`gzipping ${assetBase} to ${asset.name}`)
127
128 if (!fs.existsSync(asset.path)) {
129 return reject(new Error(`${asset.path} does not exist`))
130 }
131
132 const command = `tar -czf '${outPath}' '${assetBase}'`
133 const options = {
134 cwd: assetDirectory,
135 maxBuffer: Infinity
136 }
137
138 childProcess.exec(command, options, (err) => {
139 if (err) {
140 reject(err)
141 } else {
142 asset.path = outPath
143 resolve(asset)
144 }
145 })
146 })
147}
148
149function createRelease (assets) {
150 const options = {
151 uri: `https://api.github.com/repos/${repo}/releases`,
152 headers: {
153 Authorization: `token ${token}`,
154 'User-Agent': `node/${process.versions.node}`
155 },
156 json: {
157 tag_name: `${version}`,
158 target_commitish: 'master',
159 name: `vmd v${version}`,
160 body: 'An awesome new release :tada:',
161 draft: true,
162 prerelease: false
163 }
164 }
165
166 return new Promise((resolve, reject) => {
167 console.log(`creating new draft release v${version}`)
168
169 request.post(options, (err, response, body) => {
170 if (err) {
171 return reject(Error(`Request failed: ${err.message || err}`))
172 }
173
174 if (response.statusCode !== 201) {
175 return reject(Error(`Non-201 response: ${response.statusCode}\n${util.inspect(body)}`))
176 }
177
178 resolve({assets: assets, draft: body})
179 })
180 })
181}
182
183function publishRelease (release) {
184 const options = {
185 uri: release.draft.url,
186 headers: {
187 Authorization: `token ${token}`,
188 'User-Agent': `node/${process.versions.node}`
189 },
190 json: {
191 draft: false
192 }
193 }
194
195 return new Promise((resolve, reject) => {
196 console.log('publishing release')
197
198 request.post(options, (err, response, body) => {
199 if (err) {
200 return reject(Error(`Request failed: ${err.message || err}`))
201 }
202
203 if (response.statusCode !== 200) {
204 return reject(Error(`Non-200 response: ${response.statusCode}\n${util.inspect(body)}`))
205 }
206
207 resolve(body)
208 })
209 })
210}
211
212function uploadAssets (release) {
213 return Promise.all(release.assets.map((asset) => {
214 return uploadAsset(release.draft, asset)
215 })).then(() => release)
216}
217
218function uploadAsset (release, asset) {
219 return promiseRetryer.run({
220 delay: function (attempt) {
221 return attempt * 1000
222 },
223 onAttempt: function (attempt) {
224 console.log(`attempt ${attempt} to upload ${asset.name}`)
225 },
226 onError: function (err, attempt) {
227 console.log(`failed to upload ${asset.name} at attempt ${attempt}: ${err.message || err}`)
228 },
229 maxRetries: 3,
230 promise: function (attempt) {
231 return upload(release, asset)
232 }
233 })
234
235 function upload (release, asset) {
236 const contentType = {
237 '.zip': 'application/zip',
238 '.gz': 'application/tar+gzip'
239 }[path.extname(asset.name)]
240
241 const options = {
242 uri: release.upload_url.replace(/\{.*$/, `?name=${asset.name}`),
243 headers: {
244 Authorization: `token ${token}`,
245 'Content-Type': contentType,
246 'Content-Length': fs.statSync(asset.path).size,
247 'User-Agent': `node/${process.versions.node}`
248 }
249 }
250
251 return new Promise((resolve, reject) => {
252 const assetRequest = request.post(options, (err, response, body) => {
253 if (err) {
254 return reject(Error(`Uploading asset failed: ${err.message || err}`))
255 }
256
257 if (response.statusCode >= 400) {
258 return reject(Error(`400+ response: ${response.statusCode}\n${util.inspect(body)}`))
259 }
260
261 resolve(asset)
262 })
263
264 fs.createReadStream(asset.path).pipe(assetRequest)
265 })
266 }
267}