Skip to content

Commit

Permalink
feat(electron-updater): Make it possible to "auto-downgrade" the appl…
Browse files Browse the repository at this point in the history
…ication on channel change

Close #1149
  • Loading branch information
develar committed Apr 16, 2017
1 parent 4351b56 commit a3c4a9e
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 39 deletions.
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ platform:

cache:
- node_modules
- '%USERPROFILE%\.electron'
- '%LOCALAPPDATA%\electron\Cache'

environment:
TEST_FILES: ExtraBuildTest,BuildTest,extraMetadataTest,filesTest,globTest,nsisUpdaterTest,oneClickInstallerTest,installerTest
Expand Down
2 changes: 1 addition & 1 deletion circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ machine:

dependencies:
cache_directories:
- "~/.electron"
- "~/.cache/.electron"
- "~/.cache/electron-builder"

# https://discuss.circleci.com/t/installing-git-lfs/867
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
"yargs": "^7.1.0"
},
"devDependencies": {
"@develar/typescript-json-schema": "0.11.0",
"@types/electron": "^1.4.35",
"@types/ini": "^1.3.29",
"@types/jest": "^19.2.2",
Expand All @@ -81,6 +80,7 @@
"convert-source-map": "^1.5.0",
"decompress-zip": "^0.3.0",
"depcheck": "^0.6.7",
"develar-typescript-json-schema": "0.11.0",
"globby": "^6.1.0",
"jest-cli": "^19.0.2",
"jest-environment-node-debug": "^2.0.0",
Expand Down
45 changes: 32 additions & 13 deletions packages/electron-updater/src/AppUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { EventEmitter } from "events"
import { readFile } from "fs-extra-p"
import { safeLoad } from "js-yaml"
import * as path from "path"
import { gt as isVersionGreaterThan, valid as parseVersion } from "semver"
import { eq as isVersionsEqual, gt as isVersionGreaterThan, prerelease as getVersionPreleaseComponents, valid as parseVersion } from "semver"
import "source-map-support/register"
import { FileInfo, Provider, UpdateCheckResult, UpdaterSignal } from "./api"
import { BintrayProvider } from "./BintrayProvider"
Expand All @@ -25,10 +25,23 @@ export interface Logger {

export abstract class AppUpdater extends EventEmitter {
/**
* Automatically download an update when it is found.
* Whether to automatically download an update when it is found.
*/
autoDownload = true

/**
* *GitHub provider only.* Whether to allow update to pre-release versions. Defaults to `true` if application version contains prerelease components (e.g. `0.12.1-alpha.1`, here `alpha` is a prerelease component), otherwise `false`.
*
* If `true`, downgrade will be allowed (`allowDowngrade` will be set to `true`).
*/
allowPrerelease = false

/**
* Whether to allow version downgrade (when a user from the beta channel wants to go back to the stable channel).
* Defaults to `true` if application version contains prerelease components (e.g. `0.12.1-alpha.1`, here `alpha` is a prerelease component), otherwise `false`.
*/
allowDowngrade = false

/**
* The request headers.
*/
Expand Down Expand Up @@ -64,7 +77,9 @@ export abstract class AppUpdater extends EventEmitter {
protected versionInfo: VersionInfo | null
private fileInfo: FileInfo | null

constructor(options: PublishConfiguration | null | undefined) {
private currentVersion: string

constructor(options: PublishConfiguration | null | undefined, app?: any) {
super()

this.on("error", (error: Error) => {
Expand All @@ -73,8 +88,8 @@ export abstract class AppUpdater extends EventEmitter {
}
})

if ((<any>global).__test_app != null) {
this.app = (<any>global).__test_app
if (app != null || (<any>global).__test_app != null) {
this.app = app || (<any>global).__test_app
this.untilAppReady = BluebirdPromise.resolve()
}
else {
Expand All @@ -96,6 +111,16 @@ export abstract class AppUpdater extends EventEmitter {
})
}

const currentVersionString = this.app.getVersion()
this.currentVersion = parseVersion(currentVersionString)
if (this.currentVersion == null) {
throw new Error(`App version is not valid semver version: "${currentVersionString}`)
}

const versionPrereleaseComponent = getVersionPreleaseComponents(this.currentVersion)
this.allowDowngrade = versionPrereleaseComponent != null && versionPrereleaseComponent.length > 0
this.allowPrerelease = this.allowDowngrade

if (options != null) {
this.setFeedURL(options)
}
Expand Down Expand Up @@ -171,16 +196,10 @@ export abstract class AppUpdater extends EventEmitter {
throw new Error(`Latest version (from update server) is not valid semver version: "${latestVersion}`)
}

const currentVersionString = this.app.getVersion()
const currentVersion = parseVersion(currentVersionString)
if (currentVersion == null) {
throw new Error(`App version is not valid semver version: "${currentVersion}`)
}

if (!isVersionGreaterThan(latestVersion, currentVersion)) {
if (this.allowDowngrade ? isVersionsEqual(latestVersion, this.currentVersion) : !isVersionGreaterThan(latestVersion, this.currentVersion)) {
this.updateAvailable = false
if (this.logger != null) {
this.logger.info(`Update for version ${currentVersionString} is not available (latest version: ${versionInfo.version})`)
this.logger.info(`Update for version ${this.currentVersion} is not available (latest version: ${versionInfo.version}, downgrade is ${this.allowDowngrade ? "allowed" : "disallowed"}.`)
}
this.emit("update-not-available", versionInfo)
return {
Expand Down
4 changes: 2 additions & 2 deletions packages/electron-updater/src/NsisUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export class NsisUpdater extends AppUpdater {
private quitAndInstallCalled = false
private quitHandlerAdded = false

constructor(options?: PublishConfiguration) {
super(options)
constructor(options?: PublishConfiguration, app?: any) {
super(options, app)
}

/**
Expand Down
10 changes: 10 additions & 0 deletions test/out/__snapshots__/nsisUpdaterTest.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ Array [
]
`;

exports[`downgrade (allowed) 1`] = `
Object {
"name": "TestApp Setup 1.1.0.exe",
"sha2": "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b",
"url": "https://dl.bintray.com/actperepo/generic/TestApp Setup 1.1.0.exe",
}
`;

exports[`downgrade (disallowed) 1`] = `undefined`;

exports[`file url 1`] = `
Object {
"name": "TestApp Setup 1.1.0.exe",
Expand Down
63 changes: 51 additions & 12 deletions test/src/nsisUpdaterTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,8 @@ test("check updates - no versions at all", async () => {
await assertThat(updater.checkForUpdates()).throws()
})

// test("cannot find suitable file for version", async () => {
// const updater = new NsisUpdater(<BintrayOptions>{
// provider: "bintray",
// owner: "actperepo",
// package: "incorrect-file-version",
// })
//
// await assertThat(updater.checkForUpdates()).throws()
// })

test("file url", async () => {
const updater = new NsisUpdater()
async function testUpdateFromBintray(app: any) {
const updater = new NsisUpdater(null, app)
updater.updateConfigPath = await writeUpdateConfig(<BintrayOptions>{
provider: "bintray",
owner: "actperepo",
Expand All @@ -71,9 +61,58 @@ test("file url", async () => {
expect(updateCheckResult.fileInfo).toMatchSnapshot()
await assertThat(path.join(await updateCheckResult.downloadPromise)).isFile()

expect(actualEvents).toEqual(expectedEvents)
}
test("file url", () => testUpdateFromBintray(null))

test("downgrade (disallowed)", async () => {
const updater = new NsisUpdater(null, {
getVersion: function () {
return "2.0.0"
},

getAppPath: function () {
},

on: function () {
// ignored
},
}
)
updater.updateConfigPath = await writeUpdateConfig(<BintrayOptions>{
provider: "bintray",
owner: "actperepo",
package: "TestApp",
})

const actualEvents: Array<string> = []
const expectedEvents = ["checking-for-update", "update-not-available"]
for (const eventName of expectedEvents) {
updater.addListener(eventName, () => {
actualEvents.push(eventName)
})
}

const updateCheckResult = await updater.checkForUpdates()
expect(updateCheckResult.fileInfo).toMatchSnapshot()
expect(updateCheckResult.downloadPromise).toBeUndefined()

expect(actualEvents).toEqual(expectedEvents)
})

test("downgrade (allowed)", () => testUpdateFromBintray({
getVersion: function () {
return "2.0.0-beta.1"
},

getAppPath: function () {
},

on: function () {
// ignored
},
}))

test("file url generic", async () => {
const updater = new NsisUpdater()
updater.updateConfigPath = await writeUpdateConfig(<GenericServerOptions>{
Expand Down
18 changes: 9 additions & 9 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,6 @@
"7zip-bin-mac" "^1.0.1"
"7zip-bin-win" "^2.0.2"

"@develar/[email protected]":
version "0.11.0"
resolved "https://registry.yarnpkg.com/@develar/typescript-json-schema/-/typescript-json-schema-0.11.0.tgz#a1ac659a9b1cecdfed0e7e972d0404d7978fd8d7"
dependencies:
glob "~7.1.1"
json-stable-stringify "^1.0.1"
typescript "~2.1.5"
yargs "^7.0.2"

"@types/electron@^1.4.35":
version "1.4.35"
resolved "https://registry.yarnpkg.com/@types/electron/-/electron-1.4.35.tgz#48e5e6ef0b49f27b9f78b87d80f796e3f0a4f33c"
Expand Down Expand Up @@ -1020,6 +1011,15 @@ detect-newline@^1.0.3:
get-stdin "^4.0.1"
minimist "^1.1.0"

[email protected]:
version "0.11.0"
resolved "https://registry.yarnpkg.com/develar-typescript-json-schema/-/develar-typescript-json-schema-0.11.0.tgz#d73580cae2fb0b8f9a7cee3d10aff6caae751cca"
dependencies:
glob "~7.1.1"
json-stable-stringify "^1.0.1"
typescript "~2.1.5"
yargs "^7.0.2"

diff@^3.0.0, diff@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
Expand Down

0 comments on commit a3c4a9e

Please sign in to comment.