forked from samuelmeuli/action-electron-builder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
183 lines (156 loc) · 5.25 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
const { execSync } = require("child_process");
const { existsSync } = require("fs");
const { join } = require("path");
/**
* Logs to the console
*/
const log = msg => console.log(`\n${msg}`); // eslint-disable-line no-console
/**
* Exits the current process with an error code and message
*/
const exit = msg => {
console.error(msg);
process.exit(1);
};
/**
* Executes the provided shell command and redirects stdout/stderr to the console
*/
const run = (cmd, cwd) => execSync(cmd, { encoding: "utf8", stdio: "inherit", cwd });
/**
* Determines the current operating system (one of ["mac", "windows", "linux"])
*/
const getPlatform = () => {
switch (process.platform) {
case "darwin":
return "mac";
case "win32":
return "windows";
default:
return "linux";
}
};
/**
* Returns the value for an environment variable (or `null` if it's not defined)
*/
const getEnv = name => process.env[name.toUpperCase()] || null;
/**
* Sets the specified env variable if the value isn't empty
*/
const setEnv = (name, value) => {
if (value) {
process.env[name.toUpperCase()] = value.toString();
}
};
/**
* Returns the value for an input variable (or `null` if it's not defined). If the variable is
* required and doesn't have a value, abort the action
*/
const getInput = (name, required) => {
const value = getEnv(`INPUT_${name}`);
if (required && !value) {
exit(`"${name}" input variable is not defined`);
}
return value;
};
/*
* Taken from https://stackoverflow.com/a/5878101.
*
* Function to test if an object is a plain object, i.e. is constructed
* by the built-in Object constructor and inherits directly from Object.prototype
* or null (e.g., const foo = Object.create(null)). Some built-in objects pass the test,
* e.g. Math which is a plain object and some host or exotic objects may pass also.
*/
const isPlainObject = obj => {
// Basic check for type object that's not null
if (typeof obj === "object" && obj !== null) {
// If Object.getPrototypeOf supported, use it
if (typeof Object.getPrototypeOf === "function") {
const proto = Object.getPrototypeOf(obj);
return proto === Object.prototype || proto === null;
}
// Otherwise, use internal class
// This should be reliable as if getPrototypeOf not supported, is pre-ES5
return Object.prototype.toString.call(obj) === "[object Object]";
}
// Not an object
return false;
};
/**
* Given configuration overrides as object, return a list of CLI args that can be
* passed directly to `electron-builder build` using its `-c...` override mechanism.
*
* Example, given:
* {
* extraMetadata: {
* version: "4.1.0",
* },
* releaseInfo: {
* releaseName: "foo",
* releaseNotes: "Some notes",
* },
* }
*
* Return:
* ["-c.releaseInfo.releaseNotes=Some notes",
* "-c.releaseInfo.releaseName=foo",
* "-c.extraMetadata.version=4.1.0"]
*/
const transformConfigOverridesToCliArgs = overrides => {
const stack = Object.entries(overrides);
const cliArgs = [];
while (stack.length) {
const [path, config] = stack.pop();
if (!isPlainObject(config)) {
cliArgs.push(`-c.${path}=${config}`);
} else {
Object.entries(config).forEach(([key, subConfig]) => {
stack.push([`${path}.${key}`, subConfig]);
});
}
}
return cliArgs;
};
/**
* Installs NPM dependencies and builds/releases the Electron app
*/
const runAction = () => {
const platform = getPlatform();
const release = getInput("release", true) === "true";
const pkgRoot = getInput("package_root", true);
const configOverrides = JSON.parse(getInput("config_overrides") || "{}");
// TODO: Deprecated option, remove in v2.0. `electron-builder` always requires a `package.json` in
// the same directory as the Electron app, so the `package_root` option should be used instead
const appRoot = getInput("app_root") || pkgRoot;
const pkgJsonPath = join(pkgRoot, "package.json");
// Make sure `package.json` file exists
if (!existsSync(pkgJsonPath)) {
exit(`\`package.json\` file not found at path "${pkgJsonPath}"`);
}
// Copy "github_token" input variable to "GH_TOKEN" env variable (required by `electron-builder`)
setEnv("GH_TOKEN", getInput("github_token", true));
// Require code signing certificate and password if building for macOS. Export them to environment
// variables (required by `electron-builder`)
if (platform === "mac") {
setEnv("CSC_LINK", getInput("mac_certs"));
setEnv("CSC_KEY_PASSWORD", getInput("mac_certs_password"));
} else if (platform === "windows") {
setEnv("CSC_LINK", getInput("windows_certs"));
setEnv("CSC_KEY_PASSWORD", getInput("windows_certs_password"));
}
// Disable console advertisements during install phase
setEnv("ADBLOCK", true);
log("Installing dependencies");
run("npm ci");
// Run NPM build script if it exists
run("npm run build --if-present", pkgRoot);
log(`Building${release ? " and releasing" : ""} the Electron app…`);
const runner = "npx --no-install";
const executable = "electron-builder";
const platformArg = `--${platform}`;
const releaseArg = release ? "--publish always" : "--publish never";
const configOverrideArgs = transformConfigOverridesToCliArgs(configOverrides);
const script = [runner, executable, platformArg, releaseArg, ...configOverrideArgs].join(" ");
log(`Running: ${script}`);
run(script, appRoot);
};
runAction();