ickyrr-gagarin
Version:
A current updated fork of gagarin
335 lines (252 loc) • 9.98 kB
JavaScript
var parseBuildErrors = require('./parseBuildErrors');
var linkNodeModules = require('./linkNodeModules');
var Promise = require('es6-promise').Promise;
var chalk = require('chalk');
var spawn = require('child_process').spawn;
var tools = require('../tools');
var path = require('path');
var logs = require('../logs');
var fs = require('fs');
var utils = require('../utils');
var memoize = {};
module.exports = function Build (options) {
"use strict";
options = options || {};
var pathToApp = options.pathToApp || path.resolve('.');
// it has to be absolute because we are going to use it as cwd
// for meteor build in a moment ...
pathToApp = path.resolve(pathToApp);
var timeout = options.timeout || 120000;
var verbose = options.verbose !== undefined ? !!options.verbose : false;
if (logs.isSilentBuild()) {
// the global settings get precedence here
verbose = false;
}
var pathToSmartJson = path.join(pathToApp, 'smart.json');
var pathToMain = path.join(pathToApp, '.meteor', 'local', 'build', 'main.js');
var pathToMain2 = path.join(pathToApp, '.gagarin', 'local', 'bundle', 'main.js');
var skipBuild = !!options.skipBuild;
this.start = function start () {
// TODO: in the future, we will also want to cache
// w/r/t build options (e.g. production/debug)
if (memoize[pathToApp]) {
return memoize[pathToApp];
}
memoize[pathToApp] = new Promise(function (resolve, reject) {
checkIfMeteorIsRunning(pathToApp).then(function (isRunning) {
if (isRunning) {
if (!fs.existsSync(pathToMain)) {
throw new Error("Even though meteor seems to be running, the file " +
"'.meteor/local/build/main.js' does not exist. Either, there is a stale mongod process, " +
"or you just need to remove '.meteor/local/db/mongo.lock' manually.");
}
logs.system('using ' + pathToMain);
return pathToMain
}
if (skipBuild) {
// first, try using the previous build ...
if (fs.existsSync(pathToMain2)) {
logs.system('using ' + pathToMain2);
return pathToMain2;
} else { // as a fallback solution try using main.js from develop mode
if (fs.existsSync(pathToMain)) {
logs.system('using ' + pathToMain);
return pathToMain;
}
throw new Error('There is no build available, so you cannot "skip build".');
}
}
return smartJsonWarning(pathToApp).then(function () {
// finally run the builder ...
logs.system('running meteor build at ' + pathToApp);
return ensureGagarinVersionsMatch(pathToApp, verbose).then(function () {
return BuildPromise({
pathToApp : pathToApp,
verbose : verbose,
timeout : timeout
});
});
});
}).then(resolve, reject);
});
return memoize[pathToApp];
}; // start
};
// PRIVATE BUILD PROMISE IMPLEMENTATION
function BuildPromise(options) {
"use strict";
options = options || {};
var pathToApp = options.pathToApp || path.resolve('.');
var mongoUrl = options.mongoUrl || "http://localhost:27017";
var timeout = options.timeout || 120000;
var verbose = options.verbose !== undefined ? !!options.verbose : false;
var pathToMain = path.join(pathToApp, '.gagarin', 'local', 'bundle', 'main.js');
var pathToLocal = path.join(pathToApp, '.gagarin', 'local');
var env = Object.create(process.env);
return tools.getReleaseVersion(pathToApp).then(function (version) { return new Promise(function (resolve, reject) {
var args;
logs.system("detected METEOR@" + version);
if (version >= '1.0.0') {
args = [ 'build', '--debug', '--directory', pathToLocal ];
} else {
args = [ 'bundle', '--debug', '--directory', path.join(pathToLocal, 'bundle') ];
}
logs.system("spawning meteor process with the following args");
logs.system(JSON.stringify(args));
var buildTimeout = null;
var meteorBinary = tools.getMeteorBinary();
//make sure that platforms file contains only server and browser
//and cache this file under platforms.gagarin.backup
var platformsFilePath = path.join(pathToApp,'.meteor','platforms');
var platformsBackupPath = path.join(pathToApp,'.meteor','platforms.gagarin.backup');
fs.rename(platformsFilePath,platformsBackupPath,function(err,data){
fs.writeFile(platformsFilePath,'server\nbrowser\n',function(){
spawnMeteorProcess();
});
});
var output = "";
var meteor = null;
var spawnMeteorProcess = function(){
meteor = spawn(meteorBinary, args, {
cwd: pathToApp, env: env, stdio: verbose ? 'inherit' : 'ignore'
});
meteor.on('exit', function onExit (code) {
//switch back to initial content of platforms file
fs.rename(platformsBackupPath,platformsFilePath);
if (code) {
return reject(new Error('meteor build exited with code ' + code));
}
/*
var err = parseBuildErrors(output);
if (err) {
return reject(err);
}
*/
logs.system('linking node_modules');
linkNodeModules(pathToApp).then(function () {
logs.system('everything is fine');
resolve(pathToMain);
}).catch(reject);
buildTimeout = setTimeout(function () {
meteor.once('exit', function () {
reject(new Error('Timeout while waiting for meteor build to finish.'));
});
meteor.kill('SIGINT')
}, timeout);
clearTimeout(buildTimeout);
});
}
//-----------------------------------------------
/*
meteor.stdout.on('data', function onData (data) {
output += data.toString();
if (data.toString().match(/WARNING: The output directory is under your source tree./)) {
logMeteorOutput(' creating your test build in ' + path.join(pathToLocal, 'bundle') + '\n');
return;
}
logMeteorOutput(data);
});
meteor.stdout.on('error', function onError (data) {
console.log(chalk.red(data.toString()));
clearTimeout(buildTimeout);
});
*/
}); });
function logMeteorOutput(data) {
if (!verbose) {
return;
}
process.stdout.write(chalk.gray(chalk.stripColor(data)));
}
} // Build
/**
* Check smart package version and if it's wrong, install the right one.
*
* @param {string} pathToApp
*/
function ensureGagarinVersionsMatch(pathToApp, verbose) {
var pathToMeteorPackages = path.join(pathToApp, '.meteor', 'packages');
var nodeModuleVersion = require('../../package.json').version;
return new Promise(function (resolve, reject) {
utils.getGagarinPackageVersion(pathToApp).then(function (packageVersion) {
if (packageVersion === nodeModuleVersion) {
logs.system("node module and smart package versions match, " + packageVersion);
return resolve();
}
tools.getReleaseVersion(pathToApp).then(function (meteorReleaseVersion) {
if (meteorReleaseVersion < "0.9.0") {
// really, we can do nothing about package version
// without a decent package management system
logs.system("meteor version is too old to automatically fix package version");
return resolve();
}
logs.system("meteor add anti:gagarin@=" + nodeModuleVersion);
var meteor = spawn('meteor', [ 'add', 'anti:gagarin@=' + nodeModuleVersion ], {
stdio: verbose ? 'inherit' : 'ignore', cwd: pathToApp
});
/*
meteor.stdout.on('data', function (data) {
if (!verbose) {
return;
}
process.stdout.write(chalk.gray(chalk.stripColor(data)));
});
*/
meteor.on('error', reject);
meteor.on('exit', function (code) {
if (code > 0) {
return reject(new Error('meteor exited with code ' + code));
}
logs.system("anti:gagarin is now in version " + nodeModuleVersion);
resolve();
});
}).catch(reject);
}).catch(reject);
}); // Promise
}
/**
* Guess if "develop" meteor is currently running.
*
* @param {string} pathToApp
*/
function checkIfMeteorIsRunning(pathToApp) {
var pathToMongoLock = path.join(pathToApp, '.meteor', 'local', 'db', 'mongod.lock');
return new Promise(function (resolve, reject) {
fs.readFile(pathToMongoLock, { encoding: 'utf8' }, function (err, data) {
if (err) {
// if the file does not exist, then we are ok anyway
return err.code !== 'ENOENT' ? reject(err) : resolve();
} else {
// isLocked iff the content is non empty
resolve(!!data);
}
});
});
}
/**
* Look for "smart.json" file. If there's one, print warning
* before resolving.
*
* @param {string} pathToApp
*/
function smartJsonWarning(pathToApp) {
var pathToSmartJSON = path.join(pathToApp, 'smart.json');
return new Promise(function (resolve, reject) {
fs.readFile(pathToSmartJSON, { endcoding: 'urf8' }, function (err, data) {
if (err) {
// if the file does not exist, then we are ok anyway
return err.code !== 'ENOENT' ? reject(err) : resolve();
} else {
// since the file exists, first print a warning, then resolve ...
process.stdout.write(chalk.yellow(
tools.banner([
'we have detected a smart.json file in ' + pathToApp,
'since Gagarin no longer supports meteorite, this file will be ignored',
].join('\n'), {})
));
process.stdout.write('\n');
resolve();
}
});
});
}