Skip to content

Commit 189cb0c

Browse files
feat(publisher): initial work on a publish command to sent make artifacts to github
Makers now return an array of artifacts which the publish command will use to send them to GitHub
1 parent f1cac74 commit 189cb0c

File tree

13 files changed

+189
-8
lines changed

13 files changed

+189
-8
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"electron-packager": "^8.4.0",
6767
"electron-winstaller": "^2.5.0",
6868
"fs-promise": "^1.0.0",
69+
"github": "^7.2.0",
6970
"inquirer": "^2.0.0",
7071
"log-symbols": "^1.0.2",
7172
"node-gyp": "^3.4.0",

src/electron-forge-make.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ const main = async () => {
1717
program
1818
.version(require('../package.json').version)
1919
.arguments('[cwd]')
20-
.option('-s, --skip-package', 'Assume the app is already packaged')
20+
.option('--skip-package', 'Assume the app is already packaged')
2121
.option('-a, --arch [arch]', 'Target architecture')
2222
.option('-p, --platform [platform]', 'Target build platform')
23+
.allowUnknownOption(true)
2324
.action((cwd) => {
2425
if (!cwd) return;
2526
if (path.isAbsolute(cwd) && fs.existsSync(cwd)) {
@@ -78,6 +79,7 @@ const main = async () => {
7879

7980
const packageJSON = await readPackageJSON(dir);
8081
const appName = packageJSON.productName || packageJSON.name;
82+
const outputs = [];
8183

8284
for (const targetArch of targetArchs) {
8385
const packageDir = path.resolve(dir, `out/${appName}-${declaredPlatform}-${targetArch}`);
@@ -99,7 +101,7 @@ const main = async () => {
99101
}
100102
}
101103
try {
102-
await (maker.default || maker)(packageDir, appName, targetArch, forgeConfig, packageJSON);
104+
outputs.push(await (maker.default || maker)(packageDir, appName, targetArch, forgeConfig, packageJSON));
103105
} catch (err) {
104106
makeSpinner.fail();
105107
if (err) throw err;
@@ -108,6 +110,12 @@ const main = async () => {
108110
makeSpinner.succeed();
109111
}
110112
}
113+
114+
return outputs;
111115
};
112116

113-
main();
117+
if (process.mainModule === module) {
118+
main();
119+
}
120+
121+
export default main;

src/electron-forge-publish.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import 'colors';
2+
import fs from 'fs-promise';
3+
import path from 'path';
4+
import program from 'commander';
5+
import ora from 'ora';
6+
7+
import './util/terminate';
8+
import getForgeConfig from './util/forge-config';
9+
import GitHub from './util/github';
10+
import readPackageJSON from './util/read-package-json';
11+
import resolveDir from './util/resolve-dir';
12+
13+
import make from './electron-forge-make';
14+
15+
const main = async () => {
16+
const makeResults = await make();
17+
18+
let dir = process.cwd();
19+
program
20+
.version(require('../package.json').version)
21+
.arguments('[cwd]')
22+
.option('--auth-token', 'Provided GitHub authorization token')
23+
.option('-t, --tag', 'The tag to publish to on GitHub')
24+
.allowUnknownOption(true)
25+
.action((cwd) => {
26+
if (!cwd) return;
27+
if (path.isAbsolute(cwd) && fs.existsSync(cwd)) {
28+
dir = cwd;
29+
} else if (fs.existsSync(path.resolve(dir, cwd))) {
30+
dir = path.resolve(dir, cwd);
31+
}
32+
})
33+
.parse(process.argv);
34+
35+
dir = await resolveDir(dir);
36+
if (!dir) {
37+
console.error('Failed to locate publishable Electron application'.red);
38+
if (global._resolveError) global._resolveError();
39+
process.exit(1);
40+
}
41+
42+
const artifacts = makeResults.reduce((accum, arr) => {
43+
accum.push(...arr);
44+
return accum;
45+
}, []);
46+
47+
const packageJSON = await readPackageJSON(dir);
48+
49+
const forgeConfig = await getForgeConfig(dir);
50+
51+
if (!(forgeConfig.github_repository && typeof forgeConfig.github_repository === 'object' &&
52+
forgeConfig.github_repository.owner && forgeConfig.github_repository.name)) {
53+
console.error('In order to publish you must set the "github_repository.owner" and "github_repository.name" properties in your forge config. See the docs for more info'.red); // eslint-disable-line
54+
process.exit(1);
55+
}
56+
57+
const github = new GitHub(program.authToken);
58+
59+
let release;
60+
try {
61+
release = (await github.getGitHub().repos.getReleases({
62+
owner: forgeConfig.github_repository.owner,
63+
repo: forgeConfig.github_repository.name,
64+
per_page: 100,
65+
})).find(testRelease => testRelease.tag_name === program.tag || `v${packageJSON.version}`);
66+
} catch (err) {
67+
if (err.code === 404) {
68+
// Release does not exist, let's make it
69+
release = await github.getGitHub().repos.createRelease({
70+
owner: forgeConfig.github_repository.owner,
71+
repo: forgeConfig.github_repository.name,
72+
tag_name: program.tag || `v${packageJSON.version}`,
73+
name: program.tag || `v${packageJSON.version}`,
74+
draft: true,
75+
});
76+
} else {
77+
// Unknown error
78+
throw err;
79+
}
80+
}
81+
82+
let uploaded = 0;
83+
const uploadSpinner = ora.ora(`Uploading Artifacts ${uploaded}/${artifacts.length}`).start();
84+
const updateSpinner = () => {
85+
uploadSpinner.text = `Uploading Artifacts ${uploaded}/${artifacts.length}`;
86+
};
87+
88+
try {
89+
await Promise.all(artifacts.map(artifactPath =>
90+
new Promise(async (resolve) => {
91+
const done = () => {
92+
uploaded += 1;
93+
updateSpinner();
94+
resolve();
95+
};
96+
if (release.assets.find(asset => asset.name === path.basename(artifactPath))) {
97+
return done();
98+
}
99+
await github.getGitHub().repos.uploadAsset({
100+
owner: forgeConfig.github_repository.owner,
101+
repo: forgeConfig.github_repository.name,
102+
id: release.id,
103+
filePath: artifactPath,
104+
name: path.basename(artifactPath),
105+
});
106+
return done();
107+
})
108+
));
109+
} catch (err) {
110+
updateSpinner.fail();
111+
throw err;
112+
}
113+
114+
uploadSpinner.succeed();
115+
};
116+
117+
main();

src/electron-forge.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ checkSystem()
2525
.command('package', 'Package the current Electron application')
2626
.command('make', 'Generate distributables for the current Electron application')
2727
.command('start', 'Start the current Electron application')
28+
.command('publish', 'Publish the current Electron application to GitHub')
2829
.parse(process.argv);
2930

3031
config.reset();

src/makers/darwin/dmg.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import pify from 'pify';
55
import { ensureFile } from '../../util/ensure-output';
66

77
export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { // eslint-disable-line
8-
const outPath = path.resolve(dir, '../make', `${path.basename(dir)}.dmg`);
8+
const outPath = path.resolve(dir, '../make', `${appName}.dmg`);
99
await ensureFile(outPath);
1010
const dmgConfig = Object.assign({
1111
overwrite: true,
@@ -15,4 +15,5 @@ export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { /
1515
out: path.dirname(outPath),
1616
});
1717
await pify(electronDMG)(dmgConfig);
18+
return [outPath];
1819
};

src/makers/generic/zip.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,5 @@ export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { /
3636
default:
3737
throw new Error('Unrecognized platform');
3838
}
39+
return [zipPath];
3940
};

src/makers/linux/deb.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { /
2727
const debianConfig = Object.assign({}, forgeConfig.electronInstallerDebian, debianDefaults);
2828

2929
await pify(installer)(debianConfig);
30+
return [outPath];
3031
};

src/makers/linux/flatpak.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { /
2727
const flatpakConfig = Object.assign({}, forgeConfig.electronInstallerFlatpak, flatpakDefaults);
2828

2929
await pify(installer)(flatpakConfig);
30+
return [outPath];
3031
};

src/makers/linux/rpm.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { /
2727
const rpmConfig = Object.assign({}, forgeConfig.electronInstallerRedhat, rpmDefaults);
2828

2929
await pify(installer)(rpmConfig);
30+
return [outPath];
3031
};

src/makers/win32/squirrel.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createWindowsInstaller } from 'electron-winstaller';
2+
import fs from 'fs-promise';
23
import path from 'path';
34

45
import { ensureDirectory } from '../../util/ensure-output';
@@ -7,9 +8,26 @@ export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { /
78
const outPath = path.resolve(dir, `../make/squirrel.windows/${targetArch}`);
89
await ensureDirectory(outPath);
910

10-
const winstallerConfig = Object.assign({}, forgeConfig.electronWinstallerConfig, {
11+
const winstallerConfig = Object.assign({
12+
name: packageJSON.name,
13+
noMsi: true,
14+
}, forgeConfig.electronWinstallerConfig, {
1115
appDirectory: dir,
1216
outputDirectory: outPath,
1317
});
1418
await createWindowsInstaller(winstallerConfig);
19+
const artifacts = [
20+
path.resolve(outPath, 'RELEASES'),
21+
path.resolve(outPath, winstallerConfig.setupExe || `${appName}Setup.exe`),
22+
path.resolve(outPath, `${winstallerConfig.name}-${packageJSON.version}-full.nupkg`),
23+
];
24+
const deltaPath = path.resolve(outPath, `${winstallerConfig.name}-${packageJSON.version}-delta.nupkg`);
25+
if (winstallerConfig.remoteReleases || await fs.exists(deltaPath)) {
26+
artifacts.push(deltaPath);
27+
}
28+
const msiPath = path.resolve(outPath, winstallerConfig.setupMsi || `${appName}Setup.msi`);
29+
if (!winstallerConfig.noMsi && await fs.exists(msiPath)) {
30+
artifacts.push(msiPath);
31+
}
32+
return artifacts;
1533
};

0 commit comments

Comments
 (0)