Skip to content

Commit

Permalink
WIP: NSIS Auto-Update electron-userland#529 — file list
Browse files Browse the repository at this point in the history
  • Loading branch information
develar committed Sep 9, 2016
1 parent 5031116 commit 14ff276
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 63 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ dist/
/typings/main.d.ts
.DS_Store
.idea/shelf/
test/typings/electron-builder.d.ts
/test/typings/electron-builder.d.ts
/test/typings/nsis-updater.d.ts
11 changes: 7 additions & 4 deletions nsis-auto-updater/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
"out"
],
"dependencies": {
"bluebird": "^3.4.1",
"fs-extra-p": "^1.0.6"
"bluebird": "^3.4.6",
"fs-extra-p": "^1.1.8",
"semver": "^5.3.0"
},
"bundledDependencies": [
"fs-extra-p",
"bluebird"
]
"bluebird",
"semver"
],
"typings": "./out/nsis-updater.d.ts"
}
18 changes: 18 additions & 0 deletions nsis-auto-updater/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export interface VersionInfo {
readonly version: string
}
export interface FileInfo {
url: string
}

export interface Provider {
getLatestVersion(): Promise<VersionInfo>

getUpdateFile(versionInfo: VersionInfo): Promise<FileInfo>
}

export interface UpdateCheckResult {
readonly versionInfo: VersionInfo

readonly downloadPromise?: Promise<any> | null
}
112 changes: 75 additions & 37 deletions nsis-auto-updater/src/nsis-updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,23 @@ import { EventEmitter } from "events"
import { spawn } from "child_process"
import * as path from "path"
import { tmpdir } from "os"
import { Promise as BluebirdPromise } from "bluebird"
import { BintrayClient } from "../../src/publish/bintray"
import { BintrayClient, BintrayOptions } from "../../src/publish/bintray"
import { HttpError } from "../../src/publish/restApiRequest"
import semver = require("semver")
import { download } from "../../src/util/httpRequest"
import { Provider, VersionInfo, UpdateCheckResult, FileInfo } from "./api"

//noinspection JSUnusedLocalSymbols
const __awaiter = require("../../src/util/awaiter")

interface VersionInfo {
version: string
}

interface Provider {
checkForUpdates(): Promise<VersionInfo>
}

//noinspection ReservedWordAsName
interface BintraySourceMetadata {
// e.g. develar
readonly user: string
// e.g. onshape-desktop-shell
readonly package: string

// e.g. generic or bin, defaults to generic
readonly repository?: string | null
}

class BintrayProvider {
class BintrayProvider implements Provider {
private client: BintrayClient

constructor(configuration: BintraySourceMetadata) {
this.client = new BintrayClient(configuration.user, configuration.package, configuration.repository || "generic")
constructor(configuration: BintrayOptions) {
this.client = new BintrayClient(configuration.user, configuration.package, configuration.repo)
}

async checkForUpdates(): Promise<VersionInfo> {
async getLatestVersion(): Promise<VersionInfo> {
try {
const data = await this.client.getVersion("_latest")
return {
Expand All @@ -44,44 +27,102 @@ class BintrayProvider {
}
catch (e) {
if (e instanceof HttpError && e.response.statusCode === 404) {
throw new Error(`No latest version, please ensure that user, repository and package correctly configured. Or at least one version is published.${e.stack || e.message}`)
throw new Error(`No latest version, please ensure that user, package and repository correctly configured. Or at least one version is published. ${e.stack || e.message}`)
}
throw e
}
}

async getUpdateFile(versionInfo: VersionInfo): Promise<FileInfo> {
try {
const files = await this.client.getVersionFiles(versionInfo.version)
const suffix = `${versionInfo.version}.exe`
for (let file of files) {
if (file.name.endsWith(suffix) && file.name.includes("Setup")) {
return {
url: ""
}
}
}

//noinspection ExceptionCaughtLocallyJS
throw new Error(`Cannot find suitable file for version ${versionInfo.version} in: ${JSON.stringify(files, null, 2)}`)
}
catch (e) {
if (e instanceof HttpError && e.response.statusCode === 404) {
throw new Error(`No latest version, please ensure that user, package and repository correctly configured. Or at least one version is published. ${e.stack || e.message}`)
}
throw e
}
}
}

class NsisUpdater extends EventEmitter {
export class NsisUpdater extends EventEmitter {
private setupPath = path.join(tmpdir(), 'innobox-upgrade.exe')

private updateAvailable = false
private quitAndInstallCalled = false

private client: Provider

private readonly app: any

constructor(public updateUrl?: string) {
super()

this.app = (<any>global).__test_app || require("electron").app
}

getFeedURL(): string | null | undefined {
return this.updateUrl
}

setFeedURL(value: string | BintraySourceMetadata) {
setFeedURL(value: string | BintrayOptions) {
this.updateUrl = value.toString()

this.client = new BintrayProvider(<BintraySourceMetadata>value)
this.client = new BintrayProvider(<BintrayOptions>value)
}

checkForUpdates(): Promise<any> {
async checkForUpdates(): Promise<UpdateCheckResult> {
if (this.updateUrl == null) {
const message = "Update URL is not set"
this.emitError(message)
return BluebirdPromise.reject(new Error(message))
throw new Error(message)
}

this.emit("checking-for-update")
return this.client.checkForUpdates()
const versionInfo = await this.client.getLatestVersion()

const latestVersion = semver.valid(versionInfo.version)
if (latestVersion == null) {
const error = `Latest version (from update server) is not valid semver version: "${latestVersion}`
this.emitError(error)
throw new Error(error)
}

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

if (semver.gte(currentVersion, latestVersion)) {
this.updateAvailable = false
this.emit("update-not-available")
return {
versionInfo: versionInfo,
}
}

this.updateAvailable = true
this.emit("update-available")

return {
versionInfo: versionInfo,
downloadPromise: this.client.getUpdateFile(versionInfo)
.then(it => {}),
}
}

quitAndInstall(): void {
Expand All @@ -102,14 +143,11 @@ class NsisUpdater extends EventEmitter {
stdio: "ignore",
}).unref()

require("electron").app.quit()
this.app.quit()
}

// emit both error object and message, this is to keep compatibility with old APIs
private emitError (message: string) {
return this.emit("error", new Error(message), message)
}
}

const updater = new NsisUpdater()
export= updater
}
4 changes: 4 additions & 0 deletions nsis-auto-updater/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true
},
"declaration": {
"": "../test/typings/nsis-updater.d.ts"
},
"files": [
"../node_modules/@types/node/index.d.ts",
"../node_modules/@types/semver/index.d.ts",
"../node_modules/fs-extra-p/index.d.ts",
"../node_modules/fs-extra-p/bluebird.d.ts",
"../src/util/httpRequest.ts",
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
"node-gyp-rebuild": "./out/node-gyp-rebuild.js"
},
"scripts": {
"compile": "npm run compile-production && npm run compile-test && npm run compile-updater",
"compile": "npm run compile-production && npm run compile-updater && npm run compile-test",
"compile-production": "ts-babel",
"compile-test": "ts-babel test",
"compile-updater": "tsc -p nsis-auto-updater",
"compile-updater": "ts-babel nsis-auto-updater",
"lint": "tslint 'src/**/*.ts' 'test/src/**/*.ts'",
"pretest": "npm run compile && npm run lint",
"test": "node ./test/out/helpers/runTests.js",
Expand Down Expand Up @@ -121,7 +121,7 @@
"json8": "^0.9.2",
"path-sort": "^0.1.0",
"pre-git": "^3.10.0",
"ts-babel": "^1.0.4",
"ts-babel": "^1.0.6",
"tslint": "^3.15.1",
"typescript": "^2.0.2",
"whitespace": "^2.1.0"
Expand Down
8 changes: 5 additions & 3 deletions src/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import { log, warn } from "./util/log"
import { Platform, Arch, archFromString } from "./metadata"
import { getRepositoryInfo } from "./repositoryInfo"
import { DIR_TARGET } from "./targets/targetFactory"
import { BintrayPublisher, BintrayConfiguration } from "./publish/BintrayPublisher"
import { BintrayPublisher } from "./publish/BintrayPublisher"
import { BintrayOptions } from "./publish/bintray"

//noinspection JSUnusedLocalSymbols
const __awaiter = require("./util/awaiter")
Expand Down Expand Up @@ -309,8 +310,9 @@ export async function createPublisher(packager: Packager, options: PublishOption
}
if (publisherName === "bintray") {
const version = packager.metadata.version!
const bintrayInfo: BintrayConfiguration = {user: info.user, packageName: info.project, repo: "generic"}
log(`Creating Bintray Publisher — user: ${bintrayInfo.user}, package: ${bintrayInfo.packageName}, repository: ${bintrayInfo.repo}, version: ${version}`)
//noinspection ReservedWordAsName
const bintrayInfo: BintrayOptions = {user: info.user, package: info.project, repo: "generic"}
log(`Creating Bintray Publisher — user: ${bintrayInfo.user}, package: ${bintrayInfo.package}, repository: ${bintrayInfo.repo}, version: ${version}`)
return new BintrayPublisher(bintrayInfo, version, options)
}
return null
Expand Down
12 changes: 3 additions & 9 deletions src/publish/BintrayPublisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,18 @@ import { log } from "../util/log"
import { debug } from "../util/util"
import { basename } from "path"
import { stat } from "fs-extra-p"
import { BintrayClient, Version } from "./bintray"
import { BintrayClient, Version, BintrayOptions } from "./bintray"

//noinspection JSUnusedLocalSymbols
const __awaiter = require("../util/awaiter")

export interface BintrayConfiguration {
readonly user: string
readonly packageName: string
readonly repo?: string
}

export class BintrayPublisher implements Publisher {
private _versionPromise: BluebirdPromise<Version>

private readonly client: BintrayClient

constructor(private info: BintrayConfiguration, private version: string, private options: PublishOptions) {
this.client = new BintrayClient(info.user, info.packageName, info.repo || "generic", options.bintrayToken)
constructor(private info: BintrayOptions, private version: string, private options: PublishOptions) {
this.client = new BintrayClient(info.user, info.package, info.repo, options.bintrayToken)
this._versionPromise = <BluebirdPromise<Version>>this.init()
}

Expand Down
25 changes: 23 additions & 2 deletions src/publish/bintray.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
import { bintrayRequest } from "./restApiRequest"

//noinspection ReservedWordAsName
export interface Version {
readonly name: string
//noinspection ReservedWordAsName
readonly package: string
}

export interface File {
name: string
path: string

sha1: string
sha256: string
}

export interface BintrayOptions {
readonly user: string
//noinspection ReservedWordAsName
readonly package: string
readonly repo?: string
}

export class BintrayClient {
private readonly basePath: string
readonly auth: string | null
readonly repo: string

constructor(public user: string, public packageName: string, public repo: string = "generic", apiKey?: string | null) {
constructor(public user: string, public packageName: string, repo?: string, apiKey?: string | null) {
this.repo = repo || "generic"
this.auth = apiKey == null ? null : `Basic ${new Buffer(`${user}:${apiKey}`).toString("base64")}`
this.basePath = `/packages/${this.user}/${this.repo}/${this.packageName}`
}
Expand All @@ -19,6 +36,10 @@ export class BintrayClient {
return bintrayRequest<Version>(`${this.basePath}/versions/${version}`, this.auth)
}

getVersionFiles(version: string): Promise<Array<File>> {
return bintrayRequest<Array<File>>(`${this.basePath}/versions/${version}/files`, this.auth)
}

createVersion(version: string): Promise<any> {
return bintrayRequest<Version>(`${this.basePath}/versions`, this.auth, {
name: version,
Expand Down
2 changes: 1 addition & 1 deletion test/src/ArtifactPublisherTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function testAndIgnoreApiRate(name: string, testFunction: () => Promise<any>) {
test("Bintray upload", async () => {
const version = versionNumber()
//noinspection SpellCheckingInspection
const publisher = new BintrayPublisher({user: "actperepo", packageName: "test", repo: "generic"}, version, {bintrayToken: "5df2cadec86dff91392e4c419540785813c3db15"})
const publisher = new BintrayPublisher({user: "actperepo", package: "test", repo: "generic"}, version, {bintrayToken: "5df2cadec86dff91392e4c419540785813c3db15"})
try {
const artifactName = `icon-${version}.icns`
await publisher.upload(iconPath, artifactName)
Expand Down
4 changes: 4 additions & 0 deletions test/src/helpers/fileAssert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ class Assertions {
compare(this.actual, "", true)
}

isNotNull() {
compare(this.actual, null, true)
}

doesNotMatch(pattern: RegExp) {
if ((<string>this.actual).match(pattern)) {
throw new Error(`${this.actual} matches ${pattern}`)
Expand Down
Loading

0 comments on commit 14ff276

Please sign in to comment.