Skip to content

Commit

Permalink
feat: linux icons from custom dir, generate missing from ICNS
Browse files Browse the repository at this point in the history
24x24, 48x48, 64x64, 96x96 and 128x128 icons will be generated if missed in the ICNS
You can provide Linux icon set explicitly — create `icons` folder in the `build`
  • Loading branch information
develar committed Mar 17, 2016
1 parent c778e2b commit 7ac4b84
Show file tree
Hide file tree
Showing 34 changed files with 195 additions and 110 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.png filter=lfs diff=lfs merge=lfs -text
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.

5 changes: 3 additions & 2 deletions .travis.yml
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ addons:
apt:
packages:
- icnsutils
- graphicsmagick

before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install gnu-tar dpkg libicns; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install gnu-tar dpkg libicns graphicsmagick git-lfs; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then git lfs pull; fi
- gem install --no-rdoc --no-ri fpm

install:
Expand All @@ -31,7 +33,6 @@ install:
- source ~/.nvm/nvm.sh
- nvm install $NODE_VERSION
- npm install npm -g
- npm -v
- npm prune
- npm install

Expand Down
66 changes: 16 additions & 50 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,27 @@ Why the two package.json structure is ideal and how it solves a lot of issues
2. When you package the app for distribution there is no need to add up to size of the app with your `devDependencies`. Here those are always not included (because reside outside the `app` directory).

# Configuration

See [options](./docs/options.md), but consider to follow simple 4-step guide outlined below at first.

## In short
1. Ensure that required fields are specified in the application `package.json`:
1. Ensure that required fields are specified in the application `package.json`:

Standard `name`, `description`, `version` and `author`.
Standard `name`, `description`, `version` and `author`.

Custom `build` field must be specified:
```json
"build": {
"app-bundle-id": "your.id",
"app-category-type": "your.app.category.type",
"iconUrl": "(windows only) A URL to an ICO file to use as the application icon, see details below"
}
```
This object will be used as a source of [electron-packager](https://www.npmjs.com/package/electron-packager#packageropts-callback) options. You can specify any other options here.
Custom `build` field must be specified:
```json
"build": {
"app-bundle-id": "your.id",
"app-category-type": "your.app.category.type",
"iconUrl": "(windows only) A URL to an ICO file to use as the application icon, see details below"
}
```
This object will be used as a source of [electron-packager](https://www.npmjs.com/package/electron-packager#packageropts-callback) options. You can specify any other options here.

2. Create directory `build` in the root of the project and put your `background.png` (OS X DMG background), `icon.icns` (OS X app icon) and `icon.ico` (Windows app icon).
Linux icon set will be generated automatically on the fly from the OS X `icns` file.

Linux icon set will be generated automatically on the fly from the OS X `icns` file (or you can put them into the `build/icons` directory — filename must contains size (e.g. `32x32.png`)).

3. Add [scripts](https://docs.npmjs.com/cli/run-script) to the development `package.json`:
```json
Expand All @@ -64,44 +68,6 @@ Why the two package.json structure is ideal and how it solves a lot of issues

4. Install [required system packages](./docs/multi-platform-build.md).

## iconUrl
Please note — [local icon file url is not accepted](https://github.com/atom/grunt-electron-installer/issues/73), must be https/http.
* If you don't plan to build windows installer, you can omit it.
* If your project repository is public on GitHub, it will be `https://raw.githubusercontent.com/${info.user}/${info.project}/master/build/icon.ico` by default.

## Distributable Format Configuration
In the development `package.json` custom `build` field can be specified to customize distributable format:
```json
"build": {
"osx": {
"title": "computed name from app package.js, you can overwrite",
"icon": "build/icon.icns",
"icon-size": 80,
"background": "build/background.png",
"contents": [
{
"x": 410,
"y": 220,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 220,
"type": "file",
"path": "computed path to artifact, do not specify it - will be overwritten"
}
]
},
"win": "see https://github.com/electronjs/windows-installer#usage"
}
```

As you can see, you need to customize OS X options only if you want to provide custom `x, y`.
Don't customize paths to background and icon, — just follow conventions (if you don't want to use `build` as directory of resources — please create issue to ask ability to customize it).

See [OS X options](https://www.npmjs.com/package/appdmg#json-specification) and [Windows options](https://github.com/electronjs/windows-installer#usage).

# Auto Update
`electron-builder` produces all required artifacts:

Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ cache:
- '%USERPROFILE%\.electron'

init:
- git config --global core.autocrlf input
- git config --global core.autocrlf false

install:
- choco install nsis -y
Expand Down
2 changes: 1 addition & 1 deletion docs/multi-platform-build.md
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ brew install Caskroom/cask/xquartz wine mono

To build app in distributable format for Linux on OS X:
```
brew install ruby gnu-tar libicns imagemagick graphicsmagick
brew install ruby gnu-tar libicns graphicsmagick
gem install fpm
```

Expand Down
40 changes: 40 additions & 0 deletions docs/options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Options

In the development `package.json` custom `build` field can be specified to customize format:
```json
"build": {
"osx": {
"title": "computed name from the app package.js, you can overwrite",
"icon": "build/icon.icns",
"icon-size": 80,
"background": "build/background.png",
"contents": [
{
"x": 410,
"y": 220,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 220,
"type": "file",
"path": "computed path to artifact, do not specify it - will be overwritten"
}
]
},
"win": "see https://github.com/electronjs/windows-installer#usage"
}
```

As you can see, you need to customize OS X options only if you want to provide custom `x, y`.
Don't customize paths to background and icon, — just follow conventions (if you don't want to use `build` as directory of resources — please create issue to ask ability to customize it).

See [OS X options](https://www.npmjs.com/package/appdmg#json-specification) and [Windows options](https://github.com/electronjs/windows-installer#usage).

Here documented only `electron-builder` specific options:

| Name | Description
| --- | ---
| <a name="iconUrl"></a>iconUrl<br/> | <p>*windows-only.* A URL to an ICO file to use as the application icon (displayed in Control Panel > Programs and Features). Defaults to the Atom icon.</p><p>Please note — [local icon file url is not accepted](https://github.com/atom/grunt-electron-installer/issues/73), must be https/http.</p><ul><li>If you don't plan to build windows installer, you can omit it.</li><li>If your project repository is public on GitHub, it will be `https://raw.githubusercontent.com/${info.user}/${info.project}/master/build/icon.ico` by default.</li></ul>
| <a name="extraResources"></a>extraResources | <p>A [glob expression](https://www.npmjs.com/package/glob#glob-primer), when specified, copy the file or directory with matching names directly into the app's directory (`Contents/Resources` for OS X).</p><p>You can use `${os}` (expanded to osx, linux or win according to current platform) and `${arch}` in the pattern.</p><p>If directory matched, all contents are copied. So, you can just specify `foo` to copy `<project_dir>/foo` directory.</p><p>May be specified in the platform options (i.e. in the `build.osx`).
17 changes: 8 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"test": "node ./test/out/helpers/runTests.js",
"test-nix": "tape ./lib/*.spec.js ./lib/**/*.spec.js && ava",
"test-win": "ava",
"semantic-release": "semantic-release pre && npm publish && semantic-release post"
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
"validate-commit-msg": "validate-commit-msg"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -53,8 +54,8 @@
"bluebird": "^3.3.4",
"command-line-args": "^2.1.6",
"electron-packager-tf": "^5.2.3",
"electron-winstaller-fixed": "^2.0.5-beta.7",
"fs-extra": "^0.26.5",
"electron-winstaller-fixed": "^2.0.6-beta.1",
"fs-extra": "^0.26.7",
"fs-extra-p": "^0.1.0",
"globby": "^4.0.0",
"gm": "^1.21.1",
Expand All @@ -78,7 +79,7 @@
"electron-download": "^2.1.0",
"eslint": "^2.4.0",
"eslint-plugin-ava": "sindresorhus/eslint-plugin-ava",
"ghooks": "^1.0.3",
"pre-commit": "^1.1.2",
"json-parse-helpfulerror": "^1.0.3",
"path-sort": "^0.1.0",
"plist": "^1.2.0",
Expand Down Expand Up @@ -108,11 +109,9 @@
]
},
"typings": "./out/electron-builder.d.ts",
"config": {
"ghooks": {
"commit-msg": "validate-commit-msg"
}
},
"precommit": [
"validate-commit-msg"
],
"publishConfig": {
"tag": "next"
}
Expand Down
88 changes: 60 additions & 28 deletions src/linuxPackager.ts
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { PlatformPackager, BuildInfo } from "./platformPackager"
import { Platform } from "./metadata"
import { dir as _tpmDir, TmpOptions } from "tmp"
import { exec, log } from "./util"
import { outputFile, readFile, readdir } from "fs-extra-p"
import { State as Gm } from "gm"
import { outputFile, readFile, stat } from "fs-extra-p"
const template = require("lodash.template")

//noinspection JSUnusedLocalSymbols
Expand Down Expand Up @@ -67,45 +67,77 @@ Icon=${this.metadata.name}
return [`${tempFile}=/usr/share/applications/${this.appName}.desktop`]
}

// must be name without spaces and other special characters, but not product name used
private async computeDesktopIconPath(tempDir: string): Promise<Array<string>> {
const outputs = await exec("icns2png", ["-x", "-o", tempDir, path.join(this.buildResourcesDir, "icon.icns")])
log(outputs[0].toString())
if (!outputs[0].toString().includes("ih32")) {
log("48x48 is not found in the icns, 128x128 or 256x256 will be resized")

const gm = require("gm")

// icns doesn't contain required 48x48, use gm to resize
function resize(imagePath: string, size: number): BluebirdPromise<any> {
return new BluebirdPromise((resolve, reject) => {
(<Gm>gm(imagePath))
.resize(size, size)
.write(path.join(tempDir, `icon_${size}x${size}x32.png`), error => error == null ? resolve() : reject(error))
})
try {
const mappings: Array<string> = []
const pngIconsDir = path.join(this.buildResourcesDir, "icons")
for (let file of (await readdir(pngIconsDir))) {
if (file.endsWith(".png") || file.endsWith(".PNG")) {
// If parseInt encounters a character that is not a numeral in the specified radix,
// it returns the integer value parsed up to that point
try {
const size = parseInt(file, 10)
if (size > 0) {
mappings.push(`${pngIconsDir}/${file}=/usr/share/icons/hicolor/${size}x${size}/apps/${this.metadata.name}.png`)
}
}
catch (e) {
console.error(e)
}
}
}

let imagePath = path.join(tempDir, "icon_128x128x32.png")
try {
await stat(imagePath)
}
catch (e) {
imagePath = path.join(tempDir, "icon_256x256x32.png")
// 128 should be in any case
await resize(imagePath, 128)
}
await resize(imagePath, 48)
return mappings
}
catch (e) {
return this.createFromIcns(tempDir)
}
}

private async createFromIcns(tempDir: string): Promise<Array<string>> {
const outputs = await exec("icns2png", ["-x", "-o", tempDir, path.join(this.buildResourcesDir, "icon.icns")])
const output = outputs[0].toString()
log(output)

const gm = require("gm")

const imagePath = path.join(tempDir, "icon_256x256x32.png")

function resize(size: number): BluebirdPromise<any> {
return new BluebirdPromise((resolve, reject) => {
(<Gm>gm(imagePath))
.resize(size, size)
.write(path.join(tempDir, `icon_${size}x${size}x32.png`), error => error == null ? resolve() : reject(error))
})
}

const promises: Array<Promise<any>> = [resize(24), resize(96)]
if (!output.includes("ih32")) {
promises.push(resize(48))
}
if (!output.toString().includes("icp6")) {
promises.push(resize(64))
}
if (!output.includes("it32")) {
promises.push(resize(128))
}

await BluebirdPromise.all(promises)

const name = this.metadata.name
const appName = this.metadata.name

function createMapping(size: string) {
return `${tempDir}/icon_${size}x${size}x32.png=/usr/share/icons/hicolor/${size}x${size}/apps/${name}.png`
return `${tempDir}/icon_${size}x${size}x32.png=/usr/share/icons/hicolor/${size}x${size}/apps/${appName}.png`
}

return [
createMapping("16"),
createMapping("24"),
createMapping("32"),
createMapping("48"),
createMapping("64"),
createMapping("96"),
createMapping("128"),
createMapping("256"),
createMapping("512"),
Expand Down Expand Up @@ -163,7 +195,7 @@ Icon=${this.metadata.name}
async function writeConfigFile(tempDir: string, templatePath: string, options: any): Promise<string> {
const config = template(await readFile(templatePath, "utf8"),
{
// set interpolate explicitely to avoid troubles with templating of installer.nsi.tpl
// set interpolate explicitly to avoid troubles with templating of installer.nsi.tpl
interpolate: /<%=([\s\S]+?)%>/g
})(options)

Expand Down
4 changes: 3 additions & 1 deletion src/metadata.ts
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export interface AppMetadata extends Metadata {
readonly version: string

/** The application name */
/**
* The application name
**/
readonly name: string

/**
Expand Down
2 changes: 1 addition & 1 deletion test/README.md
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ brew install Caskroom/cask/xquartz wine mono
Do not use OS X bundled Ruby. Install using `brew`.

```
brew install ruby gnu-tar dpkg libicns
brew install ruby gnu-tar dpkg libicns graphicsmagick
gem install fpm
```

Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/no-author-email/package.json
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"description": "Test Application",
"author": "Foo Bar",
"devDependencies": {
"electron-prebuilt": "^0.37.1"
"electron-prebuilt": "^0.37.2"
},
"build": {
"app-bundle-id": "your.id",
Expand Down
Binary file modified test/fixtures/test-app-one/build/background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions test/fixtures/test-app-one/build/icons/128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions test/fixtures/test-app-one/build/icons/16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions test/fixtures/test-app-one/build/icons/24x24.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions test/fixtures/test-app-one/build/icons/256x256.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions test/fixtures/test-app-one/build/icons/32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions test/fixtures/test-app-one/build/icons/48x48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 7ac4b84

Please sign in to comment.