mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-06 22:40:14 -05:00
Install App Dependencies
- Spawns an npm install command from the App root - Has some special OS checks for windows command spawning
This commit is contained in:
parent
2beba7f1d7
commit
e3affff713
3 changed files with 116 additions and 18 deletions
52
core/server/apps/dependencies.js
Normal file
52
core/server/apps/dependencies.js
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
|
||||||
|
var _ = require('lodash'),
|
||||||
|
fs = require('fs'),
|
||||||
|
path = require('path'),
|
||||||
|
when = require('when'),
|
||||||
|
spawn = require('child_process').spawn,
|
||||||
|
win32 = process.platform === 'win32';
|
||||||
|
|
||||||
|
function AppDependencies(appPath) {
|
||||||
|
this.appPath = appPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppDependencies.prototype.install = function installAppDependencies() {
|
||||||
|
var def = when.defer(),
|
||||||
|
spawnOpts;
|
||||||
|
|
||||||
|
fs.exists(path.join(this.appPath, 'package.json'), function (exists) {
|
||||||
|
if (!exists) {
|
||||||
|
// Nothing to do, resolve right away?
|
||||||
|
def.resolve();
|
||||||
|
} else {
|
||||||
|
// Run npm install in the app directory
|
||||||
|
spawnOpts = {
|
||||||
|
cwd: this.appPath
|
||||||
|
};
|
||||||
|
|
||||||
|
this.spawnCommand('npm', ['install', '--production'], spawnOpts)
|
||||||
|
.on('error', def.reject)
|
||||||
|
.on('exit', function (err) {
|
||||||
|
if (err) {
|
||||||
|
def.reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
def.resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
return def.promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalize a command across OS and spawn it; taken from yeoman/generator
|
||||||
|
AppDependencies.prototype.spawnCommand = function (command, args, opt) {
|
||||||
|
var winCommand = win32 ? 'cmd' : command,
|
||||||
|
winArgs = win32 ? ['/c'].concat(command, args) : args;
|
||||||
|
|
||||||
|
opt = opt || {};
|
||||||
|
|
||||||
|
return spawn(winCommand, winArgs, _.defaults({ stdio: 'inherit' }, opt));
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = AppDependencies;
|
|
@ -5,14 +5,20 @@ var path = require('path'),
|
||||||
appProxy = require('./proxy'),
|
appProxy = require('./proxy'),
|
||||||
config = require('../config'),
|
config = require('../config'),
|
||||||
AppSandbox = require('./sandbox'),
|
AppSandbox = require('./sandbox'),
|
||||||
|
AppDependencies = require('./dependencies'),
|
||||||
loader;
|
loader;
|
||||||
|
|
||||||
|
// Get the full path to an app by name
|
||||||
|
function getAppAbsolutePath(name) {
|
||||||
|
return path.join(config().paths.appPath, name);
|
||||||
|
}
|
||||||
|
|
||||||
// Get a relative path to the given apps root, defaults
|
// Get a relative path to the given apps root, defaults
|
||||||
// to be relative to __dirname
|
// to be relative to __dirname
|
||||||
function getAppRelativePath(name, relativeTo) {
|
function getAppRelativePath(name, relativeTo) {
|
||||||
relativeTo = relativeTo || __dirname;
|
relativeTo = relativeTo || __dirname;
|
||||||
|
|
||||||
return path.relative(relativeTo, path.join(config().paths.appPath, name));
|
return path.relative(relativeTo, getAppAbsolutePath(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load apps through a psuedo sandbox
|
// Load apps through a psuedo sandbox
|
||||||
|
@ -41,17 +47,22 @@ function getAppByName(name) {
|
||||||
loader = {
|
loader = {
|
||||||
// Load a app and return the instantiated app
|
// Load a app and return the instantiated app
|
||||||
installAppByName: function (name) {
|
installAppByName: function (name) {
|
||||||
var app = getAppByName(name);
|
// Install the apps dependendencies first
|
||||||
|
var deps = new AppDependencies(getAppAbsolutePath(name));
|
||||||
|
return deps.install().then(function () {
|
||||||
|
var app = getAppByName(name);
|
||||||
|
|
||||||
// Check for an install() method on the app.
|
// Check for an install() method on the app.
|
||||||
if (!_.isFunction(app.install)) {
|
if (!_.isFunction(app.install)) {
|
||||||
return when.reject(new Error("Error loading app named " + name + "; no install() method defined."));
|
return when.reject(new Error("Error loading app named " + name + "; no install() method defined."));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapping the install() with a when because it's possible
|
// Run the app.install() method
|
||||||
// to not return a promise from it.
|
// Wrapping the install() with a when because it's possible
|
||||||
return when(app.install(appProxy)).then(function () {
|
// to not return a promise from it.
|
||||||
return when.resolve(app);
|
return when(app.install(appProxy)).then(function () {
|
||||||
|
return when.resolve(app);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
/*globals describe, beforeEach, afterEach, before, it*/
|
/*globals describe, beforeEach, afterEach, before, it*/
|
||||||
var fs = require('fs'),
|
var fs = require('fs'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
should = require('should'),
|
EventEmitter = require('events').EventEmitter,
|
||||||
sinon = require('sinon'),
|
should = require('should'),
|
||||||
_ = require('lodash'),
|
sinon = require('sinon'),
|
||||||
helpers = require('../../server/helpers'),
|
_ = require('lodash'),
|
||||||
filters = require('../../server/filters'),
|
helpers = require('../../server/helpers'),
|
||||||
|
filters = require('../../server/filters'),
|
||||||
|
|
||||||
// Stuff we are testing
|
// Stuff we are testing
|
||||||
appProxy = require('../../server/apps/proxy'),
|
appProxy = require('../../server/apps/proxy'),
|
||||||
AppSandbox = require('../../server/apps/sandbox');
|
AppSandbox = require('../../server/apps/sandbox'),
|
||||||
|
AppDependencies = require('../../server/apps/dependencies');
|
||||||
|
|
||||||
describe('Apps', function () {
|
describe('Apps', function () {
|
||||||
|
|
||||||
|
@ -154,4 +156,37 @@ describe('Apps', function () {
|
||||||
loadApp.should.throw(/^Unsafe App require[\w\W]*example$/);
|
loadApp.should.throw(/^Unsafe App require[\w\W]*example$/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Dependencies', function () {
|
||||||
|
it('can install by package.json', function (done) {
|
||||||
|
var deps = new AppDependencies(process.cwd()),
|
||||||
|
fakeEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
deps.spawnCommand = sandbox.stub().returns(fakeEmitter);
|
||||||
|
|
||||||
|
deps.install().then(function () {
|
||||||
|
deps.spawnCommand.calledWith('npm').should.equal(true);
|
||||||
|
done();
|
||||||
|
}).otherwise(done);
|
||||||
|
|
||||||
|
_.delay(function () {
|
||||||
|
fakeEmitter.emit('exit');
|
||||||
|
}, 30);
|
||||||
|
});
|
||||||
|
it('does not install when no package.json', function (done) {
|
||||||
|
var deps = new AppDependencies(__dirname),
|
||||||
|
fakeEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
deps.spawnCommand = sandbox.stub().returns(fakeEmitter);
|
||||||
|
|
||||||
|
deps.install().then(function () {
|
||||||
|
deps.spawnCommand.called.should.equal(false);
|
||||||
|
done();
|
||||||
|
}).otherwise(done);
|
||||||
|
|
||||||
|
_.defer(function () {
|
||||||
|
fakeEmitter.emit('exit');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
Loading…
Reference in a new issue