Skip to content

Commit

Permalink
feat(publisher): initial work on a publish command to sent make artif…
Browse files Browse the repository at this point in the history
…acts to github

Makers now return an array of artifacts which the publish command will use to send them to GitHub
  • Loading branch information
MarshallOfSound committed Dec 30, 2016
1 parent f1cac74 commit 189cb0c
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 8 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"electron-packager": "^8.4.0",
"electron-winstaller": "^2.5.0",
"fs-promise": "^1.0.0",
"github": "^7.2.0",
"inquirer": "^2.0.0",
"log-symbols": "^1.0.2",
"node-gyp": "^3.4.0",
Expand Down
14 changes: 11 additions & 3 deletions src/electron-forge-make.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ const main = async () => {
program
.version(require('../package.json').version)
.arguments('[cwd]')
.option('-s, --skip-package', 'Assume the app is already packaged')
.option('--skip-package', 'Assume the app is already packaged')
.option('-a, --arch [arch]', 'Target architecture')
.option('-p, --platform [platform]', 'Target build platform')
.allowUnknownOption(true)
.action((cwd) => {
if (!cwd) return;
if (path.isAbsolute(cwd) && fs.existsSync(cwd)) {
Expand Down Expand Up @@ -78,6 +79,7 @@ const main = async () => {

const packageJSON = await readPackageJSON(dir);
const appName = packageJSON.productName || packageJSON.name;
const outputs = [];

for (const targetArch of targetArchs) {
const packageDir = path.resolve(dir, `out/${appName}-${declaredPlatform}-${targetArch}`);
Expand All @@ -99,7 +101,7 @@ const main = async () => {
}
}
try {
await (maker.default || maker)(packageDir, appName, targetArch, forgeConfig, packageJSON);
outputs.push(await (maker.default || maker)(packageDir, appName, targetArch, forgeConfig, packageJSON));
} catch (err) {
makeSpinner.fail();
if (err) throw err;
Expand All @@ -108,6 +110,12 @@ const main = async () => {
makeSpinner.succeed();
}
}

return outputs;
};

main();
if (process.mainModule === module) {
main();
}

export default main;
117 changes: 117 additions & 0 deletions src/electron-forge-publish.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import 'colors';
import fs from 'fs-promise';
import path from 'path';
import program from 'commander';
import ora from 'ora';

import './util/terminate';
import getForgeConfig from './util/forge-config';
import GitHub from './util/github';
import readPackageJSON from './util/read-package-json';
import resolveDir from './util/resolve-dir';

import make from './electron-forge-make';

const main = async () => {
const makeResults = await make();

let dir = process.cwd();
program
.version(require('../package.json').version)
.arguments('[cwd]')
.option('--auth-token', 'Provided GitHub authorization token')
.option('-t, --tag', 'The tag to publish to on GitHub')
.allowUnknownOption(true)
.action((cwd) => {
if (!cwd) return;
if (path.isAbsolute(cwd) && fs.existsSync(cwd)) {
dir = cwd;
} else if (fs.existsSync(path.resolve(dir, cwd))) {
dir = path.resolve(dir, cwd);
}
})
.parse(process.argv);

dir = await resolveDir(dir);
if (!dir) {
console.error('Failed to locate publishable Electron application'.red);
if (global._resolveError) global._resolveError();
process.exit(1);
}

const artifacts = makeResults.reduce((accum, arr) => {
accum.push(...arr);
return accum;
}, []);

const packageJSON = await readPackageJSON(dir);

const forgeConfig = await getForgeConfig(dir);

if (!(forgeConfig.github_repository && typeof forgeConfig.github_repository === 'object' &&
forgeConfig.github_repository.owner && forgeConfig.github_repository.name)) {
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
process.exit(1);
}

const github = new GitHub(program.authToken);

let release;
try {
release = (await github.getGitHub().repos.getReleases({
owner: forgeConfig.github_repository.owner,
repo: forgeConfig.github_repository.name,
per_page: 100,
})).find(testRelease => testRelease.tag_name === program.tag || `v${packageJSON.version}`);
} catch (err) {
if (err.code === 404) {
// Release does not exist, let's make it
release = await github.getGitHub().repos.createRelease({
owner: forgeConfig.github_repository.owner,
repo: forgeConfig.github_repository.name,
tag_name: program.tag || `v${packageJSON.version}`,
name: program.tag || `v${packageJSON.version}`,
draft: true,
});
} else {
// Unknown error
throw err;
}
}

let uploaded = 0;
const uploadSpinner = ora.ora(`Uploading Artifacts ${uploaded}/${artifacts.length}`).start();
const updateSpinner = () => {
uploadSpinner.text = `Uploading Artifacts ${uploaded}/${artifacts.length}`;
};

try {
await Promise.all(artifacts.map(artifactPath =>
new Promise(async (resolve) => {
const done = () => {
uploaded += 1;
updateSpinner();
resolve();
};
if (release.assets.find(asset => asset.name === path.basename(artifactPath))) {
return done();
}
await github.getGitHub().repos.uploadAsset({
owner: forgeConfig.github_repository.owner,
repo: forgeConfig.github_repository.name,
id: release.id,
filePath: artifactPath,
name: path.basename(artifactPath),
});
return done();
})
));
} catch (err) {
updateSpinner.fail();
throw err;
}

uploadSpinner.succeed();
};

main();
1 change: 1 addition & 0 deletions src/electron-forge.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ checkSystem()
.command('package', 'Package the current Electron application')
.command('make', 'Generate distributables for the current Electron application')
.command('start', 'Start the current Electron application')
.command('publish', 'Publish the current Electron application to GitHub')
.parse(process.argv);

config.reset();
Expand Down
3 changes: 2 additions & 1 deletion src/makers/darwin/dmg.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import pify from 'pify';
import { ensureFile } from '../../util/ensure-output';

export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { // eslint-disable-line
const outPath = path.resolve(dir, '../make', `${path.basename(dir)}.dmg`);
const outPath = path.resolve(dir, '../make', `${appName}.dmg`);
await ensureFile(outPath);
const dmgConfig = Object.assign({
overwrite: true,
Expand All @@ -15,4 +15,5 @@ export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { /
out: path.dirname(outPath),
});
await pify(electronDMG)(dmgConfig);
return [outPath];
};
1 change: 1 addition & 0 deletions src/makers/generic/zip.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { /
default:
throw new Error('Unrecognized platform');
}
return [zipPath];
};
1 change: 1 addition & 0 deletions src/makers/linux/deb.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { /
const debianConfig = Object.assign({}, forgeConfig.electronInstallerDebian, debianDefaults);

await pify(installer)(debianConfig);
return [outPath];
};
1 change: 1 addition & 0 deletions src/makers/linux/flatpak.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { /
const flatpakConfig = Object.assign({}, forgeConfig.electronInstallerFlatpak, flatpakDefaults);

await pify(installer)(flatpakConfig);
return [outPath];
};
1 change: 1 addition & 0 deletions src/makers/linux/rpm.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ export default async (dir, appName, targetArch, forgeConfig, packageJSON) => { /
const rpmConfig = Object.assign({}, forgeConfig.electronInstallerRedhat, rpmDefaults);

await pify(installer)(rpmConfig);
return [outPath];
};
20 changes: 19 additions & 1 deletion src/makers/win32/squirrel.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createWindowsInstaller } from 'electron-winstaller';
import fs from 'fs-promise';
import path from 'path';

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

const winstallerConfig = Object.assign({}, forgeConfig.electronWinstallerConfig, {
const winstallerConfig = Object.assign({
name: packageJSON.name,
noMsi: true,
}, forgeConfig.electronWinstallerConfig, {
appDirectory: dir,
outputDirectory: outPath,
});
await createWindowsInstaller(winstallerConfig);
const artifacts = [
path.resolve(outPath, 'RELEASES'),
path.resolve(outPath, winstallerConfig.setupExe || `${appName}Setup.exe`),
path.resolve(outPath, `${winstallerConfig.name}-${packageJSON.version}-full.nupkg`),
];
const deltaPath = path.resolve(outPath, `${winstallerConfig.name}-${packageJSON.version}-delta.nupkg`);
if (winstallerConfig.remoteReleases || await fs.exists(deltaPath)) {
artifacts.push(deltaPath);
}
const msiPath = path.resolve(outPath, winstallerConfig.setupMsi || `${appName}Setup.msi`);
if (!winstallerConfig.noMsi && await fs.exists(msiPath)) {
artifacts.push(msiPath);
}
return artifacts;
};
27 changes: 27 additions & 0 deletions src/util/github.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import GitHubAPI from 'github';

export default class GitHub {
constructor(authToken) {
if (authToken) {
this.token = authToken;
} else if (process.env.GITHUB_TOKEN) {
this.token = process.env.GITHUB_TOKEN;
}
}

getGitHub() {
const github = new GitHubAPI({
protocol: 'https',
headers: {
'user-agent': 'Electron Forge',
},
});
if (this.token) {
github.authenticate({
type: 'token',
token: this.token,
});
}
return github;
}
}
4 changes: 2 additions & 2 deletions src/util/terminate.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ const d = debug('electron-forge:lifecycle');

process.on('unhandledRejection', (err) => {
process.stdout.write('\n\nAn unhandled rejection has occurred inside Forge:\n');
console.error(colors.red(err.stack));
console.error(colors.red(err.stack || JSON.stringify(err)));
process.exit(1);
});

process.on('uncaughtException', (err) => {
process.stdout.write('\n\nAn unhandled exception has occurred inside Forge:\n');
console.error(colors.red(err.stack));
console.error(colors.red(err.stack || JSON.stringify(err)));
process.exit(1);
});

Expand Down
6 changes: 5 additions & 1 deletion tmpl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
"name": ""
},
"electronInstallerDebian": {},
"electronInstallerRedhat": {}
"electronInstallerRedhat": {},
"github_repository": {
"owner": "",
"name": ""
}
}
}
}

0 comments on commit 189cb0c

Please sign in to comment.