Skip to content

Commit

Permalink
feat(linux): sh, rpm, freebsd, pacman, p5p, apk, 7z, zip, tar.xz, tar…
Browse files Browse the repository at this point in the history
….gz, tar.bz2, tar.7z

Closes electron-userland#414
  • Loading branch information
develar committed May 22, 2016
1 parent f781217 commit 9788407
Show file tree
Hide file tree
Showing 23 changed files with 404 additions and 158 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ addons:
- icnsutils
- graphicsmagick
- mono-devel
- bsdtar
- rpm

before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
Expand Down
74 changes: 72 additions & 2 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ FROM buildpack-deps:xenial-curl
# rpm is required for FPM to build rpm package

RUN apt-get update -y && \
apt-get install --no-install-recommends -y build-essential icnsutils graphicsmagick gcc-multilib g++-multilib libgnome-keyring-dev zip rpm && \
apt-get install --no-install-recommends -y bsdtar build-essential autoconf libssl-dev icnsutils graphicsmagick gcc-multilib g++-multilib libgnome-keyring-dev zip rpm && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

Expand Down Expand Up @@ -42,4 +42,74 @@ COPY test.sh /test.sh
WORKDIR /project

ENV DEBUG_COLORS true
ENV FORCE_COLOR true
ENV FORCE_COLOR true

# copied from https://github.com/docker-library/ruby/blob/0b94677b368947b64dcdcb312cd81ba946df3676/2.3/Dockerfile

# skip installing gem documentation
RUN mkdir -p /usr/local/etc \
&& { \
echo 'install: --no-document'; \
echo 'update: --no-document'; \
} >> /usr/local/etc/gemrc

ENV RUBY_MAJOR 2.3
ENV RUBY_VERSION 2.3.1
ENV RUBY_DOWNLOAD_SHA256 b87c738cb2032bf4920fef8e3864dc5cf8eae9d89d8d523ce0236945c5797dcd
ENV RUBYGEMS_VERSION 2.6.4

# some of ruby's build scripts are written in ruby
# we purge this later to make sure our final image uses what we just built
RUN set -ex \
&& buildDeps=' \
bison \
libgdbm-dev \
ruby \
' \
&& apt-get update \
&& apt-get install -y --no-install-recommends $buildDeps \
&& rm -rf /var/lib/apt/lists/* \
&& curl -fSL -o ruby.tar.gz "http://cache.ruby-lang.org/pub/ruby/$RUBY_MAJOR/ruby-$RUBY_VERSION.tar.gz" \
&& echo "$RUBY_DOWNLOAD_SHA256 *ruby.tar.gz" | sha256sum -c - \
&& mkdir -p /usr/src/ruby \
&& tar -xzf ruby.tar.gz -C /usr/src/ruby --strip-components=1 \
&& rm ruby.tar.gz \
&& cd /usr/src/ruby \
&& { echo '#define ENABLE_PATH_CHECK 0'; echo; cat file.c; } > file.c.new && mv file.c.new file.c \
&& autoconf \
&& ./configure --disable-install-doc \
&& make -j"$(nproc)" \
&& make install \
&& apt-get purge -y --auto-remove $buildDeps \
&& gem update --system $RUBYGEMS_VERSION \
&& rm -r /usr/src/ruby

ENV BUNDLER_VERSION 1.12.4

RUN gem install bundler --version "$BUNDLER_VERSION"

# install things globally, for great justice
# and don't create ".bundle" in all our apps
ENV GEM_HOME /usr/local/bundle
ENV BUNDLE_PATH="$GEM_HOME" \
BUNDLE_BIN="$GEM_HOME/bin" \
BUNDLE_SILENCE_ROOT_WARNING=1 \
BUNDLE_APP_CONFIG="$GEM_HOME"
ENV PATH $BUNDLE_BIN:$PATH
RUN mkdir -p "$GEM_HOME" "$BUNDLE_BIN" \
&& chmod 777 "$GEM_HOME" "$BUNDLE_BIN" \
&& mkdir /fpm && curl -L https://github.com/jordansissel/fpm/archive/6e2514df27664912826b4fcd89affa19df0e713b.tar.gz | tar -xz -C /fpm --strip-components 1 && cd /fpm && bundle install && make install && cd ..

# use fpm commit https://github.com/jordansissel/fpm/commit/6e2514df27664912826b4fcd89affa19df0e713b because of some important unreleased fixes:
# https://github.com/jordansissel/fpm/commit/94be82c0a23c8cd641ab9e60f3eb4a8db445fff0
# https://github.com/jordansissel/fpm/commit/77b95747b9cc01ca420ee24084a449b3ac19e6d5

ENV USE_SYSTEM_FPM true

# fix error /usr/local/bundle/gems/fpm-1.5.0/lib/fpm/package/freebsd.rb:72:in `encode': "\xE2" from ASCII-8BIT to UTF-8 (Encoding::UndefinedConversionError)
# http://jaredmarkell.com/docker-and-locales/
# http://askubuntu.com/a/601498
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
9 changes: 8 additions & 1 deletion docker/readme.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# Development machine

To build Linux:
```sh
docker run --rm -ti -v `pwd`:/project -v `pwd`/node_modules/.linux:/project/node_modules -v ~/.electron:/root/.electron electronuserland/electron-builder
```

Wine:
To build windows:
```sh
docker run --rm -ti -v ${PWD}:/project -v ${PWD##*/}-node-modules:/project/node_modules -v ~/.electron:/root/.electron electronuserland/electron-builder:wine
```

Consider using `/test.sh` to install npm dependencies and run tests.

# CI Server

```sh
Expand All @@ -22,6 +25,10 @@ docker build -t electronuserland/electron-builder docker
docker build -t electronuserland/electron-builder:wine docker/wine
```

Or just `npm run docker-images`

# Notes

* We use [named data volume](https://madcoda.com/2016/03/docker-named-volume-explained/) instead of mounted host directory to store `node_modules` because NPM is unreliable and NPM team [doesn't want to fix it](https://github.com/npm/npm/issues/3565).

`${PWD##*/}-node-modules` is used as name of data volume — it is your current directory name (e. g. `foo`) and suffix `-node-modules`.
Expand Down
16 changes: 11 additions & 5 deletions docs/Multi Platform Build.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,25 @@ To build app in distributable format for Linux on OS X:
brew install gnu-tar libicns graphicsmagick
```

To build rpm: `brew install rpm`.

## Linux
To build app in distributable format for Linux:
```
sudo apt-get install icnsutils graphicsmagick xz-utils
sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils
```

To build rpm: `sudo apt-get install --no-install-recommends -y rpm`.

To build pacman: `sudo apt-get install --no-install-recommends -y bsdtar`.

To build app in distributable format for Windows on Linux:
* Install Wine (1.8+ is required):

```
sudo add-apt-repository ppa:ubuntu-wine/ppa -y
sudo apt-get update
sudo apt-get install wine1.8 -y
sudo apt-get install --no-install-recommends -y wine1.8
```

* Install [Mono](http://www.mono-project.com/docs/getting-started/install/linux/#usage) (4.2+ is required):
Expand All @@ -46,18 +52,18 @@ To build app in distributable format for Windows on Linux:
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list
sudo apt-get update
sudo apt-get install mono-devel ca-certificates-mono -y
sudo apt-get install --no-install-recommends -y mono-devel ca-certificates-mono
```

* Install zip.
```
apt-get install zip
apt-get install --no-install-recommends -y zip
```

To build app in 32 bit from a machine with 64 bit:

```
sudo apt-get install -y gcc-multilib g++-multilib
sudo apt-get install --no-install-recommends -y gcc-multilib g++-multilib
```

### Travis Linux
Expand Down
3 changes: 2 additions & 1 deletion docs/Options.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ See all [appdmg options](https://www.npmjs.com/package/appdmg#json-specification
| --- | ---
| icon | <a name="OsXBuildOptions-icon"></a>The path to icon, which will be shown when mounted (default: `build/icon.icns`).
| background | <a name="OsXBuildOptions-background"></a><p>The path to background (default: <code>build/background.png</code> if exists). The resolution of this file determines the resolution of the installer window. If background is not specified, use <code>window.size</code>, see [specification](https://github.com/LinusU/node-appdmg#json-specification).</p>
| target | <a name="OsXBuildOptions-target"></a>Target package type: list of `default`, `dmg`, `zip`, `mas`, `7z`. Defaults to `default` (dmg and zip for Squirrel.Mac).
| target | <a name="OsXBuildOptions-target"></a>Target package type: list of `default`, `dmg`, `zip`, `mas`, `7z`, `tar.xz`, `tar.gz`, `tar.bz2`, `tar.7z`. Defaults to `default` (dmg and zip for Squirrel.Mac).
| identity | <a name="OsXBuildOptions-identity"></a><p>The name of certificate to use when signing. Consider using environment variables [CSC_LINK or CSC_NAME](https://github.com/electron-userland/electron-builder/wiki/Code-Signing). MAS installer identity is specified in the [.build.mas](#MasBuildOptions-identity).</p>
| entitlements | <a name="OsXBuildOptions-entitlements"></a><p>The path to entitlements file for signing the app. <code>build/osx.entitlements</code> will be used if exists (it is a recommended way to set). MAS entitlements is specified in the [.build.mas](#MasBuildOptions-entitlements).</p>
| entitlementsInherit | <a name="OsXBuildOptions-entitlementsInherit"></a><p>The path to child entitlements which inherit the security settings for signing frameworks and bundles of a distribution. <code>build/osx.inherit.entitlements</code> will be used if exists (it is a recommended way to set). Otherwise [default](https://github.com/electron-userland/electron-osx-sign/blob/master/default.darwin.inherit.entitlements).</p> <p>This option only applies when signing with <code>entitlements</code> provided.</p>
Expand Down Expand Up @@ -107,6 +107,7 @@ MAS (Mac Application Store) specific options (in addition to `build.osx`).
| vendor | <a name="LinuxBuildOptions-vendor"></a>The vendor. Defaults to [author](#AppMetadata-author).
| compression | <a name="LinuxBuildOptions-compression"></a>*deb-only.* The compression type, one of `gz`, `bzip2`, `xz` (default: `xz`).
| depends | <a name="LinuxBuildOptions-depends"></a>Package dependencies. Defaults to `["libappindicator1", "libnotify-bin"]`.
| target | <a name="LinuxBuildOptions-target"></a><p>Target package type: list of <code>default</code>, <code>deb</code>, <code>rpm</code>, <code>freebsd</code>, <code>pacman</code>, <code>p5p</code>, <code>apk</code>, <code>7z</code>, <code>zip</code>, <code>tar.xz</code>, <code>tar.gz</code>, <code>tar.bz2</code>, <code>tar.7z</code>. Defaults to <code>default</code> (<code>deb</code>).</p> <p>Only <code>deb</code> is tested. Feel free to file issues for <code>rpm</code> and other package formats.</p>

<a name="MetadataDirectories"></a>
## `.directories`
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"lint": "tslint src/*.ts test/src/*.ts",
"pretest": "npm run compile && npm run lint",
"test": "node ./test/out/helpers/runTests.js",
"ci-test": "git-lfs pull && npm install && npm prune && npm run test",
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
"//": "Update wiki if docs changed. Update only if functionalily are generally available (latest release, not next)",
"update-wiki": "git subtree split -b wiki --prefix docs/ && git push wiki wiki:master",
Expand Down Expand Up @@ -70,11 +69,12 @@
"electron-packager-tf": "~7.1.0",
"electron-winstaller-fixed": "~2.8.3",
"fs-extra-p": "^1.0.1",
"globby": "^4.0.0",
"globby": "^4.1.0",
"hosted-git-info": "^2.1.5",
"image-size": "^0.5.0",
"lodash.template": "^4.2.5",
"mime": "^1.3.4",
"pipe-io": "^1.2.1",
"progress": "^1.1.8",
"progress-stream": "^1.2.0",
"read-package-json": "^2.0.4",
Expand Down Expand Up @@ -105,11 +105,11 @@
"plist": "^1.2.0",
"pre-git": "^3.8.4",
"semantic-release": "^6.2.2",
"should": "^8.3.1",
"should": "^8.3.2",
"ts-babel": "^0.8.6",
"tsconfig-glob": "^0.4.3",
"tslint": "3.10.2",
"typescript": "1.9.0-dev.20160515",
"tslint": "3.10.0-dev.2",
"typescript": "1.9.0-dev.20160520-1.0",
"whitespace": "^2.0.0"
},
"babel": {
Expand Down
12 changes: 2 additions & 10 deletions src/fpmDownload.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { statOrNull, spawn, debug, debug7z } from "./util"
import { statOrNull, spawn, debug, debug7zArgs } from "./util"
import { writeFile, rename, remove, unlink, emptyDir } from "fs-extra-p"
import { download } from "./httpRequest"
import { path7za } from "7zip-bin"
Expand Down Expand Up @@ -55,15 +55,7 @@ async function doDownloadFpm(version: string, osAndArch: string): Promise<string
await emptyDir(tempUnpackDir)
await download(url, archiveName, false)

const args = ["x", archiveName, "-o" + tempUnpackDir, "-bd"]
if (debug7z.enabled) {
args.push("-bb3")
}
else if (!debug.enabled) {
args.push("-bb0")
}

await spawn(path7za, args, {
await spawn(path7za, debug7zArgs("x").concat(archiveName, `-o${tempUnpackDir}`), {
cwd: cacheDir,
stdio: ["ignore", debug.enabled ? "inherit" : "ignore", "inherit"],
})
Expand Down
55 changes: 35 additions & 20 deletions src/linuxPackager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as path from "path"
import { Promise as BluebirdPromise } from "bluebird"
import { PlatformPackager, BuildInfo } from "./platformPackager"
import { PlatformPackager, BuildInfo, smarten, archSuffix } from "./platformPackager"
import { Platform, LinuxBuildOptions } from "./metadata"
import { dir as _tpmDir, TmpOptions } from "tmp"
import { exec, debug, use } from "./util"
Expand All @@ -15,7 +15,7 @@ const tmpDir = BluebirdPromise.promisify(<(config: TmpOptions, callback: (error:
const installPrefix = "/opt"

export class LinuxPackager extends PlatformPackager<LinuxBuildOptions> {
private readonly debOptions: LinuxBuildOptions
private readonly buildOptions: LinuxBuildOptions

private readonly packageFiles: Promise<Array<string>>
private readonly scriptFiles: Promise<Array<string>>
Expand All @@ -25,7 +25,7 @@ export class LinuxPackager extends PlatformPackager<LinuxBuildOptions> {
constructor(info: BuildInfo) {
super(info)

this.debOptions = Object.assign({
this.buildOptions = Object.assign({
name: this.metadata.name,
description: this.metadata.description,
}, this.customBuildOptions)
Expand All @@ -47,6 +47,10 @@ export class LinuxPackager extends PlatformPackager<LinuxBuildOptions> {
}
}

protected get supportedTargets(): Array<string> {
return ["deb", "rpm", "sh", "freebsd", "pacman", "apk", "p5p"]
}

get platform() {
return Platform.LINUX
}
Expand Down Expand Up @@ -75,9 +79,9 @@ export class LinuxPackager extends PlatformPackager<LinuxBuildOptions> {

private async computeDesktop(tempDir: string): Promise<Array<string>> {
const tempFile = path.join(tempDir, this.appName + ".desktop")
await outputFile(tempFile, this.debOptions.desktop || `[Desktop Entry]
await outputFile(tempFile, this.buildOptions.desktop || `[Desktop Entry]
Name=${this.appName}
Comment=${this.debOptions.description}
Comment=${this.buildOptions.description}
Exec="${installPrefix}/${this.appName}/${this.appName}"
Terminal=false
Type=Application
Expand Down Expand Up @@ -168,26 +172,27 @@ Icon=${this.metadata.name}
const templateOptions = Object.assign({
// old API compatibility
executable: this.appName,
}, this.debOptions)
}, this.buildOptions)

const afterInstallTemplate = this.debOptions.afterInstall || path.join(defaultTemplatesDir, "after-install.tpl")
const afterInstallTemplate = this.buildOptions.afterInstall || path.join(defaultTemplatesDir, "after-install.tpl")
const afterInstallFilePath = writeConfigFile(tempDir, afterInstallTemplate, templateOptions)

const afterRemoveTemplate = this.debOptions.afterRemove || path.join(defaultTemplatesDir, "after-remove.tpl")
const afterRemoveTemplate = this.buildOptions.afterRemove || path.join(defaultTemplatesDir, "after-remove.tpl")
const afterRemoveFilePath = writeConfigFile(tempDir, afterRemoveTemplate, templateOptions)

return await BluebirdPromise.all<string>([afterInstallFilePath, afterRemoveFilePath])
}

async packageInDistributableFormat(outDir: string, appOutDir: string, arch: string): Promise<any> {
return await this.buildDeb(this.debOptions, outDir, appOutDir, arch)
.then(it => this.dispatchArtifactCreated(it))
packageInDistributableFormat(outDir: string, appOutDir: string, arch: string): Promise<any> {
return BluebirdPromise.map(this.targets, (target): any => {
target = target === "default" ? "deb" : target
const destination = path.join(outDir, `${this.metadata.name}-${this.metadata.version}${archSuffix(arch)}.${target}`)
return (target === "zip" || target === "7z" || target.startsWith("tar.") ? this.archiveApp(target, appOutDir, destination) : this.buildPackage(destination, target, this.buildOptions, appOutDir, arch))
.then(() => this.dispatchArtifactCreated(destination))
})
}

private async buildDeb(options: LinuxBuildOptions, outDir: string, appOutDir: string, arch: string): Promise<string> {
const archName = arch === "ia32" ? "i386" : "amd64"
const target = "deb"
const destination = path.join(outDir, `${this.metadata.name}-${this.metadata.version}-${archName}.${target}`)
private async buildPackage(destination: string, target: string, options: LinuxBuildOptions, appOutDir: string, arch: string): Promise<any> {
const scripts = await this.scriptFiles

const projectUrl = await this.computePackageUrl()
Expand All @@ -196,24 +201,35 @@ Icon=${this.metadata.name}
}

const author = options.maintainer || `${this.metadata.author.name} <${this.metadata.author.email}>`
const synopsis = options.synopsis
const args = [
"-s", "dir",
"-t", target,
"--architecture", archName,
"--rpm-os", "linux",
"--architecture", arch === "ia32" ? "i386" : "amd64",
"--name", this.metadata.name,
"--force",
"--after-install", scripts[0],
"--after-remove", scripts[1],
"--description", `${options.synopsis || ""}\n ${this.debOptions.description}`,
"--description", smarten(target === "rpm" ? this.buildOptions.description! : `${synopsis || ""}\n ${this.buildOptions.description}`),
"--maintainer", author,
"--vendor", options.vendor || author,
"--version", this.metadata.version,
"--package", destination,
"--deb-compression", options.compression || (this.devMetadata.build.compression === "store" ? "gz" : "xz"),
"--url", projectUrl,
]

if (target === "deb") {
args.push("--deb-compression", options.compression || (this.devMetadata.build.compression === "store" ? "gz" : "xz"))
}
else if (target === "rpm") {
// args.push("--rpm-compression", options.compression || (this.devMetadata.build.compression === "store" ? "none" : "xz"))
args.push("--rpm-os", "linux")

if (synopsis != null) {
args.push("--rpm-summary", smarten(synopsis))
}
}

let depends = options.depends
if (depends == null) {
depends = ["libappindicator1", "libnotify-bin"]
Expand All @@ -239,7 +255,6 @@ Icon=${this.metadata.name}
args.push(`${appOutDir}/=${installPrefix}/${this.appName}`)
args.push(...<any>(await this.packageFiles)!)
await exec(await this.fpmPath, args)
return destination
}
}

Expand Down
Loading

0 comments on commit 9788407

Please sign in to comment.