forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix elastic#7136 - check SHA of downloaded node binaries
- Loading branch information
Showing
4 changed files
with
133 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,84 +1,145 @@ | ||
module.exports = function (grunt) { | ||
let { map, fromNode } = require('bluebird'); | ||
let { resolve } = require('path'); | ||
let { pluck } = require('lodash'); | ||
let { createWriteStream } = require('fs'); | ||
let { createGunzip } = require('zlib'); | ||
let { Extract } = require('tar'); | ||
let { rename } = require('fs'); | ||
let wreck = require('wreck'); | ||
|
||
let platforms = grunt.config.get('platforms'); | ||
let activeDownloads = []; | ||
|
||
let start = async (platform) => { | ||
let finalDir = platform.nodeDir; | ||
let downloadDir = `${finalDir}.temp`; | ||
import { Promise, map, fromNode, promisify } from 'bluebird'; | ||
import { resolve, basename, dirname, join } from 'path'; | ||
import { createReadStream, createWriteStream, writeFile } from 'fs'; | ||
import { createGunzip } from 'zlib'; | ||
import { Extract } from 'tar'; | ||
import { fromFile } from 'check-hash'; | ||
import wreck from 'wreck'; | ||
|
||
if (grunt.file.isDir(platform.nodeDir)) { | ||
grunt.log.ok(`${platform.name} exists`); | ||
return; | ||
} | ||
const wreckGetAsync = promisify(wreck.get, wreck); | ||
const checkHashFromFileAsync = promisify(fromFile); | ||
const writeFileAsync = promisify(writeFile); | ||
|
||
let resp = await fromNode(cb => { | ||
let req = wreck.request('GET', platform.nodeUrl, null, function (err, resp) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
export default function downloadNodeBuilds(grunt) { | ||
const platforms = grunt.config.get('platforms'); | ||
const downloadLimit = 3; | ||
|
||
if (resp.statusCode !== 200) { | ||
return cb(new Error(`${platform.nodeUrl} failed with a ${resp.statusCode} response`)); | ||
} | ||
let shaSums = {}; | ||
const getShaSums = () => { | ||
const nodeVersion = grunt.config.get('nodeVersion'); | ||
const shaSumsUri = `https://nodejs.org/dist/v${nodeVersion}/SHASUMS256.txt`; | ||
|
||
return cb(null, resp); | ||
return wreckGetAsync(shaSumsUri).then(([resp, payload]) => { | ||
if (resp.statusCode !== 200) { | ||
throw new Error(`${shaSumsUri} failed with a ${resp.statusCode} response`); | ||
} | ||
payload | ||
.toString('utf8') | ||
.split('\n') | ||
.forEach(line => { | ||
const [sha, platform] = line.split(' '); | ||
shaSums[platform] = sha; | ||
}); | ||
grunt.log.ok(`sha sums download complete`); | ||
}); | ||
}; | ||
|
||
const checkShaSum = (platform) => { | ||
const file = basename(platform.nodeUrl); | ||
const downloadDir = join(platform.nodeDir, '..'); | ||
const filePath = resolve(downloadDir, file); | ||
const expected = { | ||
hash: 'sha256', | ||
expected: platform.win ? shaSums[basename(dirname(platform.nodeUrl)) + '/' + file] : shaSums[file] | ||
}; | ||
|
||
// use an async iife to store promise for download | ||
// then store platform in active downloads list | ||
// which we will read from in the finish task | ||
platform.downloadPromise = (async function () { | ||
grunt.file.mkdir(downloadDir); | ||
|
||
if (platform.win) { | ||
await fromNode(cb => { | ||
resp | ||
.pipe(createWriteStream(resolve(downloadDir, 'node.exe'))) | ||
.on('error', cb) | ||
.on('finish', cb); | ||
}); | ||
if (!grunt.file.isFile(filePath)) { | ||
return false; | ||
} | ||
|
||
return checkHashFromFileAsync(filePath, expected).then(([passed, actual]) => { | ||
if (passed) { | ||
grunt.log.ok(`${platform.name} shasum verified`); | ||
} else { | ||
await fromNode(cb => { | ||
resp | ||
.pipe(createGunzip()) | ||
.on('error', cb) | ||
.pipe(new Extract({ path: downloadDir, strip: 1 })) | ||
.on('error', cb) | ||
.on('end', cb); | ||
}); | ||
grunt.log.error(`${platform.name} shasum check failed`); | ||
} | ||
return passed; | ||
}); | ||
}; | ||
|
||
const getNodeBuild = (platform) => { | ||
const downloadDir = join(platform.nodeDir, '..'); | ||
const file = basename(platform.nodeUrl); | ||
const filePath = resolve(downloadDir, file); | ||
|
||
if (grunt.file.isFile(filePath)) { | ||
grunt.file.delete(filePath); | ||
} | ||
|
||
grunt.log.ok(`downloading ${platform.name}`); | ||
|
||
return wreckGetAsync(platform.nodeUrl) | ||
.then(([resp, payload]) => { | ||
if (resp.statusCode !== 200) { | ||
throw new Error(`${platform.nodeUrl} failed with a ${resp.statusCode} response`); | ||
} | ||
return payload; | ||
}) | ||
.then(payload => writeFileAsync(filePath, payload)); | ||
|
||
await fromNode(cb => { | ||
rename(downloadDir, finalDir, cb); | ||
}); | ||
}()); | ||
}; | ||
|
||
activeDownloads.push(platform); | ||
const start = async (platform) => { | ||
let downloadCounter = 0; | ||
let isDownloadValid = await checkShaSum(platform); | ||
|
||
var bytes = parseInt(resp.headers['content-length'], 10) || 'unknown number of'; | ||
var mb = ((bytes / 1024) / 1024).toFixed(2); | ||
grunt.log.ok(`downloading ${platform.name} - ${mb} mb`); | ||
if (isDownloadValid) return; | ||
|
||
grunt.log.ok('starting download ...'); | ||
while (!isDownloadValid && (downloadCounter < downloadLimit)) { | ||
await getNodeBuild(platform); | ||
isDownloadValid = await checkShaSum(platform); | ||
++downloadCounter; | ||
} | ||
|
||
if (!isDownloadValid) { | ||
throw new Error(`${platform.name} download failed`); | ||
} | ||
|
||
grunt.log.ok(`download of ${platform.name} completed successfully`); | ||
}; | ||
|
||
grunt.registerTask('_build:downloadNodeBuilds:start', function () { | ||
map(platforms, start).nodeify(this.async()); | ||
grunt.registerTask('_build:downloadNodeBuilds', function () { | ||
const done = this.async(); | ||
getShaSums() | ||
.then(() => map(platforms, start)) | ||
.nodeify(done); | ||
}); | ||
|
||
grunt.registerTask('_build:downloadNodeBuilds:finish', function () { | ||
map(activeDownloads, async (platform) => { | ||
await platform.downloadPromise; | ||
grunt.log.ok(`${platform.name} download complete`); | ||
}) | ||
.nodeify(this.async()); | ||
const extractNodeBuild = async (platform) => { | ||
const file = basename(platform.nodeUrl); | ||
const downloadDir = join(platform.nodeDir, '..'); | ||
const filePath = resolve(downloadDir, file); | ||
|
||
return new Promise((resolve, reject) => { | ||
createReadStream(filePath) | ||
.pipe(createGunzip()) | ||
.on('error', reject) | ||
.pipe(new Extract({path: platform.nodeDir, strip: 1})) | ||
.on('error', reject) | ||
.on('end', resolve); | ||
}); | ||
}; | ||
|
||
const extract = async(platform) => { | ||
const file = basename(platform.nodeUrl); | ||
const downloadDir = join(platform.nodeDir, '..'); | ||
const filePath = resolve(downloadDir, file); | ||
|
||
if (grunt.file.isDir(platform.nodeDir)) { | ||
grunt.file.delete(platform.nodeDir); | ||
} | ||
|
||
if (platform.win) { | ||
grunt.file.mkdir(platform.nodeDir); | ||
grunt.file.copy(filePath, resolve(platform.nodeDir, file)); | ||
} else { | ||
await extractNodeBuild(platform); | ||
} | ||
}; | ||
|
||
grunt.registerTask('_build:extractNodeBuilds', function () { | ||
map(platforms, extract).nodeify(this.async()); | ||
}); | ||
|
||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters