From cb85790ce79abc6d495cb663e072008ca62d8cf5 Mon Sep 17 00:00:00 2001 From: develar Date: Wed, 4 Jan 2017 11:01:59 +0100 Subject: [PATCH] fix(electron-builder-http): electron-auto-updater request can download so fast that the first few chunks arrive before ensureDirPromise has finished for configurePipes to run Closes #1081 --- .idea/rc-producer.yml | 2 +- package.json | 4 +- packages/electron-auto-updater/package.json | 2 +- .../src/electronHttpExecutor.ts | 37 +++++++------- .../electron-builder-http/src/httpExecutor.ts | 6 ++- .../src/util/nodeHttpExecutor.ts | 48 +++++++++---------- test/src/helpers/setVersions.ts | 37 ++++++++++++-- yarn.lock | 10 ++-- 8 files changed, 89 insertions(+), 57 deletions(-) diff --git a/.idea/rc-producer.yml b/.idea/rc-producer.yml index 7d6adfe85f9..a54d8d6e78a 100644 --- a/.idea/rc-producer.yml +++ b/.idea/rc-producer.yml @@ -1,7 +1,7 @@ - &defaults files: ["test/src/**/*", "!**/helpers/**/*"] script: "node_modules/jest-cli/bin/jest.js" - scriptArgs: ["-i", "--env", "jest-environment-node-debug", &filePattern '/${fileNameWithoutExt}\.\w+$'] + scriptArgs: ["-i", "--env", "jest-environment-node-debug", &filePattern '--testPathPattern=[/\\]{1}${fileNameWithoutExt}\.\w+$'] rcName: "${fileNameWithoutExt}" beforeRun: typescript diff --git a/package.json b/package.json index f65fd292ff3..b2ad251ecc5 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ "test-deps-mac": "brew install rpm dpkg mono lzip gnu-tar graphicsmagick xz && brew install wine --without-x11", "postinstall": "lerna bootstrap", "update-deps": "lerna exec -- npm-check-updates --reject 'electron-builder-http'", - "lerna-publish": "lerna publish --skip-npm --skip-git" + "lerna-publish": "lerna publish --skip-npm --skip-git", + "set-versions": "node test/out/helpers/setVersions.js p", + "set-dep-versions": "node test/out/helpers/setVersions.js" }, "devDependencies": { "@develar/semantic-release": "^6.3.26", diff --git a/packages/electron-auto-updater/package.json b/packages/electron-auto-updater/package.json index d55bf096e4e..7c1090ebc5e 100644 --- a/packages/electron-auto-updater/package.json +++ b/packages/electron-auto-updater/package.json @@ -1,6 +1,6 @@ { "name": "electron-auto-updater", - "version": "0.8.5", + "version": "0.0.0-semantic-release", "description": "NSIS Auto Updater", "main": "out/main.js", "author": "Vladimir Krivosheev", diff --git a/packages/electron-auto-updater/src/electronHttpExecutor.ts b/packages/electron-auto-updater/src/electronHttpExecutor.ts index ca2fb8bce7a..b987e195a48 100644 --- a/packages/electron-auto-updater/src/electronHttpExecutor.ts +++ b/packages/electron-auto-updater/src/electronHttpExecutor.ts @@ -3,7 +3,7 @@ import { net } from "electron" import { ensureDir } from "fs-extra-p" import BluebirdPromise from "bluebird-lst-c" import * as path from "path" -import { HttpExecutor, DownloadOptions, HttpError, checkSha2, maxRedirects, safeGetHeader, configurePipes } from "electron-builder-http" +import { HttpExecutor, DownloadOptions, HttpError, maxRedirects, safeGetHeader, configurePipes } from "electron-builder-http" import { safeLoad } from "js-yaml" import _debug from "debug" import Debugger = debug.Debugger @@ -12,17 +12,21 @@ import { parse as parseUrl } from "url" export class ElectronHttpExecutor extends HttpExecutor { private readonly debug: Debugger = _debug("electron-builder") - download(url: string, destination: string, options?: DownloadOptions | null): Promise { - return new BluebirdPromise( (resolve, reject) => { - this.doDownload(url, destination, 0, options || {}, (error: Error) => { - if (error == null) { - resolve(destination) - } - else { - reject(error) - } - }) + async download(url: string, destination: string, options?: DownloadOptions | null): Promise { + if (options == null || !options.skipDirCreation) { + await ensureDir(path.dirname(destination)) + } + + return await new BluebirdPromise((resolve, reject) => { + this.doDownload(url, destination, 0, options || {}, (error: Error) => { + if (error == null) { + resolve(destination) + } + else { + reject(error) + } }) + }) } private addTimeOutHandler(request: Electron.ClientRequest, callback: (error: Error) => void) { @@ -35,12 +39,9 @@ export class ElectronHttpExecutor extends HttpExecutor void) { - const ensureDirPromise = options.skipDirCreation ? BluebirdPromise.resolve() : ensureDir(path.dirname(destination)) - const parsedUrl = parseUrl(url) // user-agent must be specified, otherwise some host can return 401 unauthorised - //FIXME hack, the electron typings specifies Protocol with capital but the code actually uses with small case const requestOpts = { protocol: parsedUrl.protocol, hostname: parsedUrl.hostname, @@ -67,13 +68,7 @@ export class ElectronHttpExecutor extends HttpExecutor configurePipes(options, response, destination, callback)) - .catch(callback) + configurePipes(options, response, destination, callback) }) this.addTimeOutHandler(request, callback) request.on("error", callback) diff --git a/packages/electron-builder-http/src/httpExecutor.ts b/packages/electron-builder-http/src/httpExecutor.ts index 44577b943ba..72c29390b99 100644 --- a/packages/electron-builder-http/src/httpExecutor.ts +++ b/packages/electron-builder-http/src/httpExecutor.ts @@ -107,7 +107,7 @@ export function request(url: Url, token: string | null = null, data: {[name: return executorHolder.httpExecutor.request(url, token, data, method, headers) } -export function checkSha2(sha2Header: string | null | undefined, sha2: string | null | undefined, callback: (error: Error | null) => void): boolean { +function checkSha2(sha2Header: string | null | undefined, sha2: string | null | undefined, callback: (error: Error | null) => void): boolean { if (sha2Header != null && sha2 != null) { // todo why bintray doesn't send this header always if (sha2Header == null) { @@ -148,6 +148,10 @@ export function safeGetHeader(response: any, headerKey: string) { } export function configurePipes(options: DownloadOptions, response: any, destination: string, callback: (error: Error | null) => void) { + if (!checkSha2(safeGetHeader(response, "X-Checksum-Sha2"), options.sha2, callback)) { + return + } + const streams: Array = [] if (options.onProgress != null) { const contentLength = safeGetHeader(response, "content-length") diff --git a/packages/electron-builder/src/util/nodeHttpExecutor.ts b/packages/electron-builder/src/util/nodeHttpExecutor.ts index e1fe13a33aa..5da8f00e5da 100644 --- a/packages/electron-builder/src/util/nodeHttpExecutor.ts +++ b/packages/electron-builder/src/util/nodeHttpExecutor.ts @@ -6,27 +6,35 @@ import BluebirdPromise from "bluebird-lst-c" import * as path from "path" import { homedir } from "os" import { parse as parseIni } from "ini" -import { HttpExecutor, DownloadOptions, HttpError, configurePipes, checkSha2, maxRedirects } from "electron-builder-http" +import { HttpExecutor, DownloadOptions, HttpError, configurePipes, maxRedirects } from "electron-builder-http" import { RequestOptions } from "https" import { safeLoad } from "js-yaml" import { parse as parseUrl } from "url" import { debug } from "./util" export class NodeHttpExecutor extends HttpExecutor { - private httpsAgent: Promise | null - - download(url: string, destination: string, options?: DownloadOptions | null): Promise { - return >(this.httpsAgent || (this.httpsAgent = createAgent())) - .then(it => new BluebirdPromise((resolve, reject) => { - this.doDownload(url, destination, 0, options || {}, it, (error: Error) => { - if (error == null) { - resolve(destination) - } - else { - reject(error) - } - }) - })) + private httpsAgentPromise: Promise | null + + async download(url: string, destination: string, options?: DownloadOptions | null): Promise { + if (options == null || !options.skipDirCreation) { + await ensureDir(path.dirname(destination)) + } + + if (this.httpsAgentPromise == null) { + this.httpsAgentPromise = createAgent() + } + + const agent = await this.httpsAgentPromise + return await new BluebirdPromise((resolve, reject) => { + this.doDownload(url, destination, 0, options || {}, agent, (error: Error) => { + if (error == null) { + resolve(destination) + } + else { + reject(error) + } + }) + }) } private addTimeOutHandler(request: ClientRequest, callback: (error: Error) => void) { @@ -39,8 +47,6 @@ export class NodeHttpExecutor extends HttpExecutor void) { - const ensureDirPromise = options.skipDirCreation ? BluebirdPromise.resolve() : ensureDir(path.dirname(destination)) - const parsedUrl = parseUrl(url) // user-agent must be specified, otherwise some host can return 401 unauthorised const request = https.request({ @@ -67,13 +73,7 @@ export class NodeHttpExecutor extends HttpExecutor configurePipes(options, response, destination, callback)) - .catch(callback) + configurePipes(options, response, destination, callback) }) this.addTimeOutHandler(request, callback) request.on("error", callback) diff --git a/test/src/helpers/setVersions.ts b/test/src/helpers/setVersions.ts index 0f0e6230f0b..35ee79902a9 100644 --- a/test/src/helpers/setVersions.ts +++ b/test/src/helpers/setVersions.ts @@ -3,16 +3,43 @@ import { readdir, readJson, writeJson } from "fs-extra-p" import BluebirdPromise from "bluebird-lst-c" const printErrorAndExit = require("../../../packages/electron-builder/out/util/promise").printErrorAndExit -// const exec = require("../../../packages/electron-builder/out/util/util").exec +const exec = require("../../../packages/electron-builder/out/util/util").exec + +const rootDir = path.join(__dirname, "../../..") +const packageDir = path.join(rootDir, "packages") async function main(): Promise { - const rootDir = path.join(__dirname, "../../..") - const packageDir = path.join(rootDir, "packages") const packages = (await readdir(packageDir)).filter(it => !it.includes(".")).sort() - // const versions = await BluebirdPromise.map(packages, it => exec("node", [path.join(rootDir, "test", "vendor", "yarn.js"), "info", "--json", it, "dist-tags"]).then((it: string) => JSON.parse(it).data)) + const packageData = await BluebirdPromise.map(packages, it => readJson(path.join(packageDir, it, "package.json"))) - const versions = packageData.map(it => it.version) + const args = process.argv.slice(2) + if (args.length > 0 && args[0] === "p") { + await setPackageVersions(packages, packageData) + } + else { + await setDepVersions(packages, packageData) + } +} +async function setPackageVersions(packages: Array, packageData: Array) { + const versions = await BluebirdPromise.map(packages, it => exec("node", [path.join(rootDir, "test", "vendor", "yarn.js"), "info", "--json", it, "dist-tags"]).then((it: string) => JSON.parse(it).data)) + for (let i = 0; i < packages.length; i++) { + const packageName = packages[i] + const packageJson = packageData[i] + const versionInfo = versions[i] + const latestVersion = versionInfo.next || versionInfo.latest + if (packageJson.version == latestVersion) { + continue + } + + packageJson.version = latestVersion + console.log(`Set ${packageName} version to ${latestVersion}`) + await writeJson(path.join(packageDir, packageName, "package.json"), packageJson, {spaces: 2}) + } +} + +async function setDepVersions(packages: Array, packageData: Array) { + const versions = packageData.map(it => it.version) for (let version of versions) { if (version == "0.0.0-semantic-release") { throw new Error(`Semantic version 0.0.0-semantic-release is detected, please fix it`) diff --git a/yarn.lock b/yarn.lock index b1eee4b1916..ebd50433b6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1749,8 +1749,8 @@ istanbul-lib-hook@^1.0.0-alpha.4: append-transform "^0.3.0" istanbul-lib-instrument@^1.1.1, istanbul-lib-instrument@^1.3.0, istanbul-lib-instrument@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.4.0.tgz#33da8df669da74e532ba409e0397db0dc56be093" + version "1.4.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.4.1.tgz#9b98c18e327198d24c0bbbf9091fb988aff2d976" dependencies: babel-generator "^6.18.0" babel-template "^6.16.0" @@ -2396,7 +2396,7 @@ mime@^1.2.11: dependencies: brace-expansion "^1.0.0" -minimist@0.0.8, minimist@~0.0.1: +minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" @@ -2404,6 +2404,10 @@ minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"