Skip to content

Commit 7ac4b84

Browse files
committed
feat: linux icons from custom dir, generate missing from ICNS
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`
1 parent c778e2b commit 7ac4b84

34 files changed

+195
-110
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.png filter=lfs diff=lfs merge=lfs -text

.idea/dictionaries/develar.xml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.travis.yml

100644100755
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ addons:
1919
apt:
2020
packages:
2121
- icnsutils
22+
- graphicsmagick
2223

2324
before_install:
2425
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
25-
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install gnu-tar dpkg libicns; fi
26+
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install gnu-tar dpkg libicns graphicsmagick git-lfs; fi
27+
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then git lfs pull; fi
2628
- gem install --no-rdoc --no-ri fpm
2729

2830
install:
@@ -31,7 +33,6 @@ install:
3133
- source ~/.nvm/nvm.sh
3234
- nvm install $NODE_VERSION
3335
- npm install npm -g
34-
- npm -v
3536
- npm prune
3637
- npm install
3738

README.md

100644100755
Lines changed: 16 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,27 @@ Why the two package.json structure is ideal and how it solves a lot of issues
3434
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).
3535

3636
# Configuration
37+
38+
See [options](./docs/options.md), but consider to follow simple 4-step guide outlined below at first.
39+
3740
## In short
38-
1. Ensure that required fields are specified in the application `package.json`:
41+
1. Ensure that required fields are specified in the application `package.json`:
3942

40-
Standard `name`, `description`, `version` and `author`.
43+
Standard `name`, `description`, `version` and `author`.
4144

42-
Custom `build` field must be specified:
43-
```json
44-
"build": {
45-
"app-bundle-id": "your.id",
46-
"app-category-type": "your.app.category.type",
47-
"iconUrl": "(windows only) A URL to an ICO file to use as the application icon, see details below"
48-
}
49-
```
50-
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.
45+
Custom `build` field must be specified:
46+
```json
47+
"build": {
48+
"app-bundle-id": "your.id",
49+
"app-category-type": "your.app.category.type",
50+
"iconUrl": "(windows only) A URL to an ICO file to use as the application icon, see details below"
51+
}
52+
```
53+
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.
5154

5255
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).
53-
Linux icon set will be generated automatically on the fly from the OS X `icns` file.
56+
57+
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`)).
5458

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

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

67-
## iconUrl
68-
Please note — [local icon file url is not accepted](https://github.com/atom/grunt-electron-installer/issues/73), must be https/http.
69-
* If you don't plan to build windows installer, you can omit it.
70-
* 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.
71-
72-
## Distributable Format Configuration
73-
In the development `package.json` custom `build` field can be specified to customize distributable format:
74-
```json
75-
"build": {
76-
"osx": {
77-
"title": "computed name from app package.js, you can overwrite",
78-
"icon": "build/icon.icns",
79-
"icon-size": 80,
80-
"background": "build/background.png",
81-
"contents": [
82-
{
83-
"x": 410,
84-
"y": 220,
85-
"type": "link",
86-
"path": "/Applications"
87-
},
88-
{
89-
"x": 130,
90-
"y": 220,
91-
"type": "file",
92-
"path": "computed path to artifact, do not specify it - will be overwritten"
93-
}
94-
]
95-
},
96-
"win": "see https://github.com/electronjs/windows-installer#usage"
97-
}
98-
```
99-
100-
As you can see, you need to customize OS X options only if you want to provide custom `x, y`.
101-
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).
102-
103-
See [OS X options](https://www.npmjs.com/package/appdmg#json-specification) and [Windows options](https://github.com/electronjs/windows-installer#usage).
104-
10571
# Auto Update
10672
`electron-builder` produces all required artifacts:
10773

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ cache:
77
- '%USERPROFILE%\.electron'
88

99
init:
10-
- git config --global core.autocrlf input
10+
- git config --global core.autocrlf false
1111

1212
install:
1313
- choco install nsis -y

docs/multi-platform-build.md

100644100755
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ brew install Caskroom/cask/xquartz wine mono
2424

2525
To build app in distributable format for Linux on OS X:
2626
```
27-
brew install ruby gnu-tar libicns imagemagick graphicsmagick
27+
brew install ruby gnu-tar libicns graphicsmagick
2828
gem install fpm
2929
```
3030

docs/options.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Options
2+
3+
In the development `package.json` custom `build` field can be specified to customize format:
4+
```json
5+
"build": {
6+
"osx": {
7+
"title": "computed name from the app package.js, you can overwrite",
8+
"icon": "build/icon.icns",
9+
"icon-size": 80,
10+
"background": "build/background.png",
11+
"contents": [
12+
{
13+
"x": 410,
14+
"y": 220,
15+
"type": "link",
16+
"path": "/Applications"
17+
},
18+
{
19+
"x": 130,
20+
"y": 220,
21+
"type": "file",
22+
"path": "computed path to artifact, do not specify it - will be overwritten"
23+
}
24+
]
25+
},
26+
"win": "see https://github.com/electronjs/windows-installer#usage"
27+
}
28+
```
29+
30+
As you can see, you need to customize OS X options only if you want to provide custom `x, y`.
31+
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).
32+
33+
See [OS X options](https://www.npmjs.com/package/appdmg#json-specification) and [Windows options](https://github.com/electronjs/windows-installer#usage).
34+
35+
Here documented only `electron-builder` specific options:
36+
37+
| Name | Description
38+
| --- | ---
39+
| <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>
40+
| <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`).

package.json

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"test": "node ./test/out/helpers/runTests.js",
1313
"test-nix": "tape ./lib/*.spec.js ./lib/**/*.spec.js && ava",
1414
"test-win": "ava",
15-
"semantic-release": "semantic-release pre && npm publish && semantic-release post"
15+
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
16+
"validate-commit-msg": "validate-commit-msg"
1617
},
1718
"repository": {
1819
"type": "git",
@@ -53,8 +54,8 @@
5354
"bluebird": "^3.3.4",
5455
"command-line-args": "^2.1.6",
5556
"electron-packager-tf": "^5.2.3",
56-
"electron-winstaller-fixed": "^2.0.5-beta.7",
57-
"fs-extra": "^0.26.5",
57+
"electron-winstaller-fixed": "^2.0.6-beta.1",
58+
"fs-extra": "^0.26.7",
5859
"fs-extra-p": "^0.1.0",
5960
"globby": "^4.0.0",
6061
"gm": "^1.21.1",
@@ -78,7 +79,7 @@
7879
"electron-download": "^2.1.0",
7980
"eslint": "^2.4.0",
8081
"eslint-plugin-ava": "sindresorhus/eslint-plugin-ava",
81-
"ghooks": "^1.0.3",
82+
"pre-commit": "^1.1.2",
8283
"json-parse-helpfulerror": "^1.0.3",
8384
"path-sort": "^0.1.0",
8485
"plist": "^1.2.0",
@@ -108,11 +109,9 @@
108109
]
109110
},
110111
"typings": "./out/electron-builder.d.ts",
111-
"config": {
112-
"ghooks": {
113-
"commit-msg": "validate-commit-msg"
114-
}
115-
},
112+
"precommit": [
113+
"validate-commit-msg"
114+
],
116115
"publishConfig": {
117116
"tag": "next"
118117
}

src/linuxPackager.ts

100644100755
Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { PlatformPackager, BuildInfo } from "./platformPackager"
44
import { Platform } from "./metadata"
55
import { dir as _tpmDir, TmpOptions } from "tmp"
66
import { exec, log } from "./util"
7+
import { outputFile, readFile, readdir } from "fs-extra-p"
78
import { State as Gm } from "gm"
8-
import { outputFile, readFile, stat } from "fs-extra-p"
99
const template = require("lodash.template")
1010

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

70+
// must be name without spaces and other special characters, but not product name used
7071
private async computeDesktopIconPath(tempDir: string): Promise<Array<string>> {
71-
const outputs = await exec("icns2png", ["-x", "-o", tempDir, path.join(this.buildResourcesDir, "icon.icns")])
72-
log(outputs[0].toString())
73-
if (!outputs[0].toString().includes("ih32")) {
74-
log("48x48 is not found in the icns, 128x128 or 256x256 will be resized")
75-
76-
const gm = require("gm")
77-
78-
// icns doesn't contain required 48x48, use gm to resize
79-
function resize(imagePath: string, size: number): BluebirdPromise<any> {
80-
return new BluebirdPromise((resolve, reject) => {
81-
(<Gm>gm(imagePath))
82-
.resize(size, size)
83-
.write(path.join(tempDir, `icon_${size}x${size}x32.png`), error => error == null ? resolve() : reject(error))
84-
})
72+
try {
73+
const mappings: Array<string> = []
74+
const pngIconsDir = path.join(this.buildResourcesDir, "icons")
75+
for (let file of (await readdir(pngIconsDir))) {
76+
if (file.endsWith(".png") || file.endsWith(".PNG")) {
77+
// If parseInt encounters a character that is not a numeral in the specified radix,
78+
// it returns the integer value parsed up to that point
79+
try {
80+
const size = parseInt(file, 10)
81+
if (size > 0) {
82+
mappings.push(`${pngIconsDir}/${file}=/usr/share/icons/hicolor/${size}x${size}/apps/${this.metadata.name}.png`)
83+
}
84+
}
85+
catch (e) {
86+
console.error(e)
87+
}
88+
}
8589
}
8690

87-
let imagePath = path.join(tempDir, "icon_128x128x32.png")
88-
try {
89-
await stat(imagePath)
90-
}
91-
catch (e) {
92-
imagePath = path.join(tempDir, "icon_256x256x32.png")
93-
// 128 should be in any case
94-
await resize(imagePath, 128)
95-
}
96-
await resize(imagePath, 48)
91+
return mappings
9792
}
93+
catch (e) {
94+
return this.createFromIcns(tempDir)
95+
}
96+
}
97+
98+
private async createFromIcns(tempDir: string): Promise<Array<string>> {
99+
const outputs = await exec("icns2png", ["-x", "-o", tempDir, path.join(this.buildResourcesDir, "icon.icns")])
100+
const output = outputs[0].toString()
101+
log(output)
102+
103+
const gm = require("gm")
104+
105+
const imagePath = path.join(tempDir, "icon_256x256x32.png")
106+
107+
function resize(size: number): BluebirdPromise<any> {
108+
return new BluebirdPromise((resolve, reject) => {
109+
(<Gm>gm(imagePath))
110+
.resize(size, size)
111+
.write(path.join(tempDir, `icon_${size}x${size}x32.png`), error => error == null ? resolve() : reject(error))
112+
})
113+
}
114+
115+
const promises: Array<Promise<any>> = [resize(24), resize(96)]
116+
if (!output.includes("ih32")) {
117+
promises.push(resize(48))
118+
}
119+
if (!output.toString().includes("icp6")) {
120+
promises.push(resize(64))
121+
}
122+
if (!output.includes("it32")) {
123+
promises.push(resize(128))
124+
}
125+
126+
await BluebirdPromise.all(promises)
98127

99-
const name = this.metadata.name
128+
const appName = this.metadata.name
100129

101130
function createMapping(size: string) {
102-
return `${tempDir}/icon_${size}x${size}x32.png=/usr/share/icons/hicolor/${size}x${size}/apps/${name}.png`
131+
return `${tempDir}/icon_${size}x${size}x32.png=/usr/share/icons/hicolor/${size}x${size}/apps/${appName}.png`
103132
}
104133

105134
return [
106135
createMapping("16"),
136+
createMapping("24"),
107137
createMapping("32"),
108138
createMapping("48"),
139+
createMapping("64"),
140+
createMapping("96"),
109141
createMapping("128"),
110142
createMapping("256"),
111143
createMapping("512"),
@@ -163,7 +195,7 @@ Icon=${this.metadata.name}
163195
async function writeConfigFile(tempDir: string, templatePath: string, options: any): Promise<string> {
164196
const config = template(await readFile(templatePath, "utf8"),
165197
{
166-
// set interpolate explicitely to avoid troubles with templating of installer.nsi.tpl
198+
// set interpolate explicitly to avoid troubles with templating of installer.nsi.tpl
167199
interpolate: /<%=([\s\S]+?)%>/g
168200
})(options)
169201

src/metadata.ts

100644100755
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
export interface AppMetadata extends Metadata {
22
readonly version: string
33

4-
/** The application name */
4+
/**
5+
* The application name
6+
**/
57
readonly name: string
68

79
/**

0 commit comments

Comments
 (0)