Skip to content

Commit

Permalink
WIP(updater): support private github repo, nuts and so on on macOS
Browse files Browse the repository at this point in the history
  • Loading branch information
develar committed May 31, 2017
1 parent 81fd398 commit 62303d7
Show file tree
Hide file tree
Showing 20 changed files with 107 additions and 62 deletions.
11 changes: 11 additions & 0 deletions docs/Auto Update.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ Emitted on progress. Only supported over Windows build, since `Squirrel.Mac` [do
* [`.getLatestVersion()`](#module_electron-updater.Provider+getLatestVersion) ⇒ <code>Promise&lt;module:electron-updater.T&gt;</code>
* [`.setRequestHeaders(value)`](#module_electron-updater.Provider+setRequestHeaders)
* [`.getUpdateFile(versionInfo)`](#module_electron-updater.Provider+getUpdateFile) ⇒ <code>Promise&lt;[FileInfo](#FileInfo)&gt;</code>
* [`.validateUpdateInfo(info)`](#module_electron-updater.Provider+validateUpdateInfo)
* [.UpdaterSignal](#UpdaterSignal)
* [`.login(handler)`](#module_electron-updater.UpdaterSignal+login)
* [`.progress(handler)`](#module_electron-updater.UpdaterSignal+progress)
Expand Down Expand Up @@ -335,6 +336,7 @@ This is different from the normal quit event sequence.
* [`.getLatestVersion()`](#module_electron-updater.Provider+getLatestVersion) ⇒ <code>Promise&lt;module:electron-updater.T&gt;</code>
* [`.setRequestHeaders(value)`](#module_electron-updater.Provider+setRequestHeaders)
* [`.getUpdateFile(versionInfo)`](#module_electron-updater.Provider+getUpdateFile) ⇒ <code>Promise&lt;[FileInfo](#FileInfo)&gt;</code>
* [`.validateUpdateInfo(info)`](#module_electron-updater.Provider+validateUpdateInfo)

<a name="module_electron-updater.Provider+getLatestVersion"></a>

Expand All @@ -358,6 +360,15 @@ This is different from the normal quit event sequence.
| --- | --- |
| versionInfo | <code>module:electron-updater.T</code> |

<a name="module_electron-updater.Provider+validateUpdateInfo"></a>

#### `provider.validateUpdateInfo(info)`
**Kind**: instance method of [<code>Provider</code>](#Provider)

| Param | Type |
| --- | --- |
| info | <code>[UpdateInfo](Publishing-Artifacts#UpdateInfo)</code> |

<a name="UpdaterSignal"></a>

### UpdaterSignal
Expand Down
3 changes: 2 additions & 1 deletion docs/Publishing Artifacts.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ Or in the [~/.aws/credentials](http://docs.aws.amazon.com/sdk-for-javascript/v2/
| --- | --- | --- |
| **path**| <code>string</code> | <a name="UpdateInfo-path"></a> |
| githubArtifactName| <code>string</code> \| <code>null</code> | <a name="UpdateInfo-githubArtifactName"></a> |
| **sha2**| <code>string</code> | <a name="UpdateInfo-sha2"></a> |
| sha2| <code>string</code> | <a name="UpdateInfo-sha2"></a> |
| sha512| <code>string</code> | <a name="UpdateInfo-sha512"></a> |
| releaseName| <code>string</code> \| <code>null</code> | <a name="UpdateInfo-releaseName"></a>The release name. |
| releaseNotes| <code>string</code> \| <code>null</code> | <a name="UpdateInfo-releaseNotes"></a>The release notes. |
| **releaseDate**| <code>string</code> | <a name="UpdateInfo-releaseDate"></a>The release date. |
Expand Down
5 changes: 3 additions & 2 deletions docs/api/electron-builder-util.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ File permission is fixed — allow execute for all if owner can, allow read for
* [`.prepareArgs(args, exePath)`](#module_electron-builder-util.prepareArgs) ⇒ <code>Array&lt;string&gt;</code>
* [`.removePassword(input)`](#module_electron-builder-util.removePassword) ⇒ <code>string</code>
* [`.replaceDefault(inList, defaultList)`](#module_electron-builder-util.replaceDefault) ⇒ <code>Array&lt;string&gt;</code>
* [`.safeStringifyJson(data)`](#module_electron-builder-util.safeStringifyJson) ⇒ <code>string</code>
* [`.safeStringifyJson(data, skippedNames)`](#module_electron-builder-util.safeStringifyJson) ⇒ <code>string</code>
* [`.smarten(s)`](#module_electron-builder-util.smarten) ⇒ <code>string</code>
* [`.spawn(command, args, options)`](#module_electron-builder-util.spawn) ⇒ <code>Promise&lt;any&gt;</code>
* [`.use(value, task)`](#module_electron-builder-util.use) ⇒ <code>null</code> \| <code>module:electron-builder-util.R</code>
Expand Down Expand Up @@ -598,12 +598,13 @@ File permission is fixed — allow execute for all if owner can, allow read for

<a name="module_electron-builder-util.safeStringifyJson"></a>

### `electron-builder-util.safeStringifyJson(data)` ⇒ <code>string</code>
### `electron-builder-util.safeStringifyJson(data, skippedNames)` ⇒ <code>string</code>
**Kind**: method of [<code>electron-builder-util</code>](#module_electron-builder-util)

| Param | Type |
| --- | --- |
| data | <code>any</code> |
| skippedNames | <code>Set&lt;string&gt;</code> |

<a name="module_electron-builder-util.smarten"></a>

Expand Down
10 changes: 0 additions & 10 deletions docs/api/electron-updater.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ Developer API only. See [[Auto Update]] for user documentation.
* [.GenericProvider](#GenericProvider) ⇐ <code>[Provider](Auto-Update#Provider)</code>
* [`.getLatestVersion()`](#module_electron-updater/out/GenericProvider.GenericProvider+getLatestVersion) ⇒ <code>Promise&lt;[UpdateInfo](Publishing-Artifacts#UpdateInfo)&gt;</code>
* [`.getUpdateFile(versionInfo)`](#module_electron-updater/out/GenericProvider.GenericProvider+getUpdateFile) ⇒ <code>Promise&lt;[FileInfo](Auto-Update#FileInfo)&gt;</code>
* [`.validateUpdateInfo(info)`](#module_electron-updater/out/GenericProvider.validateUpdateInfo)

<a name="GenericProvider"></a>

Expand All @@ -140,15 +139,6 @@ Developer API only. See [[Auto Update]] for user documentation.
| --- | --- |
| versionInfo | <code>[UpdateInfo](Publishing-Artifacts#UpdateInfo)</code> |

<a name="module_electron-updater/out/GenericProvider.validateUpdateInfo"></a>

### `electron-updater/out/GenericProvider.validateUpdateInfo(info)`
**Kind**: method of [<code>electron-updater/out/GenericProvider</code>](#module_electron-updater/out/GenericProvider)

| Param | Type |
| --- | --- |
| info | <code>[UpdateInfo](Publishing-Artifacts#UpdateInfo)</code> |

<a name="module_electron-updater/out/GitHubProvider"></a>

## electron-updater/out/GitHubProvider
Expand Down
16 changes: 11 additions & 5 deletions packages/electron-builder-http/src/httpExecutor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createHash } from "crypto"
import { createHash, Hash } from "crypto"
import _debug from "debug"
import { EventEmitter } from "events"
import { createWriteStream } from "fs-extra-p"
Expand Down Expand Up @@ -28,6 +28,7 @@ export interface DownloadOptions {
readonly headers?: RequestHeaders | null
readonly skipDirCreation?: boolean
readonly sha2?: string | null
readonly sha512?: string | null

readonly cancellationToken: CancellationToken

Expand Down Expand Up @@ -188,10 +189,12 @@ export abstract class HttpExecutor<REQUEST> {
}

class DigestTransform extends Transform {
private readonly digester = createHash("sha256")
private readonly digester: Hash

constructor(private expected: string) {
constructor(private expected: string, algorithm: string) {
super()

this.digester = createHash(algorithm)
}

_transform(chunk: any, encoding: string, callback: Function) {
Expand Down Expand Up @@ -251,8 +254,11 @@ function configurePipes(options: DownloadOptions, response: any, destination: st
}
}

if (options.sha2 != null) {
streams.push(new DigestTransform(options.sha2))
if (options.sha512 != null) {
streams.push(new DigestTransform(options.sha512, "sha512"))
}
else if (options.sha2 != null) {
streams.push(new DigestTransform(options.sha2, "sha256"))
}

const fileOut = createWriteStream(destination)
Expand Down
6 changes: 5 additions & 1 deletion packages/electron-builder-http/src/publishOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,11 @@ export interface VersionInfo {
export interface UpdateInfo extends VersionInfo {
readonly path: string
readonly githubArtifactName?: string | null
readonly sha2: string

// deprecated
readonly sha2?: string

readonly sha512?: string

/**
* The release name.
Expand Down
4 changes: 2 additions & 2 deletions packages/electron-builder-util/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,9 @@ export function isPullRequest() {
return isSet(process.env.TRAVIS_PULL_REQUEST) || isSet(process.env.CI_PULL_REQUEST) || isSet(process.env.CI_PULL_REQUESTS)
}

export function safeStringifyJson(data: any) {
export function safeStringifyJson(data: any, skippedNames?: Set<string>) {
return JSON.stringify(data, (name, value) => {
if (name.endsWith("Password") || name.endsWith("Token") || name.includes("password") || name.includes("token")) {
if (name.endsWith("Password") || name.endsWith("Token") || name.includes("password") || name.includes("token") || (skippedNames != null && skippedNames.has(name))) {
return "<stripped sensitive data>"
}
return value
Expand Down
28 changes: 18 additions & 10 deletions packages/electron-builder/src/publish/PublishManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createHash } from "crypto"
import { Arch, Platform, PlatformSpecificBuildOptions, Target } from "electron-builder-core"
import { CancellationToken } from "electron-builder-http/out/CancellationToken"
import { BintrayOptions, GenericServerOptions, GithubOptions, githubUrl, PublishConfiguration, PublishProvider, S3Options, s3Url, UpdateInfo, VersionInfo } from "electron-builder-http/out/publishOptions"
import { asArray, debug, isEmptyOrSpaces, isPullRequest, safeStringifyJson } from "electron-builder-util"
import { asArray, debug, isEmptyOrSpaces, isPullRequest, Lazy, safeStringifyJson } from "electron-builder-util"
import { log, warn } from "electron-builder-util/out/log"
import { throwError } from "electron-builder-util/out/promise"
import { HttpPublisher, PublishContext, Publisher, PublishOptions } from "electron-publish"
Expand Down Expand Up @@ -112,7 +112,7 @@ export class PublishManager implements PublishContext {
const publishConfigs = event.publishConfig == null ? await getPublishConfigs(packager, target == null ? null : target.options, event.arch) : [event.publishConfig]

if (debug.enabled) {
debug(`artifactCreated: ${safeStringifyJson(event)}, publishConfigs: ${safeStringifyJson(publishConfigs)}`)
debug(`artifactCreated: ${safeStringifyJson(event, new Set(["packager"]))}, publishConfigs: ${safeStringifyJson(publishConfigs)}`)
}

const eventFile = event.file
Expand Down Expand Up @@ -238,6 +238,9 @@ async function writeUpdateInfo(event: ArtifactCreated, _publishConfigs: Array<Pu
}

const version = packager.appInfo.version
let sha2 = new Lazy(() => hash(event.file!, "sha256"))
let sha512 = new Lazy(() => hash(event.file!, "sha512"))
const isMac = packager.platform === Platform.MAC

for (const publishConfig of publishConfigs) {
if (publishConfig.provider === "bintray") {
Expand All @@ -247,7 +250,7 @@ async function writeUpdateInfo(event: ArtifactCreated, _publishConfigs: Array<Pu
const channel = (<GenericServerOptions>publishConfig).channel || "latest"
const createdFiles = new Set<string>()

if (packager.platform === Platform.MAC) {
if (isMac) {
const isGitHub = publishConfig.provider === "github"
// backward compatibility - write json file
const updateInfoFile = isGitHub ? path.join(outDir, "github", `${channel}-mac.json`) : path.join(outDir, `${channel}-mac.json`)
Expand All @@ -266,17 +269,22 @@ async function writeUpdateInfo(event: ArtifactCreated, _publishConfigs: Array<Pu
})
}

const sha2 = await sha256(event.file!)
const updateInfoFile = path.join(outDir, `${channel}${packager.platform === Platform.MAC ? "-mac" : ""}.yml`)
const updateInfoFile = path.join(outDir, `${channel}${isMac ? "-mac" : ""}.yml`)
if (!createdFiles.has(updateInfoFile)) {
createdFiles.add(updateInfoFile)
await writeFile(updateInfoFile, safeDump(<UpdateInfo>{
const info = <UpdateInfo>{
version: version,
releaseDate: new Date().toISOString(),
githubArtifactName: event.safeArtifactName,
path: path.basename(event.file!),
sha2: sha2,
}))
sha512: await sha512.value,
}

if (packager.platform === Platform.WINDOWS) {
// backward compatibility
(<any>info).sha2 = await sha2.value
}
await writeFile(updateInfoFile, safeDump(info))
}

// artifact should be uploaded only to designated publish provider
Expand Down Expand Up @@ -400,9 +408,9 @@ export async function getPublishConfigs(packager: PlatformPackager<any>, targetS
return await (<Promise<Array<PublishConfiguration>>>BluebirdPromise.map(asArray(publishers), it => getResolvedPublishConfig(packager, typeof it === "string" ? {provider: it} : it, arch)))
}

function sha256(file: string) {
function hash(file: string, algorithm: string) {
return new BluebirdPromise<string>((resolve, reject) => {
const hash = createHash("sha256")
const hash = createHash(algorithm)
hash
.on("error", reject)
.setEncoding("hex")
Expand Down
20 changes: 2 additions & 18 deletions packages/electron-updater/src/GenericProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class GenericProvider extends Provider<UpdateInfo> {
throw e
}

validateUpdateInfo(result)
Provider.validateUpdateInfo(result)
if (isUseOldMacProvider()) {
(<any>result).releaseJsonUrl = url.format(Object.assign({}, this.baseUrl, {pathname: pathname}))
}
Expand All @@ -54,23 +54,7 @@ export class GenericProvider extends Provider<UpdateInfo> {
name: path.posix.basename(versionInfo.path),
url: url.format(Object.assign({}, this.baseUrl, {pathname: path.posix.resolve(this.baseUrl.pathname || "/", versionInfo.path)})),
sha2: versionInfo.sha2,
sha512: versionInfo.sha512,
}
}
}

// sha2 is required only for windows because on macOS update is verified by Squirrel.Mac
export function validateUpdateInfo(info: UpdateInfo) {
if (isUseOldMacProvider()) {
if ((<any>info).url == null) {
throw new Error("Update info doesn't contain url")
}
return
}

if (info.sha2 == null ) {
throw new Error(`Update info doesn't contain sha2 checksum: ${JSON.stringify(info, null, 2)}`)
}
if (info.path == null) {
throw new Error(`Update info doesn't contain file path: ${JSON.stringify(info, null, 2)}`)
}
}
4 changes: 2 additions & 2 deletions packages/electron-updater/src/GitHubProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { safeLoad } from "js-yaml"
import * as path from "path"
import { parse as parseUrl } from "url"
import { AppUpdater } from "./AppUpdater"
import { validateUpdateInfo } from "./GenericProvider"
import { FileInfo, formatUrl, getChannelFilename, getDefaultChannelName, isUseOldMacProvider, Provider } from "./main"

export abstract class BaseGitHubProvider<T extends UpdateInfo> extends Provider<T> {
Expand Down Expand Up @@ -74,7 +73,7 @@ export class GitHubProvider extends BaseGitHubProvider<UpdateInfo> {
throw e
}

validateUpdateInfo(result)
Provider.validateUpdateInfo(result)
if (isUseOldMacProvider()) {
(<any>result).releaseJsonUrl = `${githubUrl(this.options)}/${requestOptions.path}`
}
Expand Down Expand Up @@ -118,6 +117,7 @@ export class GitHubProvider extends BaseGitHubProvider<UpdateInfo> {
name: name,
url: formatUrl(Object.assign({path: this.getBaseDownloadPath(versionInfo.version, name)}, this.baseUrl)),
sha2: versionInfo.sha2,
sha512: versionInfo.sha512,
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/electron-updater/src/NsisUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export class NsisUpdater extends AppUpdater {
headers: this.computeRequestHeaders(fileInfo),
cancellationToken: cancellationToken,
sha2: fileInfo == null ? null : fileInfo.sha2,
sha512: fileInfo == null ? null : fileInfo.sha512,
}

if (this.listenerCount(DOWNLOAD_PROGRESS) > 0) {
Expand Down
6 changes: 3 additions & 3 deletions packages/electron-updater/src/PrivateGitHubProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import { safeLoad } from "js-yaml"
import * as path from "path"
import { parse as parseUrl } from "url"
import { NET_SESSION_NAME } from "./electronHttpExecutor"
import { validateUpdateInfo } from "./GenericProvider"
import { BaseGitHubProvider } from "./GitHubProvider"
import { FileInfo, formatUrl, getChannelFilename, getDefaultChannelName } from "./main"
import { FileInfo, formatUrl, getChannelFilename, getDefaultChannelName, Provider } from "./main"

export interface PrivateGitHubUpdateInfo extends UpdateInfo {
assets: Array<Asset>
Expand Down Expand Up @@ -45,7 +44,7 @@ export class PrivateGitHubProvider extends BaseGitHubProvider<PrivateGitHubUpdat
throw e
}

validateUpdateInfo(result);
Provider.validateUpdateInfo(result);
(<PrivateGitHubUpdateInfo>result).assets = assets
return result
}
Expand Down Expand Up @@ -99,6 +98,7 @@ export class PrivateGitHubProvider extends BaseGitHubProvider<PrivateGitHubUpdat
name: name,
url: versionInfo.assets.find(it => it.name == name)!.url,
sha2: versionInfo.sha2,
sha512: versionInfo.sha512,
headers: headers,
session: this.netSession
}
Expand Down
19 changes: 18 additions & 1 deletion packages/electron-updater/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { RequestHeaders } from "electron-builder-http"
import { CancellationToken } from "electron-builder-http/out/CancellationToken"
import { ProgressInfo } from "electron-builder-http/out/ProgressCallbackTransform"
import { VersionInfo } from "electron-builder-http/out/publishOptions"
import { UpdateInfo, VersionInfo } from "electron-builder-http/out/publishOptions"
import { EventEmitter } from "events"
import { format as buggyFormat, Url } from "url"
import { AppUpdater } from "./AppUpdater"
Expand Down Expand Up @@ -40,6 +40,7 @@ export interface FileInfo {
readonly name: string
readonly url: string
readonly sha2?: string
readonly sha512?: string
readonly headers?: Object
}

Expand All @@ -53,6 +54,22 @@ export abstract class Provider<T extends VersionInfo> {
abstract getLatestVersion(): Promise<T>

abstract getUpdateFile(versionInfo: T): Promise<FileInfo>

static validateUpdateInfo(info: UpdateInfo) {
if (isUseOldMacProvider()) {
if ((<any>info).url == null) {
throw new Error("Update info doesn't contain url")
}
return
}

if (info.sha2 == null && info.sha512 == null) {
throw new Error(`Update info doesn't contain sha2 or sha512 checksum: ${JSON.stringify(info, null, 2)}`)
}
if (info.path == null) {
throw new Error(`Update info doesn't contain file path: ${JSON.stringify(info, null, 2)}`)
}
}
}

// due to historical reasons for windows we use channel name without platform specifier
Expand Down
Loading

0 comments on commit 62303d7

Please sign in to comment.