Skip to content

Commit

Permalink
feat(deployment): DigitalOcean Spaces support (publish & auto update)
Browse files Browse the repository at this point in the history
  • Loading branch information
develar committed Sep 23, 2017
1 parent 9caead3 commit 8a83577
Show file tree
Hide file tree
Showing 27 changed files with 401 additions and 191 deletions.
2 changes: 2 additions & 0 deletions .idea/dictionaries/develar.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion .yarnclean
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,8 @@ Gruntfile.js
*.gz
*.md

!tslint/lib/test
!tslint/lib/test

aws-sdk.min.js
aws-sdk-react-native.js
aws-sdk-core-react-native.js
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A complete solution to package and build a ready for distribution Electron app f
* [Windows](https://electron.build/configuration/configuration#WinBuildOptions-target): `nsis` (Installer), `nsis-web` (Web installer), `portable` (portable app without installation), AppX (Windows Store), Squirrel.Windows.
* [Two package.json structure](https://electron.build/tutorials/two-package-structure) is supported, but you are not forced to use it even if you have native production dependencies.
* [Build version management](https://electron.build/configuration/configuration#build-version-management).
* [Publishing artifacts](https://electron.build/publishing-artifacts) to GitHub Releases, Amazon S3 and Bintray.
* [Publishing artifacts](https://electron.build/publishing-artifacts) to GitHub Releases, Amazon S3, DigitalOcean Spaces and Bintray.
* Pack in a distributable format [already packaged app](#pack-only-in-a-distributable-format).
* Separate [build steps](https://github.com/electron-userland/electron-builder/issues/1102#issuecomment-271845854).
* Build and publish in parallel, using hard links on CI server to reduce IO and disk space usage.
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A complete solution to package and build a ready for distribution Electron app f
* [Windows](configuration/win.md#WindowsConfiguration-target): `nsis` (Installer), `nsis-web` (Web installer), `portable` (portable app without installation), AppX (Windows Store), Squirrel.Windows.
* [Two package.json structure](tutorials/two-package-structure.md) is supported, but you are not forced to use it even if you have native production dependencies.
* [Build version management](configuration/configuration.md#build-version-management).
* [Publishing artifacts](publishing-artifacts.md) to GitHub Releases, Amazon S3 and Bintray.
* [Publishing artifacts](publishing-artifacts.md) to GitHub Releases, Amazon S3, DigitalOcean Spaces and Bintray.
* Pack in a distributable format [already packaged app](#pack-only-in-a-distributable-format).
* Separate [build steps](https://github.com/electron-userland/electron-builder/issues/1102#issuecomment-271845854).
* Build and publish in parallel, using hard links on CI server to reduce IO and disk space usage.
Expand Down
2 changes: 1 addition & 1 deletion docs/auto-update.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Simplified auto-update is supported on Windows if you use the default NSIS targe
* Download progress supported on all platforms, including macOS.
* [Staged rollouts](#staged-rollouts) supported on all platforms, including macOS.
* Actually, built-in autoUpdater is used inside on macOS.
* Different providers supported out of the box (GitHub, Bintray, Amazon S3, generic HTTP(s) server).
* Different providers supported out of the box ([GitHub Releases](https://help.github.com/articles/about-releases/), [Amazon S3](https://aws.amazon.com/s3/), [DigitalOcean Spaces](https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-spaces), [Bintray](https://bintray.com) and generic HTTP(s) server).
* You need only 2 lines of code to make it work.

## Quick Setup Guide
Expand Down
18 changes: 15 additions & 3 deletions docs/configuration/publish.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ Detected automatically using:

<!-- do not edit. start of generated block -->
## BintrayOptions
[Bintray](https://bintray.com/) options.
[Bintray](https://bintray.com/) options. Requires an API key. An API key can be obtained from the user [profile](https://bintray.com/profile/edit) page ("Edit Your Profile" -> API Key).
Define `BT_TOKEN` environment variable.

* **<code id="BintrayOptions-provider">provider</code>** "bintray" - The provider. Must be `bintray`.
* <code id="BintrayOptions-package">package</code> String - The Bintray package name.
Expand Down Expand Up @@ -77,12 +78,23 @@ Or in the [~/.aws/credentials](http://docs.aws.amazon.com/sdk-for-javascript/v2/

* **<code id="S3Options-provider">provider</code>** "s3" - The provider. Must be `s3`.
* **<code id="S3Options-bucket">bucket</code>** String - The bucket name.
* <code id="S3Options-path">path</code> = `/` String - The directory path.
* <code id="S3Options-region">region</code> String - The region. Is determined and set automatically when publishing.
* <code id="S3Options-channel">channel</code> = `latest` String - The channel.
* <code id="S3Options-acl">acl</code> = `public-read` "private" | "public-read" - The ACL. Set to `null` to not [add](https://github.com/electron-userland/electron-builder/issues/1822).

Please see [required permissions for the S3 provider](https://github.com/electron-userland/electron-builder/issues/1618#issuecomment-314679128).
* <code id="S3Options-storageClass">storageClass</code> = `STANDARD` "STANDARD" | "REDUCED_REDUNDANCY" | "STANDARD_IA" - The type of storage to use for the object.
* <code id="S3Options-channel">channel</code> = `latest` String - The update channel.
* <code id="S3Options-path">path</code> = `/` String - The directory path.

## SpacesOptions
[DigitalOcean Spaces](https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-spaces) options.
Access key is required, define `DO_KEY_ID` and `DO_SECRET_KEY` environment variables.

* **<code id="SpacesOptions-provider">provider</code>** "spaces" - The provider. Must be `spaces`.
* **<code id="SpacesOptions-name">name</code>** String - The space name.
* **<code id="SpacesOptions-region">region</code>** String - The region (e.g. `nyc3`).
* <code id="SpacesOptions-channel">channel</code> = `latest` String - The update channel.
* <code id="SpacesOptions-path">path</code> = `/` String - The directory path.
* <code id="SpacesOptions-acl">acl</code> = `public-read` "private" | "public-read" - The ACL. Set to `null` to not [add](https://github.com/electron-userland/electron-builder/issues/1822).

<!-- end of generated block -->
2 changes: 1 addition & 1 deletion docs/publishing-artifacts.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Travis and AppVeyor support publishing artifacts. But it requires additional con

`electron-builder` allows you to just set `GH_TOKEN` environment variable and that's all (see [publish configuration](/configuration/publish.md)).

Currently, [GitHub Releases](https://help.github.com/articles/about-releases/), [Amazon S3](https://aws.amazon.com/s3/) and [Bintray](https://bintray.com) are supported.
Currently, [GitHub Releases](https://help.github.com/articles/about-releases/), [Amazon S3](https://aws.amazon.com/s3/), [DigitalOcean Spaces](https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-spaces) and [Bintray](https://bintray.com) are supported.

To use Amazon S3 please install `electron-publisher-s3` dependency.

Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"jsdoc": "ts2jsdoc packages/builder-util-runtime packages/electron-updater packages/builder-util packages/electron-builder packages/electron-publish",
"jsdoc2md": "node scripts/jsdoc2md.js",
"docs": "yarn jsdoc && yarn jsdoc2md && gitbook",
"deploy-docs": "./scripts/publish.sh"
"deploy-docs": "./scripts/publish.sh",
"serve-docs": "gitbook && http-server _book -c-1"
},
"//": "repository must be specified otherwise conventional-changelog will use forked repo (currently cloned)",
"repository": "https://github.com/electron-userland/electron-builder",
Expand All @@ -30,7 +31,7 @@
"7zip-bin": "^2.2.4",
"archiver": "^2.0.3",
"async-exit-hook": "^2.0.1",
"aws-sdk": "^2.120.0",
"aws-sdk": "^2.122.0",
"bluebird-lst": "^1.0.3",
"chalk": "^2.1.0",
"chromium-pickle-js": "^0.2.0",
Expand Down Expand Up @@ -79,7 +80,7 @@
"@types/electron-is-dev": "^0.3.0",
"@types/iconv-lite": "^0.0.1",
"@types/ini": "^1.3.29",
"@types/jest": "^20.0.8",
"@types/jest": "^21.1.0",
"@types/js-yaml": "^3.9.1",
"@types/lodash.isequal": "^4.5.2",
"@types/node-emoji": "^1.4.0",
Expand All @@ -95,7 +96,7 @@
"develar-typescript-json-schema": "0.17.0",
"electron-builder-tslint-config": "^1.0.4",
"env-paths": "^1.0.0",
"finalhandler": "^1.0.5",
"finalhandler": "^1.0.6",
"gitbook-plugin-analytics": "^0.2.1",
"gitbook-plugin-develar-toolbar-buttons": "^1.0.2",
"gitbook-plugin-edit-link": "^2.0.2",
Expand All @@ -106,7 +107,7 @@
"jest-junit": "^3.0.0",
"jsdoc-to-markdown": "^3.0.0",
"path-sort": "^0.1.0",
"serve-static": "^1.12.4",
"serve-static": "^1.12.6",
"ts-babel": "^4.1.5",
"ts-jsdoc": "^2.0.6",
"tslint": "^5.7.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/builder-util-runtime/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export { CancellationToken, CancellationError } from "./CancellationToken"
export { HttpError, HttpExecutor, DownloadOptions, DigestTransform, RequestHeaders, safeGetHeader, configureRequestOptions, configureRequestOptionsFromUrl, safeStringifyJson, parseJson } from "./httpExecutor"
export { BintrayOptions, GenericServerOptions, GithubOptions, PublishConfiguration, S3Options, s3Url, Publish, githubUrl, PublishProvider } from "./publishOptions"
export { BintrayOptions, GenericServerOptions, GithubOptions, PublishConfiguration, S3Options, SpacesOptions, BaseS3Options, getS3LikeProviderBaseUrl, Publish, githubUrl, PublishProvider } from "./publishOptions"
export { UpdateInfo, VersionInfo, PackageFileInfo } from "./updateInfo"
export { parseDn } from "./rfc2253Parser"
export { UUID } from "./uuid"
Expand Down
94 changes: 75 additions & 19 deletions packages/builder-util-runtime/src/publishOptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type PublishProvider = "github" | "bintray" | "s3" | "generic"
export type PublishProvider = "github" | "bintray" | "s3" | "spaces" | "generic"

// typescript-json-schema generates only PublishConfiguration if it is specified in the list, so, it is not added here
export type AllPublishOptions = string | GithubOptions | S3Options | GenericServerOptions | BintrayOptions
Expand Down Expand Up @@ -94,14 +94,35 @@ export interface GenericServerOptions extends PublishConfiguration {
readonly channel?: string | null
}

export interface BaseS3Options extends PublishConfiguration {
/**
* The update channel.
* @default latest
*/
channel?: string | null

/**
* The directory path.
* @default /
*/
readonly path?: string | null

/**
* The ACL. Set to `null` to not [add](https://github.com/electron-userland/electron-builder/issues/1822).
*
* @default public-read
*/
readonly acl?: "private" | "public-read" | null
}

/**
* [Amazon S3](https://aws.amazon.com/s3/) options. `https` must be used, so, if you use direct Amazon S3 endpoints, format `https://s3.amazonaws.com/bucket_name` [must be used](http://stackoverflow.com/a/11203685/1910191). And do not forget to make files/directories public.
*
* AWS credentials are required, please see [getting your credentials](http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/getting-your-credentials.html).
* Define `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` [environment variables](http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-environment.html).
* Or in the [~/.aws/credentials](http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-shared.html).
*/
export interface S3Options extends PublishConfiguration {
export interface S3Options extends BaseS3Options {
/**
* The provider. Must be `s3`.
*/
Expand All @@ -112,22 +133,10 @@ export interface S3Options extends PublishConfiguration {
*/
readonly bucket: string

/**
* The directory path.
* @default /
*/
readonly path?: string | null

/**
* The region. Is determined and set automatically when publishing.
*/
readonly region?: string | null

/**
* The channel.
* @default latest
*/
readonly channel?: string | null
region?: string | null

/**
* The ACL. Set to `null` to not [add](https://github.com/electron-userland/electron-builder/issues/1822).
Expand All @@ -145,8 +154,39 @@ export interface S3Options extends PublishConfiguration {
readonly storageClass?: "STANDARD" | "REDUCED_REDUNDANCY" | "STANDARD_IA" | null
}

/** @private */
export function s3Url(options: S3Options) {
/**
* [DigitalOcean Spaces](https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-spaces) options.
* Access key is required, define `DO_KEY_ID` and `DO_SECRET_KEY` environment variables.
*/
export interface SpacesOptions extends BaseS3Options {
/**
* The provider. Must be `spaces`.
*/
readonly provider: "spaces"

/**
* The space name.
*/
readonly name: string

/**
* The region (e.g. `nyc3`).
*/
readonly region: string
}

export function getS3LikeProviderBaseUrl(configuration: PublishConfiguration) {
const provider = configuration.provider
if (provider === "s3") {
return s3Url((configuration as S3Options))
}
if (provider === "spaces") {
return spacesUrl((configuration as SpacesOptions))
}
throw new Error(`Not supported provider: ${provider}`)
}

function s3Url(options: S3Options) {
let url: string
if (!options.bucket.includes(".")) {
if (options.region === "cn-north-1") {
Expand All @@ -157,7 +197,7 @@ export function s3Url(options: S3Options) {
}
}
else {
if (!options.region) {
if (options.region == null) {
throw new Error(`Bucket name "${options.bucket}" includes a dot, but S3 region is missing`)
}

Expand All @@ -173,8 +213,24 @@ export function s3Url(options: S3Options) {
return url
}

function spacesUrl(options: SpacesOptions) {
if (options.name == null) {
throw new Error(`name is missing`)
}
if (options.region == null) {
throw new Error(`region is missing`)
}

let url = `https://${options.name}.${options.region}.digitaloceanspaces.com`
if (options.path != null) {
url += `/${options.path}`
}
return url
}

/**
* [Bintray](https://bintray.com/) options.
* [Bintray](https://bintray.com/) options. Requires an API key. An API key can be obtained from the user [profile](https://bintray.com/profile/edit) page ("Edit Your Profile" -> API Key).
* Define `BT_TOKEN` environment variable.
*/
export interface BintrayOptions extends PublishConfiguration {
/**
Expand Down
21 changes: 12 additions & 9 deletions packages/electron-builder/src/publish/PublishManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import BluebirdPromise from "bluebird-lst"
import { Arch, asArray, AsyncTaskManager, isEmptyOrSpaces, isPullRequest, log, safeStringifyJson, warn } from "builder-util"
import { BintrayOptions, CancellationToken, GenericServerOptions, GithubOptions, githubUrl, PublishConfiguration, PublishProvider, S3Options, s3Url } from "builder-util-runtime"
import { BintrayOptions, CancellationToken, GenericServerOptions, getS3LikeProviderBaseUrl, GithubOptions, githubUrl, PublishConfiguration, PublishProvider } from "builder-util-runtime"
import _debug from "debug"
import { getCiTag, HttpPublisher, PublishContext, Publisher, PublishOptions } from "electron-publish"
import { BintrayPublisher } from "electron-publish/out/BintrayPublisher"
Expand Down Expand Up @@ -200,7 +200,7 @@ export async function getPublishConfigsForUpdateInfo(packager: PlatformPackager<

export function createPublisher(context: PublishContext, version: string, publishConfig: PublishConfiguration, options: PublishOptions): Publisher | null {
if (debug.enabled) {
debug(`create publisher: ${safeStringifyJson(publishConfig)}`)
debug(`Create publisher: ${safeStringifyJson(publishConfig)}`)
}

const provider = publishConfig.provider
Expand Down Expand Up @@ -231,14 +231,17 @@ function requireProviderClass(provider: string): any | null {
case "generic":
return null

case "spaces":
return require(`electron-publisher-s3/out/${provider}Publisher`).default

default:
return require(`electron-publisher-${provider}`).default
}
}

export function computeDownloadUrl(publishConfig: PublishConfiguration, fileName: string | null, packager: PlatformPackager<any>) {
if (publishConfig.provider === "generic") {
const baseUrlString = (publishConfig as GenericServerOptions).url
export function computeDownloadUrl(publishConfiguration: PublishConfiguration, fileName: string | null, packager: PlatformPackager<any>) {
if (publishConfiguration.provider === "generic") {
const baseUrlString = (publishConfiguration as GenericServerOptions).url
if (fileName == null) {
return baseUrlString
}
Expand All @@ -248,12 +251,12 @@ export function computeDownloadUrl(publishConfig: PublishConfiguration, fileName
}

let baseUrl
if (publishConfig.provider === "s3") {
baseUrl = s3Url((publishConfig as S3Options))
if (publishConfiguration.provider === "github") {
const gh = publishConfiguration as GithubOptions
baseUrl = `${githubUrl(gh)}/${gh.owner}/${gh.repo}/releases/download/${gh.vPrefixedTagName === false ? "" : "v"}${packager.appInfo.version}`
}
else {
const gh = publishConfig as GithubOptions
baseUrl = `${githubUrl(gh)}/${gh.owner}/${gh.repo}/releases/download/${gh.vPrefixedTagName === false ? "" : "v"}${packager.appInfo.version}`
baseUrl = getS3LikeProviderBaseUrl(publishConfiguration)
}

if (fileName == null) {
Expand Down
3 changes: 2 additions & 1 deletion packages/electron-builder/src/publish/updateUnfoBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ export async function writeUpdateInfo(event: ArtifactCreated, _publishConfigs: A
dir = path.join(outDir, publishConfig.provider)
}

if (isMac) {
// spaces is a new publish provider, no need to keep backward compatibility
if (isMac && publishConfig.provider !== "spaces") {
await writeOldMacInfo(publishConfig, outDir, dir, channel, createdFiles, version, packager)
}

Expand Down
2 changes: 1 addition & 1 deletion packages/electron-publish/src/BintrayPublisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class BintrayPublisher extends HttpPublisher {
if (isEmptyOrSpaces(token)) {
token = process.env.BT_TOKEN
if (isEmptyOrSpaces(token)) {
throw new Error(`Bintray token is not set, neither programmatically, nor using env "BT_TOKEN"`)
throw new Error(`Bintray token is not set, neither programmatically, nor using env "BT_TOKEN" (see https://www.electron.build/configuration/publish#bintrayoptions)`)
}

token = token.trim()
Expand Down
2 changes: 1 addition & 1 deletion packages/electron-publisher-s3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
],
"dependencies": {
"fs-extra-p": "^4.4.2",
"aws-sdk": "^2.120.0",
"aws-sdk": "^2.122.0",
"mime": "^2.0.2",
"electron-publish": "~0.0.0-semantic-release",
"builder-util": "^0.0.0-semantic-release",
Expand Down
Loading

0 comments on commit 8a83577

Please sign in to comment.