Skip to content

Commit

Permalink
feat(electron-updater): GitHub: Allow pre-release builds to be auto u…
Browse files Browse the repository at this point in the history
…pdated

Close #1391
  • Loading branch information
develar committed Apr 16, 2017
1 parent 257a7dd commit f275831
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 39 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"tunnel-agent": "^0.6.0",
"update-notifier": "^2.1.0",
"uuid-1345": "^0.99.6",
"xelement": "^1.0.13",
"yargs": "^7.1.0"
},
"devDependencies": {
Expand Down
3 changes: 2 additions & 1 deletion packages/electron-updater/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"semver": "^5.3.0",
"source-map-support": "^0.4.14",
"electron-builder-http": "~0.0.0-semantic-release",
"electron-is-dev": "^0.1.2"
"electron-is-dev": "^0.1.2",
"xelement": "^1.0.13"
},
"typings": "./out/electron-updater.d.ts"
}
64 changes: 32 additions & 32 deletions packages/electron-updater/src/AppUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export abstract class AppUpdater extends EventEmitter {
client = new GenericProvider({provider: "generic", url: options})
}
else {
client = createClient(options)
client = this.createClient(options)
}
this.clientPromise = BluebirdPromise.resolve(client)
}
Expand Down Expand Up @@ -184,7 +184,7 @@ export abstract class AppUpdater extends EventEmitter {

private async doCheckForUpdates(): Promise<UpdateCheckResult> {
if (this.clientPromise == null) {
this.clientPromise = this.loadUpdateConfig().then(it => createClient(it))
this.clientPromise = this.loadUpdateConfig().then(it => this.createClient(it))
}

const client = await this.clientPromise
Expand Down Expand Up @@ -288,41 +288,41 @@ export abstract class AppUpdater extends EventEmitter {
}
return requestHeaders
}
}

function createClient(data: string | PublishConfiguration) {
if (typeof data === "string") {
throw new Error("Please pass PublishConfiguration object")
}
private createClient(data: string | PublishConfiguration) {
if (typeof data === "string") {
throw new Error("Please pass PublishConfiguration object")
}

const provider = (<PublishConfiguration>data).provider
switch (provider) {
case "github":
const githubOptions = <GithubOptions>data
const token = (githubOptions.private ? process.env.GH_TOKEN : null) || githubOptions.token
if (token == null) {
return new GitHubProvider(githubOptions)
}
else {
return new PrivateGitHubProvider(githubOptions, token)
const provider = (<PublishConfiguration>data).provider
switch (provider) {
case "github":
const githubOptions = <GithubOptions>data
const token = (githubOptions.private ? process.env.GH_TOKEN : null) || githubOptions.token
if (token == null) {
return new GitHubProvider(githubOptions, this)
}
else {
return new PrivateGitHubProvider(githubOptions, token)
}

case "s3": {
const s3 = <S3Options>data
return new GenericProvider({
provider: "generic",
url: s3Url(s3),
channel: s3.channel || ""
})
}

case "s3": {
const s3 = <S3Options>data
return new GenericProvider({
provider: "generic",
url: s3Url(s3),
channel: s3.channel || ""
})
}

case "generic":
return new GenericProvider(<GenericServerOptions>data)
case "generic":
return new GenericProvider(<GenericServerOptions>data)

case "bintray":
return new BintrayProvider(<BintrayOptions>data)
case "bintray":
return new BintrayProvider(<BintrayOptions>data)

default:
throw new Error(`Unsupported provider: ${provider}`)
default:
throw new Error(`Unsupported provider: ${provider}`)
}
}
}
45 changes: 40 additions & 5 deletions packages/electron-updater/src/GitHubProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { RequestOptions } from "http"
import * as path from "path"
import { parse as parseUrl } from "url"
import { FileInfo, formatUrl, getChannelFilename, getCurrentPlatform, getDefaultChannelName, Provider } from "./api"
import { AppUpdater } from "./AppUpdater"
import { validateUpdateInfo } from "./GenericProvider"

export abstract class BaseGitHubProvider<T extends UpdateInfo> extends Provider<T> {
Expand All @@ -24,23 +25,50 @@ export abstract class BaseGitHubProvider<T extends UpdateInfo> extends Provider<
}

export class GitHubProvider extends BaseGitHubProvider<UpdateInfo> {
constructor(protected readonly options: GithubOptions) {
constructor(protected readonly options: GithubOptions, private readonly updater: AppUpdater) {
super(options, "github.com")
}

async getLatestVersion(): Promise<UpdateInfo> {
const basePath = this.basePath
const cancellationToken = new CancellationToken()
const version = await this.getLatestVersionString(basePath, cancellationToken)

const xElement = require("xelement")
const feedXml = await request(Object.assign({
path: `${basePath}.atom`,
headers: Object.assign({}, this.requestHeaders, {Accept: "application/xml"})
}, this.baseUrl), cancellationToken)

const feed = new xElement.Parse(feedXml)
const latestRelease = feed.element("entry")
if (latestRelease == null) {
throw new Error(`No published versions on GitHub`)
}

let version: string
try {
if (this.updater.allowPrerelease) {
version = latestRelease.element("link").getAttr("href").match(/\/tag\/v?([^\/]+)$/)[1]
}
else {
version = await this.getLatestVersionString(basePath, cancellationToken)
}
}
catch (e) {
throw new Error(`Cannot parse releases feed: ${e.stack || e.message},\nXML:\n${feedXml}`)
}

let result: any
const channelFile = getChannelFilename(getDefaultChannelName())
const requestOptions = Object.assign({path: this.getBaseDownloadPath(version, channelFile), headers: this.requestHeaders || undefined}, this.baseUrl)
try {
result = await request<UpdateInfo>(requestOptions, cancellationToken)
}
catch (e) {
if (e instanceof HttpError && e.response.statusCode === 404) {
throw new Error(`Cannot find ${channelFile} in the latest release artifacts (${formatUrl(<any>requestOptions)}): ${e.stack || e.message}`)
if (!this.updater.allowPrerelease) {
if (e instanceof HttpError && e.response.statusCode === 404) {
throw new Error(`Cannot find ${channelFile} in the latest release artifacts (${formatUrl(<any>requestOptions)}): ${e.stack || e.message}`)
}
}
throw e
}
Expand All @@ -49,13 +77,20 @@ export class GitHubProvider extends BaseGitHubProvider<UpdateInfo> {
if (getCurrentPlatform() === "darwin") {
result.releaseJsonUrl = `${githubUrl(this.options)}/${requestOptions.path}`
}

if (result.releaseName == null) {
result.releaseName = latestRelease.getElementValue("title")
}
if (result.releaseNotes == null) {
result.releaseNotes = latestRelease.getElementValue("content")
}
return result
}

private async getLatestVersionString(basePath: string, cancellationToken: CancellationToken): Promise<string> {
const requestOptions: RequestOptions = Object.assign({
path: `${basePath}/latest`,
headers: Object.assign({Accept: "application/json"}, this.requestHeaders)
headers: Object.assign({}, this.requestHeaders, {Accept: "application/json"})
}, this.baseUrl)
try {
// do not use API to avoid limit
Expand Down
18 changes: 18 additions & 0 deletions test/out/__snapshots__/nsisUpdaterTest.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,24 @@ Object {
}
`;

exports[`file url github pre-release 1`] = `
Object {
"name": "TestApp-Setup-1.1.0.exe",
"sha2": "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2",
"url": "https://github.com/develar/__test_nsis_release/releases/download/v1.5.0/TestApp-Setup-1.1.0.exe",
}
`;

exports[`file url github pre-release 2`] = `
Object {
"path": "TestApp Setup 1.1.0.exe",
"releaseName": "1.5.0",
"releaseNotes": "<p>Test pre-release. qefr</p>",
"sha2": "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2",
"version": "1.5.0",
}
`;

exports[`file url github private 1`] = `
Object {
"headers": Object {
Expand Down
38 changes: 37 additions & 1 deletion test/src/nsisUpdaterTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ test("file url github", async () => {
updater.updateConfigPath = await writeUpdateConfig(<GithubOptions>{
provider: "github",
owner: "develar",
repo: "__test_nsis_release",
repo: "__test_nsis_release",
})

const actualEvents: Array<string> = []
Expand All @@ -209,6 +209,42 @@ test("file url github", async () => {
expect(actualEvents).toEqual(expectedEvents)
})

test("file url github pre-release", async () => {
const updater = new NsisUpdater(null, {
getVersion: function () {
return "1.6.0-beta.1"
},

getAppPath: function () {
},

on: function () {
// ignored
},
})
updater.updateConfigPath = await writeUpdateConfig(<GithubOptions>{
provider: "github",
owner: "develar",
repo: "__test_nsis_release",
})

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

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

await assertThat(path.join(await updateCheckResult.downloadPromise)).isFile()

expect(actualEvents).toEqual(expectedEvents)
})

test.skip("file url github private", async () => {
const updater = new NsisUpdater()
updater.updateConfigPath = await writeUpdateConfig(<GithubOptions>{
Expand Down
6 changes: 6 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3481,6 +3481,12 @@ xdg-basedir@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"

xelement@^1.0.13:
version "1.0.13"
resolved "https://registry.yarnpkg.com/xelement/-/xelement-1.0.13.tgz#36b724d1e6489d6aae285b06995ae5e33cec10b7"
dependencies:
sax "^1.2.1"

xml-name-validator@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"
Expand Down

0 comments on commit f275831

Please sign in to comment.