@vscode/gulp-electron 1.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -0
- package/azure-pipelines.yml +40 -0
- package/package.json +52 -0
- package/src/darwin.js +410 -0
- package/src/download.js +205 -0
- package/src/index.js +146 -0
- package/src/linux.js +64 -0
- package/src/win32.js +152 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
**Deprecated**
|
|
2
|
+
|
|
3
|
+
Please use the official way of requiring Electron: https://electronjs.org/docs/tutorial/first-app
|
|
4
|
+
|
|
5
|
+
--
|
|
6
|
+
|
|
7
|
+
# @vscode/gulp-electron
|
|
8
|
+
|
|
9
|
+
### Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install --save-dev @vscode/gulp-electron
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Usage
|
|
16
|
+
|
|
17
|
+
You can use this module in two distinct ways: **to package your application** and/or
|
|
18
|
+
**to download a version of Electron** to disk.
|
|
19
|
+
|
|
20
|
+
#### How to Package Your Application
|
|
21
|
+
|
|
22
|
+
You should source your app's files using `gulp.src` and pipe them through
|
|
23
|
+
`@vscode/gulp-electron`. The following task will create your application in
|
|
24
|
+
the `app` folder, ready for launch.
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
var gulp = require("gulp");
|
|
28
|
+
var symdest = require("gulp-symdest");
|
|
29
|
+
var electron = require("@vscode/gulp-electron");
|
|
30
|
+
|
|
31
|
+
gulp.task("default", function () {
|
|
32
|
+
return gulp
|
|
33
|
+
.src("src/**")
|
|
34
|
+
.pipe(electron({ version: "0.34.1", platform: "darwin" }))
|
|
35
|
+
.pipe(symdest("app"));
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Note:** It is important to use `gulp-symdest` only because of the OS X
|
|
40
|
+
platform. An application bundle has symlinks within and if you use `gulp.dest`
|
|
41
|
+
to pipe the built app to disk, those will be missing. `symdest` will make
|
|
42
|
+
sure symlinks are taken into account.
|
|
43
|
+
|
|
44
|
+
Finally, you can always pipe it to a **zip archive** for easy distribution.
|
|
45
|
+
[joaomoreno/gulp-vinyl-zip](https://github.com/joaomoreno/gulp-vinyl-zip) is recommended:
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
var gulp = require("gulp");
|
|
49
|
+
var zip = require("gulp-vinyl-zip");
|
|
50
|
+
var electron = require("@vscode/gulp-electron");
|
|
51
|
+
|
|
52
|
+
gulp.task("default", function () {
|
|
53
|
+
return gulp
|
|
54
|
+
.src("src/**")
|
|
55
|
+
.pipe(electron({ version: "0.34.1", platform: "darwin" }))
|
|
56
|
+
.pipe(zip.dest("app-darwin.zip"));
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### How to Download Electron
|
|
61
|
+
|
|
62
|
+
There's also a very handy export `electron.dest()` function that
|
|
63
|
+
makes sure you always have the exact version of Electron in a directory:
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
var gulp = require("gulp");
|
|
67
|
+
var electron = require("@vscode/gulp-electron");
|
|
68
|
+
|
|
69
|
+
gulp.task("default", function () {
|
|
70
|
+
return electron.dest("electron-build", {
|
|
71
|
+
version: "0.34.1",
|
|
72
|
+
platform: "darwin",
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This will place a vanilla Electron build into the `electron-build` directory.
|
|
78
|
+
If you run it consecutively and it detects that the version in the destination directory
|
|
79
|
+
is the intended one, it will end up in a no-op. Else it will download the provided version
|
|
80
|
+
and replace it.
|
|
81
|
+
|
|
82
|
+
### Options
|
|
83
|
+
|
|
84
|
+
You **must** provide the following options:
|
|
85
|
+
|
|
86
|
+
- `version` - the [Electron version](https://github.com/atom/electron/releases) to use
|
|
87
|
+
- `platform` - kind of OS (`darwin`, `linux`, `win32`)
|
|
88
|
+
|
|
89
|
+
The following options are **optional**:
|
|
90
|
+
|
|
91
|
+
- `quiet` - suppress a progress bar when downloading
|
|
92
|
+
- `token` - GitHub access token(to avoid request limit. You can grab it [here](https://github.com/settings/tokens))
|
|
93
|
+
|
|
94
|
+
- `arch` - the processor architecture (`ia32`, `x64`)
|
|
95
|
+
|
|
96
|
+
- **Windows**
|
|
97
|
+
|
|
98
|
+
- `winIcon` - path to an `.ico` file
|
|
99
|
+
- `companyName` - company name
|
|
100
|
+
- `copyright` - copyright statement
|
|
101
|
+
|
|
102
|
+
- **Darwin**
|
|
103
|
+
|
|
104
|
+
- `darwinIcon` - path to an `.icns` file
|
|
105
|
+
- `darwinHelpBookFolder` - the `CFBundleHelpBookFolder` value
|
|
106
|
+
- `darwinHelpBookName` - the `CFBundleHelpBookName` value
|
|
107
|
+
- `darwinBundleDocumentTypes` - ([reference](https://developer.apple.com/library/ios/documentation/filemanagement/conceptual/documentinteraction_topicsforios/Articles/RegisteringtheFileTypesYourAppSupports.html)) array of dictionaries, each containing the following structure:
|
|
108
|
+
- `name` - the `CFBundleTypeName` value
|
|
109
|
+
- `role` - the `CFBundleTypeRole` value
|
|
110
|
+
- `ostypes` - the `CFBundleTypeOSTypes` value, a `string` array
|
|
111
|
+
- `utis` - the `LSItemContentTypes` value, a `string` array
|
|
112
|
+
- `extensions` - the `CFBundleTypeExtensions` value, a `string` array of file extensions
|
|
113
|
+
- `iconFile` - the `CFBundleTypeIconFile` value
|
|
114
|
+
- `darwinForceDarkModeSupport` - Forces Mojave dark mode support to be enabled for older Electron versions
|
|
115
|
+
|
|
116
|
+
- **Linux**
|
|
117
|
+
- `linuxExecutableName` - overwrite the name of the executable in Linux
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
trigger:
|
|
2
|
+
batch: true
|
|
3
|
+
branches:
|
|
4
|
+
include:
|
|
5
|
+
- main
|
|
6
|
+
|
|
7
|
+
pr: [main]
|
|
8
|
+
|
|
9
|
+
resources:
|
|
10
|
+
repositories:
|
|
11
|
+
- repository: templates
|
|
12
|
+
type: github
|
|
13
|
+
name: microsoft/vscode-engineering
|
|
14
|
+
ref: main
|
|
15
|
+
endpoint: Monaco
|
|
16
|
+
|
|
17
|
+
parameters:
|
|
18
|
+
- name: publishPackage
|
|
19
|
+
displayName: 🚀 Publish
|
|
20
|
+
type: boolean
|
|
21
|
+
default: false
|
|
22
|
+
|
|
23
|
+
extends:
|
|
24
|
+
template: azure-pipelines/npm-package/pipeline.yml@templates
|
|
25
|
+
parameters:
|
|
26
|
+
npmPackages:
|
|
27
|
+
- name: gulp-electron
|
|
28
|
+
buildSteps:
|
|
29
|
+
- script: npm ci
|
|
30
|
+
testPlatforms:
|
|
31
|
+
- name: Linux
|
|
32
|
+
nodeVersions: [14.x, 16.x, 17.x]
|
|
33
|
+
- name: MacOS
|
|
34
|
+
nodeVersions: [14.x, 16.x, 17.x]
|
|
35
|
+
- name: Windows
|
|
36
|
+
nodeVersions: [14.x, 16.x, 17.x]
|
|
37
|
+
testSteps:
|
|
38
|
+
- script: npm ci
|
|
39
|
+
- script: npm test
|
|
40
|
+
publishPackage: ${{ parameters.publishPackage }}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vscode/gulp-electron",
|
|
3
|
+
"version": "1.33.0",
|
|
4
|
+
"description": "gulp plugin for packaging Electron into VS Code",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "mocha"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/microsoft/vscode-gulp-electron.git"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=10"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"gulpplugin",
|
|
18
|
+
"atom",
|
|
19
|
+
"shell",
|
|
20
|
+
"atom-shell",
|
|
21
|
+
"electron",
|
|
22
|
+
"github"
|
|
23
|
+
],
|
|
24
|
+
"author": "João Moreno",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/microsoft/vscode-gulp-electron/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/microsoft/vscode-gulp-electron",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@electron/get": "^2.0.2",
|
|
32
|
+
"@octokit/rest": "^18.0.14",
|
|
33
|
+
"event-stream": "3.3.4",
|
|
34
|
+
"gulp-filter": "^5.1.0",
|
|
35
|
+
"gulp-rename": "1.2.2",
|
|
36
|
+
"gulp-symdest": "^1.2.0",
|
|
37
|
+
"gulp-vinyl-zip": "^2.1.2",
|
|
38
|
+
"mkdirp": "^0.5.1",
|
|
39
|
+
"plist": "^3.0.1",
|
|
40
|
+
"progress": "^1.1.8",
|
|
41
|
+
"rcedit": "^0.3.0",
|
|
42
|
+
"rimraf": "^2.4.2",
|
|
43
|
+
"semver": "^4.3.4",
|
|
44
|
+
"temp": "^0.8.3",
|
|
45
|
+
"vinyl": "^2.2.0",
|
|
46
|
+
"vinyl-fs": "^3.0.3"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"gulp-buffer": "0.0.2",
|
|
50
|
+
"mocha": "^9.2.2"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/darwin.js
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var path = require("path");
|
|
4
|
+
var plist = require("plist");
|
|
5
|
+
var es = require("event-stream");
|
|
6
|
+
var vfs = require("vinyl-fs");
|
|
7
|
+
var File = require("vinyl");
|
|
8
|
+
var rename = require("gulp-rename");
|
|
9
|
+
var semver = require("semver");
|
|
10
|
+
|
|
11
|
+
function getOriginalAppName(opts) {
|
|
12
|
+
return semver.gte(opts.version, "0.24.0") ? "Electron" : "Atom";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getOriginalAppFullName(opts) {
|
|
16
|
+
return getOriginalAppName(opts) + ".app";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getAppName(opts) {
|
|
20
|
+
return (opts.productAppName || opts.productName) + ".app";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
exports.getAppPath = function (opts) {
|
|
24
|
+
return getAppName(opts) + "/Contents/Resources/app";
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function removeDefaultApp(opts) {
|
|
28
|
+
var defaultAppPath = path.join(
|
|
29
|
+
getOriginalAppFullName(opts),
|
|
30
|
+
"Contents",
|
|
31
|
+
"Resources",
|
|
32
|
+
"default_app"
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return es.mapSync(function (f) {
|
|
36
|
+
if (!f.relative.startsWith(defaultAppPath)) {
|
|
37
|
+
return f;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function patchIcon(opts) {
|
|
43
|
+
if (!opts.darwinIcon) {
|
|
44
|
+
return es.through();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
var resourcesPath = path.join(
|
|
48
|
+
getOriginalAppFullName(opts),
|
|
49
|
+
"Contents",
|
|
50
|
+
"Resources"
|
|
51
|
+
);
|
|
52
|
+
var iconName = semver.gte(opts.version, "0.24.0")
|
|
53
|
+
? "electron.icns"
|
|
54
|
+
: "atom.icns";
|
|
55
|
+
var originalIconPath = path.join(resourcesPath, iconName);
|
|
56
|
+
var iconPath = path.join(resourcesPath, opts.productName + ".icns");
|
|
57
|
+
var pass = es.through();
|
|
58
|
+
|
|
59
|
+
// filter out original icon
|
|
60
|
+
var src = pass.pipe(
|
|
61
|
+
es.mapSync(function (f) {
|
|
62
|
+
if (f.relative !== originalIconPath) {
|
|
63
|
+
return f;
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// add custom icon
|
|
69
|
+
var icon = vfs.src(opts.darwinIcon).pipe(rename(iconPath));
|
|
70
|
+
|
|
71
|
+
return es.duplex(pass, es.merge(src, icon));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function patchInfoPlist(opts) {
|
|
75
|
+
var contentsPath = path.join(getOriginalAppFullName(opts), "Contents");
|
|
76
|
+
var resourcesPath = path.join(contentsPath, "Resources");
|
|
77
|
+
var infoPlistPath = path.join(contentsPath, "Info.plist");
|
|
78
|
+
var didCloseIcons = false;
|
|
79
|
+
|
|
80
|
+
var icons = es.through();
|
|
81
|
+
var input = es.through();
|
|
82
|
+
var output = input.pipe(
|
|
83
|
+
es.map(function (f, cb) {
|
|
84
|
+
if (f.relative !== infoPlistPath) {
|
|
85
|
+
return cb(null, f);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
var contents = "";
|
|
89
|
+
|
|
90
|
+
f.contents.on("error", function (err) {
|
|
91
|
+
cb(err);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
f.contents.on("data", function (d) {
|
|
95
|
+
contents += d;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
f.contents.on("end", function () {
|
|
99
|
+
var infoPlist = plist.parse(contents.toString("utf8"));
|
|
100
|
+
|
|
101
|
+
opts.darwinBundleIdentifier &&
|
|
102
|
+
(infoPlist["CFBundleIdentifier"] = opts.darwinBundleIdentifier);
|
|
103
|
+
opts.darwinApplicationCategoryType &&
|
|
104
|
+
(infoPlist["LSApplicationCategoryType"] =
|
|
105
|
+
opts.darwinApplicationCategoryType);
|
|
106
|
+
infoPlist["CFBundleName"] = opts.productName;
|
|
107
|
+
infoPlist["CFBundleDisplayName"] =
|
|
108
|
+
opts.productDisplayName || opts.productName;
|
|
109
|
+
infoPlist["CFBundleVersion"] = opts.productVersion;
|
|
110
|
+
infoPlist["CFBundleShortVersionString"] = opts.productVersion;
|
|
111
|
+
opts.copyright &&
|
|
112
|
+
(infoPlist["NSHumanReadableCopyright"] = opts.copyright);
|
|
113
|
+
infoPlist["CFBundleIconFile"] = opts.productName + ".icns";
|
|
114
|
+
|
|
115
|
+
if (opts.darwinExecutable) {
|
|
116
|
+
infoPlist["CFBundleExecutable"] = opts.darwinExecutable;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
//Register the Application Help Book if it exists
|
|
120
|
+
if (opts.darwinHelpBookFolder && opts.darwinHelpBookName) {
|
|
121
|
+
infoPlist["CFBundleHelpBookFolder"] = opts.darwinHelpBookFolder;
|
|
122
|
+
infoPlist["CFBundleHelpBookName"] = opts.darwinHelpBookName;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (opts.darwinBundleDocumentTypes) {
|
|
126
|
+
var iconsPaths = [];
|
|
127
|
+
|
|
128
|
+
infoPlist["CFBundleDocumentTypes"] = (
|
|
129
|
+
infoPlist["CFBundleDocumentTypes"] || []
|
|
130
|
+
).concat(
|
|
131
|
+
opts.darwinBundleDocumentTypes.map(function (type) {
|
|
132
|
+
iconsPaths.push(type.iconFile);
|
|
133
|
+
|
|
134
|
+
var result = {
|
|
135
|
+
CFBundleTypeName: type.name,
|
|
136
|
+
CFBundleTypeRole: type.role,
|
|
137
|
+
CFBundleTypeOSTypes: type.ostypes,
|
|
138
|
+
CFBundleTypeExtensions: type.extensions,
|
|
139
|
+
CFBundleTypeIconFile: path.basename(type.iconFile),
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
if (type.utis) {
|
|
143
|
+
result["LSItemContentTypes"] = type.utis;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return result;
|
|
147
|
+
})
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
if (iconsPaths.length) {
|
|
151
|
+
didCloseIcons = true;
|
|
152
|
+
es.merge(
|
|
153
|
+
iconsPaths.map(function (iconPath) {
|
|
154
|
+
return vfs.src(iconPath).pipe(
|
|
155
|
+
rename(function (path) {
|
|
156
|
+
path.dirname = resourcesPath;
|
|
157
|
+
})
|
|
158
|
+
);
|
|
159
|
+
})
|
|
160
|
+
).pipe(icons);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (opts.darwinBundleURLTypes) {
|
|
165
|
+
infoPlist["CFBundleURLTypes"] = opts.darwinBundleURLTypes.map(
|
|
166
|
+
function (type) {
|
|
167
|
+
return {
|
|
168
|
+
CFBundleTypeRole: type.role,
|
|
169
|
+
CFBundleURLName: type.name,
|
|
170
|
+
CFBundleURLSchemes: type.urlSchemes,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (opts.darwinForceDarkModeSupport) {
|
|
177
|
+
infoPlist["NSRequiresAquaSystemAppearance"] = false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
f.contents = Buffer.from(plist.build(infoPlist), "utf8");
|
|
181
|
+
cb(null, f);
|
|
182
|
+
});
|
|
183
|
+
}))
|
|
184
|
+
.pipe(
|
|
185
|
+
es.through(
|
|
186
|
+
null,
|
|
187
|
+
function () {
|
|
188
|
+
if (!didCloseIcons) {
|
|
189
|
+
es.readArray([]).pipe(icons);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
this.emit("end");
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
return es.duplex(input, es.merge(output, icons));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function createEntitlementsPlist(opts) {
|
|
201
|
+
var input = es.through();
|
|
202
|
+
if (!opts.darwinEntitlements) {
|
|
203
|
+
return input;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
var contentsPath = path.join(getOriginalAppFullName(opts), "Contents");
|
|
207
|
+
var entitlementsPlistPath = path.join(contentsPath, "Entitlements.plist");
|
|
208
|
+
|
|
209
|
+
var result = {};
|
|
210
|
+
opts.darwinEntitlements.forEach((element) => {
|
|
211
|
+
result[element] = true;
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
var entitlementsFile = new File({
|
|
215
|
+
path: entitlementsPlistPath,
|
|
216
|
+
contents: Buffer.from(plist.build(result)),
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
return es.duplex(input, es.merge(input, es.readArray([entitlementsFile])));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function patchHelperInfoPlist(opts) {
|
|
223
|
+
var didCloseIcons = false;
|
|
224
|
+
|
|
225
|
+
var icons = es.through();
|
|
226
|
+
var input = es.through();
|
|
227
|
+
var output = input.pipe(
|
|
228
|
+
es.map(function (f, cb) {
|
|
229
|
+
if (
|
|
230
|
+
!/Contents\/Frameworks\/Electron\ Helper( \w+)?\.app\/Contents\/Info.plist$/i.test(
|
|
231
|
+
f.relative
|
|
232
|
+
)
|
|
233
|
+
) {
|
|
234
|
+
return cb(null, f);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
var contents = "";
|
|
238
|
+
|
|
239
|
+
f.contents.on("error", function (err) {
|
|
240
|
+
cb(err);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
f.contents.on("data", function (d) {
|
|
244
|
+
contents += d;
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
f.contents.on("end", function () {
|
|
248
|
+
var infoPlist = plist.parse(contents.toString("utf8"));
|
|
249
|
+
var match = /\.helper\.([^.]+)$/.exec(
|
|
250
|
+
infoPlist["CFBundleIdentifier"] || ""
|
|
251
|
+
);
|
|
252
|
+
var suffix = match ? match[1] : "";
|
|
253
|
+
|
|
254
|
+
if (opts.darwinBundleIdentifier) {
|
|
255
|
+
infoPlist["CFBundleIdentifier"] =
|
|
256
|
+
opts.darwinBundleIdentifier + ".helper";
|
|
257
|
+
|
|
258
|
+
if (suffix) {
|
|
259
|
+
infoPlist["CFBundleIdentifier"] += "." + suffix;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
infoPlist["CFBundleName"] = opts.productName + " Helper";
|
|
264
|
+
if (suffix) {
|
|
265
|
+
infoPlist["CFBundleName"] += " " + suffix;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (infoPlist["CFBundleDisplayName"]) {
|
|
269
|
+
infoPlist["CFBundleDisplayName"] = infoPlist["CFBundleName"];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (infoPlist["CFBundleExecutable"]) {
|
|
273
|
+
infoPlist["CFBundleExecutable"] = infoPlist["CFBundleName"];
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
f.contents = Buffer.from(plist.build(infoPlist), "utf8");
|
|
277
|
+
cb(null, f);
|
|
278
|
+
});
|
|
279
|
+
}))
|
|
280
|
+
.pipe(
|
|
281
|
+
es.through(
|
|
282
|
+
null,
|
|
283
|
+
function () {
|
|
284
|
+
if (!didCloseIcons) {
|
|
285
|
+
es.readArray([]).pipe(icons);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
this.emit("end");
|
|
289
|
+
}
|
|
290
|
+
)
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
return es.duplex(input, es.merge(output, icons));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function addCredits(opts) {
|
|
297
|
+
if (!opts.darwinCredits) {
|
|
298
|
+
return es.through();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
var creditsPath = path.join(
|
|
302
|
+
getOriginalAppFullName(opts),
|
|
303
|
+
"Contents",
|
|
304
|
+
"Resources",
|
|
305
|
+
"Credits.rtf"
|
|
306
|
+
);
|
|
307
|
+
var input = es.through();
|
|
308
|
+
var credits;
|
|
309
|
+
|
|
310
|
+
if (typeof opts.darwinCredits === "string") {
|
|
311
|
+
credits = vfs.src(opts.darwinCredits).pipe(rename(creditsPath));
|
|
312
|
+
} else if (opts.darwinCredits instanceof Buffer) {
|
|
313
|
+
credits = es.readArray([
|
|
314
|
+
new File({
|
|
315
|
+
path: creditsPath,
|
|
316
|
+
contents: opts.darwinCredits,
|
|
317
|
+
}),
|
|
318
|
+
]);
|
|
319
|
+
} else {
|
|
320
|
+
throw new Error("Unexpected value for darwinCredits");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return es.duplex(input, es.merge(input, credits));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function renameApp(opts) {
|
|
327
|
+
var originalAppName = getOriginalAppName(opts);
|
|
328
|
+
var originalAppNameRegexp = new RegExp("^" + getOriginalAppFullName(opts));
|
|
329
|
+
var appName = getAppName(opts);
|
|
330
|
+
|
|
331
|
+
return rename(function (path) {
|
|
332
|
+
// The app folder itself looks like a file
|
|
333
|
+
if (
|
|
334
|
+
path.dirname === "." &&
|
|
335
|
+
path.basename === originalAppName &&
|
|
336
|
+
path.extname === ".app"
|
|
337
|
+
) {
|
|
338
|
+
path.basename = opts.productAppName || opts.productName;
|
|
339
|
+
} else {
|
|
340
|
+
path.dirname = path.dirname.replace(originalAppNameRegexp, appName);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (
|
|
344
|
+
/Contents\/MacOS$/.test(path.dirname) &&
|
|
345
|
+
path.basename === "Electron" &&
|
|
346
|
+
opts.darwinExecutable
|
|
347
|
+
) {
|
|
348
|
+
path.basename = opts.darwinExecutable;
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function renameAppHelper(opts) {
|
|
354
|
+
var originalAppName = getOriginalAppName(opts);
|
|
355
|
+
var originalAppNameRegexp = new RegExp("^" + getOriginalAppFullName(opts));
|
|
356
|
+
var appName = getAppName(opts);
|
|
357
|
+
var name = opts.productName;
|
|
358
|
+
|
|
359
|
+
return rename(function (path) {
|
|
360
|
+
var basenameMatch = /^Electron Helper( \(\w+\))?$/.exec(path.basename);
|
|
361
|
+
|
|
362
|
+
if (
|
|
363
|
+
/Contents\/Frameworks/.test(path.dirname) &&
|
|
364
|
+
path.extname === ".app" &&
|
|
365
|
+
basenameMatch
|
|
366
|
+
) {
|
|
367
|
+
var suffix = basenameMatch[1] || "";
|
|
368
|
+
path.basename = name + " Helper" + suffix;
|
|
369
|
+
} else if (
|
|
370
|
+
/Contents\/Frameworks\/Electron\ Helper( \(\w+\))?\.app/.test(
|
|
371
|
+
path.dirname
|
|
372
|
+
)
|
|
373
|
+
) {
|
|
374
|
+
var isInMacOS = /Contents\/Frameworks\/Electron\ Helper( \(\w+\))?\.app\/Contents\/MacOS$/.test(
|
|
375
|
+
path.dirname
|
|
376
|
+
);
|
|
377
|
+
path.dirname = path.dirname.replace(
|
|
378
|
+
/Electron\ Helper( \(\w+\))?\.app/,
|
|
379
|
+
name + " Helper$1.app"
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
if (
|
|
383
|
+
isInMacOS &&
|
|
384
|
+
/^Electron Helper( \(\w+\))?$/.test(path.basename) &&
|
|
385
|
+
path.extname === ""
|
|
386
|
+
) {
|
|
387
|
+
path.basename = path.basename.replace(
|
|
388
|
+
/Electron\ Helper( \(\w+\))?$/,
|
|
389
|
+
name + " Helper$1"
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
exports.patch = function (opts) {
|
|
397
|
+
var pass = es.through();
|
|
398
|
+
|
|
399
|
+
var src = pass
|
|
400
|
+
.pipe(opts.keepDefaultApp ? es.through() : removeDefaultApp(opts))
|
|
401
|
+
.pipe(patchIcon(opts))
|
|
402
|
+
.pipe(patchInfoPlist(opts))
|
|
403
|
+
.pipe(patchHelperInfoPlist(opts))
|
|
404
|
+
.pipe(createEntitlementsPlist(opts))
|
|
405
|
+
.pipe(addCredits(opts))
|
|
406
|
+
.pipe(renameApp(opts))
|
|
407
|
+
.pipe(renameAppHelper(opts));
|
|
408
|
+
|
|
409
|
+
return es.duplex(pass, src);
|
|
410
|
+
};
|
package/src/download.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var path = require("path");
|
|
4
|
+
const { downloadArtifact } = require("@electron/get");
|
|
5
|
+
const ProgressBar = require("progress");
|
|
6
|
+
var rename = require("gulp-rename");
|
|
7
|
+
var es = require("event-stream");
|
|
8
|
+
var zfs = require("gulp-vinyl-zip");
|
|
9
|
+
var filter = require("gulp-filter");
|
|
10
|
+
const { Octokit } = require("@octokit/rest");
|
|
11
|
+
const got = require("got");
|
|
12
|
+
|
|
13
|
+
async function getDownloadUrl(
|
|
14
|
+
ownerRepo,
|
|
15
|
+
{ version, platform, arch, token, artifactName, artifactSuffix }
|
|
16
|
+
) {
|
|
17
|
+
const [owner, repo] = ownerRepo.split("/");
|
|
18
|
+
const octokit = new Octokit({ auth: token });
|
|
19
|
+
const releaseVersion = version.startsWith("v") ? version : `v${version}`;
|
|
20
|
+
|
|
21
|
+
const { data: release } = await octokit.repos.getReleaseByTag({
|
|
22
|
+
owner,
|
|
23
|
+
repo,
|
|
24
|
+
tag: releaseVersion,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (!release) {
|
|
28
|
+
throw new Error(`Release for ${releaseVersion} not found`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { data: assets } = await octokit.repos.listReleaseAssets({
|
|
32
|
+
owner,
|
|
33
|
+
repo,
|
|
34
|
+
release_id: release.id,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
artifactName = artifactName || "electron";
|
|
38
|
+
|
|
39
|
+
const targetName = artifactSuffix ?
|
|
40
|
+
`${artifactName}-${releaseVersion}-${platform}-${arch}-${artifactSuffix}.zip` :
|
|
41
|
+
`${artifactName}-${releaseVersion}-${platform}-${arch}.zip`;
|
|
42
|
+
const asset = assets.find((asset) => {
|
|
43
|
+
return asset.name === targetName;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (!asset) {
|
|
47
|
+
throw new Error(`Release asset for ${releaseVersion} not found`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const requestOptions = await octokit.repos.getReleaseAsset.endpoint({
|
|
51
|
+
owner,
|
|
52
|
+
repo,
|
|
53
|
+
asset_id: asset.id,
|
|
54
|
+
headers: {
|
|
55
|
+
Accept: "application/octet-stream",
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const { url, headers } = requestOptions;
|
|
60
|
+
headers.authorization = `token ${token}`;
|
|
61
|
+
|
|
62
|
+
const response = await got(url, {
|
|
63
|
+
followRedirect: false,
|
|
64
|
+
method: "HEAD",
|
|
65
|
+
headers,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return response.headers.location;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function download(opts) {
|
|
72
|
+
let bar;
|
|
73
|
+
|
|
74
|
+
if (!opts.version) {
|
|
75
|
+
throw new Error("Missing version");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!opts.platform) {
|
|
79
|
+
throw new Error("Missing platform");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let arch = opts.arch;
|
|
83
|
+
if (!arch) {
|
|
84
|
+
switch (opts.platform) {
|
|
85
|
+
case "darwin":
|
|
86
|
+
arch = "x64";
|
|
87
|
+
break;
|
|
88
|
+
case "win32":
|
|
89
|
+
arch = "ia32";
|
|
90
|
+
break;
|
|
91
|
+
case "linux":
|
|
92
|
+
arch = "ia32";
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let downloadOpts = {
|
|
98
|
+
version: opts.version,
|
|
99
|
+
platform: opts.platform,
|
|
100
|
+
arch,
|
|
101
|
+
artifactName: opts.artifactName,
|
|
102
|
+
artifactSuffix: opts.artifactSuffix,
|
|
103
|
+
token: opts.token,
|
|
104
|
+
downloadOptions: {
|
|
105
|
+
getProgressCallback: (progress) => {
|
|
106
|
+
if (bar) bar.update(progress.percent);
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
bar = new ProgressBar(
|
|
112
|
+
`Downloading ${opts.artifactName}: [:bar] :percent ETA: :eta seconds `,
|
|
113
|
+
{
|
|
114
|
+
curr: 0,
|
|
115
|
+
total: 100,
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if (opts.repo) {
|
|
120
|
+
const url = await getDownloadUrl(opts.repo, downloadOpts);
|
|
121
|
+
|
|
122
|
+
downloadOpts = {
|
|
123
|
+
...downloadOpts,
|
|
124
|
+
mirrorOptions: {
|
|
125
|
+
mirror: `https://github.com/${opts.repo}/releases/download/`,
|
|
126
|
+
resolveAssetURL: () => url,
|
|
127
|
+
},
|
|
128
|
+
unsafelyDisableChecksums: true,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const start = new Date();
|
|
133
|
+
bar.start = start;
|
|
134
|
+
|
|
135
|
+
return await downloadArtifact(downloadOpts);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function downloadStream(opts) {
|
|
139
|
+
return es.readable(function (_, cb) {
|
|
140
|
+
download(opts).then(
|
|
141
|
+
(assets) => {
|
|
142
|
+
zfs
|
|
143
|
+
.src(assets)
|
|
144
|
+
.on("data", (data) => this.emit("data", data))
|
|
145
|
+
.on("error", (err) => this.emit("error", err))
|
|
146
|
+
.on("end", () => this.emit("end"));
|
|
147
|
+
},
|
|
148
|
+
(err) => cb(err)
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function getDarwinLibFFMpegPath(opts) {
|
|
154
|
+
return path.join(
|
|
155
|
+
"Electron.app",
|
|
156
|
+
"Contents",
|
|
157
|
+
"Frameworks",
|
|
158
|
+
"Electron Framework.framework",
|
|
159
|
+
"Versions",
|
|
160
|
+
"A",
|
|
161
|
+
"Libraries",
|
|
162
|
+
"libffmpeg.dylib"
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
module.exports = function (opts) {
|
|
167
|
+
const downloadOpts = {
|
|
168
|
+
version: opts.version,
|
|
169
|
+
platform: opts.platform,
|
|
170
|
+
arch: opts.arch === "arm" ? "armv7l" : opts.arch,
|
|
171
|
+
artifactName: "electron",
|
|
172
|
+
token: opts.token,
|
|
173
|
+
quiet: opts.quiet,
|
|
174
|
+
repo: opts.repo,
|
|
175
|
+
symbols: opts.symbols,
|
|
176
|
+
pdbs: opts.pdbs,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
if (opts.symbols) {
|
|
180
|
+
return downloadStream({ ...downloadOpts, artifactSuffix: "symbols" });
|
|
181
|
+
} else if (opts.pdbs) {
|
|
182
|
+
return downloadStream({ ...downloadOpts, artifactSuffix: "pdb" });
|
|
183
|
+
} else {
|
|
184
|
+
let electron = downloadStream(downloadOpts);
|
|
185
|
+
|
|
186
|
+
if (!opts.ffmpegChromium) {
|
|
187
|
+
return electron;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
electron = electron.pipe(filter(["**", "!**/*ffmpeg.*"]));
|
|
191
|
+
|
|
192
|
+
let ffmpeg = downloadStream({
|
|
193
|
+
...downloadOpts,
|
|
194
|
+
artifactName: "ffmpeg",
|
|
195
|
+
}).pipe(filter("**/*ffmpeg.*"));
|
|
196
|
+
|
|
197
|
+
if (opts.platform === "darwin") {
|
|
198
|
+
ffmpeg = ffmpeg.pipe(rename(getDarwinLibFFMpegPath(opts)));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return es.merge(electron, ffmpeg);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
module.exports.download = download;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var path = require("path");
|
|
4
|
+
var es = require("event-stream");
|
|
5
|
+
var fs = require("fs");
|
|
6
|
+
var rename = require("gulp-rename");
|
|
7
|
+
var rimraf = require("rimraf");
|
|
8
|
+
var symdest = require("gulp-symdest");
|
|
9
|
+
var download = require("./download");
|
|
10
|
+
var semver = require("semver");
|
|
11
|
+
|
|
12
|
+
function moveApp(platform, opts) {
|
|
13
|
+
var appPath = platform.getAppPath(opts);
|
|
14
|
+
|
|
15
|
+
return rename(function (path) {
|
|
16
|
+
path.dirname = appPath + (path.dirname === "." ? "" : "/" + path.dirname);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function _electron(opts) {
|
|
21
|
+
var pass = es.through();
|
|
22
|
+
var result = es.through();
|
|
23
|
+
|
|
24
|
+
var buffer = [];
|
|
25
|
+
|
|
26
|
+
var src = pass.pipe(
|
|
27
|
+
es.through(function (f) {
|
|
28
|
+
if (!buffer) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
buffer.push(f);
|
|
33
|
+
|
|
34
|
+
if (f.relative !== "package.json") {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
var json = JSON.parse(f.contents.toString("utf8"));
|
|
39
|
+
opts = { ...opts };
|
|
40
|
+
|
|
41
|
+
// We need to extract the application's name and version
|
|
42
|
+
// in order to feed it to the various platforms build
|
|
43
|
+
// process.
|
|
44
|
+
opts.productName = json.name;
|
|
45
|
+
opts.productVersion = json.version;
|
|
46
|
+
|
|
47
|
+
var platform = require("./" + opts.platform);
|
|
48
|
+
|
|
49
|
+
var sources = es
|
|
50
|
+
.merge(es.readArray(buffer), pass)
|
|
51
|
+
.pipe(moveApp(platform, opts));
|
|
52
|
+
|
|
53
|
+
var electron = download(opts).pipe(platform.patch(opts));
|
|
54
|
+
|
|
55
|
+
es.merge(sources, electron).pipe(result);
|
|
56
|
+
|
|
57
|
+
buffer = null;
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
return es.duplex(pass, es.merge(src, result));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function electron(opts) {
|
|
65
|
+
if (!opts.version) {
|
|
66
|
+
throw new Error("Missing Electron option: version.");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!opts.platform) {
|
|
70
|
+
throw new Error("Missing Electron option: platform.");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (opts.ffmpegChromium && !semver.gte(opts.version, "0.36.7")) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
"ffmpegChromium option is only supported in Electron versions >= v0.36.8."
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (opts.productName) {
|
|
80
|
+
console.warn(
|
|
81
|
+
"productName is deprecated. The application's name will be picked up automatically from package.json."
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (opts.productVersion) {
|
|
86
|
+
console.warn(
|
|
87
|
+
"productVersion is deprecated. The application's version will be picked up automatically from package.json."
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return _electron(opts);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function dest(destination, opts) {
|
|
95
|
+
if (!destination) {
|
|
96
|
+
throw new Error("Missing destination.");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
opts = opts || {};
|
|
100
|
+
opts.platform = opts.platform || process.platform;
|
|
101
|
+
opts.arch = opts.arch || process.arch;
|
|
102
|
+
|
|
103
|
+
var shouldUpdate = false;
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
var version = fs.readFileSync(path.join(destination, "version"), "utf8");
|
|
107
|
+
shouldUpdate = version !== "v" + opts.version;
|
|
108
|
+
} catch (e) {
|
|
109
|
+
shouldUpdate = true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!shouldUpdate) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
var result = es.through();
|
|
117
|
+
|
|
118
|
+
rimraf(destination, function (err) {
|
|
119
|
+
if (err) {
|
|
120
|
+
return result.emit("error", err);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
var stream = download(opts);
|
|
124
|
+
|
|
125
|
+
if (opts.platform === "win32" && opts.win32ExeBasename) {
|
|
126
|
+
stream = stream.pipe(
|
|
127
|
+
rename(function (path) {
|
|
128
|
+
if (
|
|
129
|
+
path.dirname === "." &&
|
|
130
|
+
path.basename === "electron" &&
|
|
131
|
+
path.extname === ".exe"
|
|
132
|
+
) {
|
|
133
|
+
path.basename = opts.win32ExeBasename;
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
stream.pipe(symdest(destination)).pipe(result);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
electron.dest = dest;
|
|
146
|
+
module.exports = electron;
|
package/src/linux.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var path = require("path");
|
|
4
|
+
var es = require("event-stream");
|
|
5
|
+
var rename = require("gulp-rename");
|
|
6
|
+
var semver = require("semver");
|
|
7
|
+
|
|
8
|
+
function getOriginalAppName(opts) {
|
|
9
|
+
return semver.gte(opts.version, "0.24.0") ? "electron" : "atom";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
exports.getAppPath = function (opts) {
|
|
13
|
+
return "resources/app";
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function removeDefaultApp() {
|
|
17
|
+
var defaultAppPath = path.join("resources", "default_app");
|
|
18
|
+
|
|
19
|
+
return es.mapSync(function (f) {
|
|
20
|
+
if (!f.relative.startsWith(defaultAppPath)) {
|
|
21
|
+
return f;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function renameApp(opts) {
|
|
27
|
+
return rename(function (path) {
|
|
28
|
+
if (
|
|
29
|
+
path.dirname === "." &&
|
|
30
|
+
path.basename === getOriginalAppName(opts) &&
|
|
31
|
+
path.extname === ""
|
|
32
|
+
) {
|
|
33
|
+
path.basename = opts.linuxExecutableName || opts.productName;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* For Electron versions that support the setuid sandbox on Linux, changes the permissions of
|
|
40
|
+
* the `chrome-sandbox` executable as appropriate.
|
|
41
|
+
*
|
|
42
|
+
* The sandbox helper executable must have the setuid (`+s` / `0o4000`) bit set.
|
|
43
|
+
*
|
|
44
|
+
* See: https://github.com/electron/electron/pull/17269#issuecomment-470671914
|
|
45
|
+
*/
|
|
46
|
+
function updateSandboxHelperPermissions() {
|
|
47
|
+
return es.mapSync(function (f) {
|
|
48
|
+
if (!f.isNull() && !f.isDirectory() && f.path === "chrome-sandbox") {
|
|
49
|
+
f.stat.mode = 0o4755;
|
|
50
|
+
}
|
|
51
|
+
return f;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
exports.patch = function (opts) {
|
|
56
|
+
var pass = es.through();
|
|
57
|
+
|
|
58
|
+
var src = pass
|
|
59
|
+
.pipe(updateSandboxHelperPermissions())
|
|
60
|
+
.pipe(opts.keepDefaultApp ? es.through() : removeDefaultApp())
|
|
61
|
+
.pipe(renameApp(opts));
|
|
62
|
+
|
|
63
|
+
return es.duplex(pass, src);
|
|
64
|
+
};
|
package/src/win32.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var fs = require("fs");
|
|
4
|
+
var path = require("path");
|
|
5
|
+
var es = require("event-stream");
|
|
6
|
+
var rename = require("gulp-rename");
|
|
7
|
+
var temp = require("temp").track();
|
|
8
|
+
var rcedit = require("rcedit");
|
|
9
|
+
var semver = require("semver");
|
|
10
|
+
var { spawnSync } = require("child_process");
|
|
11
|
+
|
|
12
|
+
function getOriginalAppName(opts) {
|
|
13
|
+
return semver.gte(opts.version, "0.24.0") ? "electron" : "atom";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getOriginalAppFullName(opts) {
|
|
17
|
+
return getOriginalAppName(opts) + ".exe";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getSignTool() {
|
|
21
|
+
let windowsSDKDir= "C:\\Program Files (x86)\\Windows Kits\\10\\bin\\";
|
|
22
|
+
if (!fs.existsSync(windowsSDKDir)) {
|
|
23
|
+
throw `There is no Windows 10 SDK installed at ${windowsSDKDir}.`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let findSignTool = (start) => {
|
|
27
|
+
let signToolPaths = [];
|
|
28
|
+
fs.readdirSync(start).forEach(file => {
|
|
29
|
+
const filename = path.join(start, file);
|
|
30
|
+
const stat = fs.lstatSync(filename);
|
|
31
|
+
if (stat.isDirectory()) {
|
|
32
|
+
signToolPaths = signToolPaths.concat(findSignTool(filename));
|
|
33
|
+
} else if (filename.endsWith("signtool.exe")) {
|
|
34
|
+
signToolPaths.push(filename);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return signToolPaths;
|
|
38
|
+
}
|
|
39
|
+
let signToolPaths = findSignTool(windowsSDKDir);
|
|
40
|
+
let latestWindowsSDKVersion = signToolPaths.map(x => x.replace(windowsSDKDir, "").split("\\")[0]).pop();
|
|
41
|
+
let latestWindowsSDKSignTools = signToolPaths.filter(x => x.startsWith(`${windowsSDKDir}${latestWindowsSDKVersion}`));
|
|
42
|
+
let x64SignTool = latestWindowsSDKSignTools.find(x => x.includes("x64"));
|
|
43
|
+
if (x64SignTool) {
|
|
44
|
+
return x64SignTool;
|
|
45
|
+
}
|
|
46
|
+
let x86SignTool = latestWindowsSDKSignTools.find(x => x.includes("x86"));
|
|
47
|
+
if (x86SignTool) {
|
|
48
|
+
return x86SignTool;
|
|
49
|
+
}
|
|
50
|
+
throw `No supported version for signtool installed in ${windowsSDKDir}${latestWindowsSdkVersion}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
exports.getAppPath = function (opts) {
|
|
54
|
+
return "resources/app";
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
function patchExecutable(opts) {
|
|
58
|
+
return es.map(function (f, cb) {
|
|
59
|
+
if (
|
|
60
|
+
f.relative !== getOriginalAppFullName(opts) ||
|
|
61
|
+
process.platform !== "win32"
|
|
62
|
+
) {
|
|
63
|
+
return cb(null, f);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
var patch = {
|
|
67
|
+
"version-string": {
|
|
68
|
+
CompanyName: opts.companyName || "GitHub, Inc.",
|
|
69
|
+
FileDescription: opts.productAppName || opts.productName,
|
|
70
|
+
LegalCopyright:
|
|
71
|
+
opts.copyright ||
|
|
72
|
+
"Copyright (C) 2014 GitHub, Inc. All rights reserved",
|
|
73
|
+
ProductName: opts.productAppName || opts.productName,
|
|
74
|
+
ProductVersion: opts.productVersion,
|
|
75
|
+
},
|
|
76
|
+
"file-version": opts.productVersion,
|
|
77
|
+
"product-version": opts.productVersion,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
if (opts.winIcon) {
|
|
81
|
+
patch.icon = opts.winIcon;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
var tempPath = temp.path();
|
|
85
|
+
var ostream = fs.createWriteStream(tempPath);
|
|
86
|
+
|
|
87
|
+
f.contents.pipe(ostream);
|
|
88
|
+
ostream.on("close", function () {
|
|
89
|
+
// Remove codesignature before editing exe file
|
|
90
|
+
const signToolPath = getSignTool();
|
|
91
|
+
const {error} = spawnSync(signToolPath, ["remove", "/s", tempPath]);
|
|
92
|
+
if (error) {
|
|
93
|
+
return cb(error);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
rcedit(tempPath, patch, function (err) {
|
|
97
|
+
if (err) {
|
|
98
|
+
return cb(err);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fs.readFile(tempPath, function (err, data) {
|
|
102
|
+
if (err) {
|
|
103
|
+
return cb(err);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
f.contents = data;
|
|
107
|
+
|
|
108
|
+
fs.unlink(tempPath, function (err) {
|
|
109
|
+
if (err) {
|
|
110
|
+
return cb(err);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
cb(null, f);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function removeDefaultApp() {
|
|
122
|
+
var defaultAppPath = path.join("resources", "default_app");
|
|
123
|
+
|
|
124
|
+
return es.mapSync(function (f) {
|
|
125
|
+
if (!f.relative.startsWith(defaultAppPath)) {
|
|
126
|
+
return f;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function renameApp(opts) {
|
|
132
|
+
return rename(function (path) {
|
|
133
|
+
if (
|
|
134
|
+
path.dirname === "." &&
|
|
135
|
+
path.basename === getOriginalAppName(opts) &&
|
|
136
|
+
path.extname === ".exe"
|
|
137
|
+
) {
|
|
138
|
+
path.basename = opts.productName;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
exports.patch = function (opts) {
|
|
144
|
+
var pass = es.through();
|
|
145
|
+
|
|
146
|
+
var src = pass
|
|
147
|
+
.pipe(opts.keepDefaultApp ? es.through() : removeDefaultApp())
|
|
148
|
+
.pipe(patchExecutable(opts))
|
|
149
|
+
.pipe(renameApp(opts));
|
|
150
|
+
|
|
151
|
+
return es.duplex(pass, src);
|
|
152
|
+
};
|