Skip to content

Commit

Permalink
Merge branch 'master' into build-console-output-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
rwjblue committed Jan 25, 2017
2 parents 94d0bdb + 385bf36 commit d72d552
Show file tree
Hide file tree
Showing 17 changed files with 658 additions and 298 deletions.
30 changes: 16 additions & 14 deletions lib/cli/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ const getOptionArgs = require('../utilities/get-option-args');
let logger = require('heimdalljs-logger')('ember-cli:cli');
let loggerTesting = require('heimdalljs-logger')('ember-cli:testing');
const Instrumentation = require('../models/instrumentation');
const exit = require('capture-exit');
const heimdall = require('heimdalljs');

const Promise = RSVP.Promise;
const onProcessInterrupt = require('../utilities/will-interrupt-process');

class CLI {
/**
Expand Down Expand Up @@ -145,6 +145,7 @@ class CLI {
}
}

let onCommandInterrupt = command.onInterrupt.bind(command);
let instrumentation = this.instrumentation;
let initCompleted = false;

Expand All @@ -154,6 +155,7 @@ class CLI {
instrumentation.start('command');

loggerTesting.info('cli: command.beforeRun');
onProcessInterrupt.addHandler(onCommandInterrupt);

return command.beforeRun(commandArgs);

Expand All @@ -167,14 +169,14 @@ class CLI {
}
instrumentation.start('shutdown');

onProcessInterrupt.removeHandler(onCommandInterrupt);
shutdownOnExit = function() {
instrumentation.stopAndReport('shutdown');
};

// schedule this with `capture-exit` to ensure that
// the shutdown instrumentation hook is invoked properly even if
// we exit before hitting the `finally` below
exit.onExit(shutdownOnExit);
// Ensure that the shutdown instrumentation hook is invoked properly
// even if we exit before hitting the `finally` below
onProcessInterrupt.addHandler(shutdownOnExit);
}).then(result => {
// if the help option was passed, call the help command
if (result === 'callHelp') {
Expand Down Expand Up @@ -205,15 +207,15 @@ class CLI {
});
});
})
.finally(() => {
if (shutdownOnExit) {
// invoke instrumentation shutdown and
// remove from `capture-exit` callbacks
shutdownOnExit();
exit.offExit(shutdownOnExit);
}
})
.catch(this.logError.bind(this));
.finally(() => {
if (shutdownOnExit) {
// invoke instrumentation shutdown and
// remove instrumentation interruption handlers
shutdownOnExit();
onProcessInterrupt.removeHandler(shutdownOnExit);
}
})
.catch(this.logError.bind(this));
}

/**
Expand Down
4 changes: 0 additions & 4 deletions lib/cli/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
'use strict';

// work around misbehaving libraries, so we can correctly cleanup before
// actually exiting.
require('capture-exit').captureExit();

// Main entry point
const requireAsHash = require('../utilities/require-as-hash');
const packageConfig = require('../../package.json');
Expand Down
25 changes: 7 additions & 18 deletions lib/commands/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,22 @@ module.exports = Command.extend({
],

run(commandOptions) {
let BuildTask = this.taskFor(commandOptions);
let buildTask = new BuildTask({
ui: this.ui,
analytics: this.analytics,
project: this.project,
});
let ShowAssetSizesTask = this.tasks.ShowAssetSizes;
let showTask = new ShowAssetSizesTask({
ui: this.ui,
});

return Win.checkIfSymlinksNeedToBeEnabled(this.ui)
.then(() => buildTask.run(commandOptions))
.then(() => {
if (!commandOptions.suppressSizes && (commandOptions.environment === 'production' || process.env.EMBER_ENV === 'production')) {
return Win.checkIfSymlinksNeedToBeEnabled(this.ui).then(() => {
let buildTaskName = commandOptions.watch ? 'BuildWatch' : 'Build';
return this.runTask(buildTaskName, commandOptions).then(() => {
let isProduction = commandOptions.environment === 'production' || process.env.EMBER_ENV === 'production';

if (!commandOptions.suppressSizes && isProduction) {
return showTask.run({
outputPath: commandOptions.outputPath,
});
}
});
},

taskFor(options) {
if (options.watch) {
return this.tasks.BuildWatch;
} else {
return this.tasks.Build;
}
});
},
});
43 changes: 15 additions & 28 deletions lib/commands/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,6 @@ module.exports = Command.extend({
port: this._generateTestPortNumber(commandOptions),
}, this._generateCustomConfigs(commandOptions));

let options = {
ui: this.ui,
analytics: this.analytics,
project: this.project,
};

return Win.checkIfSymlinksNeedToBeEnabled(this.ui).then(() => {
let session;

Expand All @@ -142,39 +136,32 @@ module.exports = Command.extend({
throw new SilentError('Specifying a build is not allowed with the `--server` option.');
}

let TestServerTask = this.tasks.TestServer;
let testServer = new TestServerTask(options);
let builder = new this.Builder(testOptions);

testOptions.watcher = new this.Watcher(assign(options, {
testOptions.watcher = new this.Watcher(assign(this._env(), {
builder,
verbose: false,
options: commandOptions,
}));

session = testServer.run(testOptions)
.finally(() => builder.cleanup());

session = this.runTask('TestServer', testOptions).finally(() => builder.cleanup());
} else if (hasBuild) {
session = this.runTask('Test', testOptions);
} else {
let TestTask = this.tasks.Test;
let test = new TestTask(options);


if (hasBuild) {
session = test.run(testOptions);
} else {
let BuildTask = this.tasks.Build;
let build = new BuildTask(options);

session = build.run({
environment: commandOptions.environment,
outputPath,
})
.then(() => test.run(testOptions));
}
session = this.runTask('Build', {
environment: commandOptions.environment,
outputPath,
})
.then(() => this.runTask('Test', testOptions));
}

return session.finally(this.rmTmp.bind(this));
});
},

onInterrupt() {
this.rmTmp();

return this._super.onInterrupt.apply(this, arguments);
},
});
103 changes: 5 additions & 98 deletions lib/models/builder.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
'use strict';
const exit = require('capture-exit');
exit.captureExit();

const onProcessInterrupt = require('../utilities/will-interrupt-process');
const fs = require('fs-extra');
const existsSync = require('exists-sync');
const path = require('path');
Expand All @@ -12,7 +10,6 @@ const chalk = require('chalk');
const attemptNeverIndex = require('../utilities/attempt-never-index');
const findBuildFile = require('../utilities/find-build-file');
const _resetTreeCache = require('./addon')._resetTreeCache;

const Sync = require('tree-sync');
const heimdall = require('heimdalljs');

Expand All @@ -31,8 +28,10 @@ class Builder extends CoreObject {
super(options);

this.setupBroccoliBuilder();
this.trapSignals();
this._instantiationStack = (new Error()).stack.replace(/[^\n]*\n/, '');
this._cleanup = this.cleanup.bind(this);

onProcessInterrupt.addHandler(this._cleanup);
}

/**
Expand All @@ -59,60 +58,6 @@ class Builder extends CoreObject {
this.builder = new broccoli.Builder(this.tree);
}

/**
* @private
* @method trapSignals
*/
trapSignals() {
this._boundOnSIGINT = this.onSIGINT.bind(this);
this._boundOnSIGTERM = this.onSIGTERM.bind(this);
this._boundOnMessage = this.onMessage.bind(this);
this._boundCleanup = this.cleanup.bind(this);

process.on('SIGINT', this._boundOnSIGINT);
process.on('SIGTERM', this._boundOnSIGTERM);
process.on('message', this._boundOnMessage);
exit.onExit(this._boundCleanup);

if (/^win/.test(process.platform)) {
this.trapWindowsSignals();
}
}

_cleanupSignals() {
process.removeListener('SIGINT', this._boundOnSIGINT);
process.removeListener('SIGTERM', this._boundOnSIGTERM);
process.removeListener('message', this._boundOnMessage);
exit.offExit(this._boundCleanup);

if (/^win/.test(process.platform)) {
this._cleanupWindowsSignals();
}
}

/**
* @private
* @method trapWindowsSignals
*/
trapWindowsSignals() {
// This is required to capture Ctrl + C on Windows
if (process.stdin && process.stdin.isTTY) {
process.stdin.setRawMode(true);
this._windowsCtrlCTrap = function(data) {
if (data.length === 1 && data[0] === 0x03) {
process.emit('SIGINT');
}
};
process.stdin.on('data', this._windowsCtrlCTrap);
}
}

_cleanupWindowsSignals() {
if (this._windowsCtrlCTrap && process.stdin.removeListener) {
process.stdin.removeListener('data', this._windowsCtrlCTrap);
}
}

/**
Determine whether the output path is safe to delete. If the outputPath
appears anywhere in the parents of the project root, the build would
Expand Down Expand Up @@ -241,7 +186,7 @@ class Builder extends CoreObject {
// ensure any addon treeFor caches are reset
_resetTreeCache();

this._cleanupSignals();
onProcessInterrupt.removeHandler(this._cleanup);

let node = heimdall.start({ name: 'Builder Cleanup' });

Expand Down Expand Up @@ -270,44 +215,6 @@ class Builder extends CoreObject {

return value;
}

/**
* Handles the `SIGINT` signal.
*
* Calls {{#crossLink "Builder/cleanupAndExit:method"}}{{/crossLink}} by default.
*
* @private
* @method onSIGINT
*/
onSIGINT() {
process.exit(1);
}

/**
* Handles the `SIGTERM` signal.
*
* Calls {{#crossLink "Builder/cleanupAndExit:method"}}{{/crossLink}} by default.
*
* @private
* @method onSIGTERM
*/
onSIGTERM() {
process.exit(1);
}

/**
* Handles the `message` event on the `process`.
*
* Calls `process.exit` if the `kill` property on the `message` is set.
*
* @private
* @method onMessage
*/
onMessage(message) {
if (message.kill) {
process.exit(1);
}
}
}

module.exports = Builder;
40 changes: 40 additions & 0 deletions lib/models/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,46 @@ let Command = CoreObject.extend({
this.availableOptions.map(this.validateOption.bind(this));
},

/**
* Called when command is interrupted from outside, e.g. ctrl+C or process kill
* Can be used to cleanup artifacts produced by command and control process exit code
*
* @method onInterrupt
* @return {Promise|undefined} if rejected promise then result of promise will be used as an exit code
*/
onInterrupt() {
if (this._currentTask) {
return this._currentTask.onInterrupt();
}
},

_env() {
return {
ui: this.ui,
analytics: this.analytics,
project: this.project,
};
},

runTask(name, options) {
if (this._currentTask) {
throw new Error(`Concurrent tasks are not supported`);
}

let Task = this.tasks[name];
if (!Task) {
throw new Error(`Unknown task "${name}"`);
}

let task = new Task(this._env());

this._currentTask = task;

return Promise.resolve(task.run(options)).finally(() => {
delete this._currentTask;
});
},

/**
Hook for extending a command before it is run in the cli.run command.
Most common use case would be to extend availableOptions.
Expand Down
11 changes: 11 additions & 0 deletions lib/models/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ class Task extends CoreObject {
run(/*options*/) {
throw new Error('Task needs to have run() defined.');
}

/**
* Interrupt comamd with an exit code
* Called when the process is interrupted from outside, e.g. CTRL+C or `process.kill()`
*
* @private
* @method onInterrupt
*/
onInterrupt() {
process.exit(1);
}
}

module.exports = Task;

0 comments on commit d72d552

Please sign in to comment.