c9-core/plugins/c9.cli.publish/install.js

577 wiersze
21 KiB
JavaScript

define(function(require, exports, module) {
main.consumes = [
"Plugin", "cli_commands", "proc", "api", "auth", "installer",
"installer.cli"
];
main.provides = ["cli.install"];
return main;
function main(options, imports, register) {
var Plugin = imports.Plugin;
var cmd = imports.cli_commands;
var proc = imports.proc;
var auth = imports.auth;
var api = imports.api;
var installer = imports.installer;
var installerCLI = imports["installer.cli"];
var dirname = require("path").dirname;
var TEST_MODE = !!process.env.C9_TEST_MODE;
var TAR = "tar";
var APIHOST = options.apiHost;
var BASICAUTH = process.env.C9_TEST_AUTH;
var SCM = {
"git": {
binary: "git",
clone: "clone"
},
"mercurial": {
binary: "hg",
clone: "clone"
},
"hg": {
binary: "hg",
clone: "clone"
}
};
var fs = require("fs");
var join = require("path").join;
var os = require("os");
var http = require(APIHOST.indexOf("localhost") > -1 ? "http" : "https");
var verbose = false;
var force = false;
// Set up basic auth for api if needed
if (BASICAUTH) api.basicAuth = BASICAUTH;
/***** Initialization *****/
var plugin = new Plugin("Ajax.org", main.consumes);
// var emit = plugin.getEmitter();
var loaded;
function load() {
if (loaded) return;
loaded = true;
cmd.addCommand({
name: "install",
info: " Installs a cloud9 package.",
usage: "[--verbose] [--force] [--global] [--local] [--debug] [<package>[@<version>] | . ]",
options: {
"local": {
description: "",
"default": false,
"boolean": true
},
"global": {
description: "",
"default": false,
"boolean": true
},
"debug": {
description: "",
"default": false,
"boolean": true
},
"verbose": {
"description": "Output more information",
"alias": "v",
"default": false,
"boolean": true
},
"force": {
"description": "Ignore warnings",
"alias": "f",
"default": false,
"boolean": true
}
},
check: function(argv) {
if (argv._.length < 2 && !argv["package"])
throw new Error("package");
},
exec: function(argv) {
verbose = argv["verbose"];
force = argv["force"];
if (argv.accessToken)
auth.accessToken = argv.accessToken;
if (!argv.local && !argv.debug) {
if (!process.env.C9_PID) {
console.warn("It looks like you are not running on c9.io. Will default to local installation of the package");
argv.local = true;
}
}
var name = argv._[1];
var test = name == ".";
if (test)
name = require("path").basename(process.cwd());
install(
name,
{
global: argv.global,
local: argv.local,
debug: argv.debug,
test: test
},
function(err, data) {
if (err) {
console.error(err.message || "Terminated.");
process.exit(1);
}
else {
console.log("Successfully installed", name + (argv.debug ? "" : "@" + data.version));
process.exit(0);
}
});
}
});
cmd.addCommand({
name: "remove",
alias: "uninstall",
info: " Removes a cloud9 package.",
usage: "[--verbose] [--global] [--local] <package>", // @TODO --global
options: {
"local": {
description: "",
"default": false,
"boolean": true
},
"global": {
description: "",
"default": false,
"boolean": true
},
"verbose": {
"description": "Output more information",
"alias": "v",
"default": false,
"boolean": true
}
},
check: function(argv) {
if (argv._.length < 2 && !argv["package"])
throw new Error("package");
},
exec: function(argv) {
verbose = argv["verbose"];
if (argv.accessToken)
auth.accessToken = argv.accessToken;
var name = argv._[1];
uninstall(
name,
{
global: argv.global,
local: argv.local
},
function(err, data) {
if (err) {
console.error(err.message || "Terminated.");
process.exit(1);
}
else {
console.log("Successfully removed", name);
process.exit(0);
}
});
}
});
}
/***** Methods *****/
function spawn(command, options, callback) {
if (options.stdio == null) {
// if verbose, echo stdout
// always echo stderr
options.stdio = [
"pipe",
verbose ? process.stdout : "ignore",
process.stderr
];
}
proc.spawn(command, options, function(err, child) {
if (err) return callback(err);
child.on("exit", function(code) {
if (code !== 0) {
var error = new Error("Command failed: " + command);
error.code = code;
return callback(error);
}
callback();
});
});
}
function install(packageName, options, callback) {
// Call install url
var parts = packageName.split("@");
var name = parts[0];
var version = parts[1];
var repository;
if (!options.test) {
if (verbose)
console.log("Retrieving package info");
api.packages.get(name, function (err, info) {
if (err) return callback(err);
if (verbose)
console.log("Found:", info);
if (!version)
version = info.latest;
repository = info.repository;
installPackage();
});
}
else {
installPackage();
}
function prepareDirectory(callback) {
// Create package dir
var packagePath = process.env.HOME + "/.c9/plugins/" + name;
var exists = fs.existsSync(packagePath);
// Ignore when testing and in the same dir
if (options.test && process.cwd() == packagePath)
exists = false;
if (exists) {
if (!force && !options.test)
return callback(new Error("WARNING: Directory not empty: " + packagePath
+ ". Use --force to overwrite."));
proc.execFile("rm", {
args: ["-Rf", packagePath]
}, function() {
mkdirP(packagePath);
callback(null, packagePath);
});
}
else {
mkdirP(packagePath);
callback(null, packagePath);
}
}
function installPackage() {
if (!version && !options.test)
return callback(new Error("No version found for this package"));
if (options.local) {
installLocal();
}
else if (options.debug) {
installDebug();
}
else {
installFull();
}
}
function installLocal() {
if (verbose)
console.log("Installing package locally");
prepareDirectory(function(err, packagePath) {
if (err) return callback(err);
var npmBin = [
join(process.env.HOME, process.platform == "win32" ? ".c9/npm.cmd" : ".c9/node/bin/npm"),
"/mnt/shared/sbin/npm"
];
function installNPM() {
spawn(npmBin[0], {
args: ["install"],
cwd: packagePath
}, function(err) {
if (err && err.code == 127) {
npmBin.shift();
if (npmBin.length)
return installNPM();
}
if (err) return callback(err);
callback(null, { version: version });
});
}
if (options.test) {
try {
var json = JSON.parse(fs.readFileSync(join(process.cwd(), "package.json")));
if (json.private)
return callback(new Error("ERROR: Private flag in package.json prevents from installing"));
}
catch (e) {
return callback(new Error("ERROR: Invalid package: " + e.message));
}
if (process.cwd() == packagePath)
installNPM();
else {
proc.execFile("cp", {
args: ["-R", process.cwd(), dirname(packagePath)]
}, function(err) {
if (err) return callback(err);
installNPM();
});
}
return;
}
// Download package
var gzPath = join(os.tmpDir(), name + "@" + version + ".tar.gz");
var file = fs.createWriteStream(gzPath);
var path = "/packages/" + name + "/versions/" + version
+ "/download?access_token="
+ encodeURIComponent(auth.accessToken);
var host = APIHOST.split(":")[0];
var port = parseInt(APIHOST.split(":")[1]) || null;
var request = http.get({
agent: false,
method: "get",
host: host,
port: port,
auth: BASICAUTH,
path: path
}, function(response) {
response.pipe(file);
});
if (verbose)
console.log("Downloading package to", gzPath);
request.on('response', function(res) {
if (res.statusCode != 200)
return callback(new Error("Unknown Error getting "
+ host + (port ? ":" + port : "")
+ path + ":" + res.statusCode));
});
file.on('finish', function() {
if (verbose)
console.log("Unpacking", gzPath, "to", packagePath);
// Untargz package
spawn(TAR, {
args: [
(verbose ? "-v" : ""),
"-C", normalizePath(packagePath),
"-zxf", normalizePath(gzPath)
]
}, function(err) {
if (err) {
var error = new Error("Failed to unpack package");
error.code = err.code;
return callback(error);
}
installNPM();
});
});
});
}
function installDebug() {
if (verbose)
console.log("Installing debug version of package");
if (options.test)
return callback(new Error("Test is not supported for debug installations"));
prepareDirectory(function(err, packagePath) {
if (err) return callback(err);
if (verbose)
console.log("Cloning repository: ", repository);
// Git clone repository
var scm = SCM[repository.type];
spawn(scm.binary, {
args: [scm.clone, repository.url, packagePath]
}, function(err) {
if (err) {
var error = new Error("Failed to clone package from repository. Do you have access?");
error.code = err.code;
return callback(error);
}
callback();
});
});
}
function installFull() {
// Install Locally
options.local = true;
install(name + "@" + version, options, function(err) {
if (err) return callback(err);
var path = process.env.HOME + "/.c9/plugins/" + name;
fs.readFile(path + "/package.json", "utf8", function(err, data) {
if (err) return callback(new Error("Package.json not found in " + path));
var installPath;
try { installPath = JSON.parse(data).installer; }
catch (e) {
return callback(new Error("Could not parse package.json in " + path));
}
if (installPath) {
installerCLI.verbose = verbose;
installer.createSession(name, version || "", require(path + "/" + installPath), function(err) {
if (err) return callback(new Error("Error Installing Package " + name + "@" + version));
installToDatabase();
}, force || options.test);
}
else
installToDatabase();
});
function installToDatabase() {
if (options.test)
return callback(null, { version: "test" });
if (verbose)
console.log("Notifying c9.io that package is installed");
var endpoint = options.global ? api.user : api.project;
var url = "install/" + name + "/" + version + "?mode=silent";
endpoint.post(url, function(err, info) {
callback(err, info);
});
}
});
}
}
function uninstall(packageName, options, callback) {
// Call uninstall url
var parts = packageName.split("@");
var name = parts[0];
var version = parts[1];
if (!version) {
api.packages.get(name, function (err, info) {
if (err) return callback(err);
version = info.latest;
uninstallPackage();
});
}
else {
uninstallPackage();
}
function uninstallPackage() {
if (options.local || options.debug) {
// rm -Rf
var packagePath = process.env.HOME + "/.c9/plugins/" + name;
spawn("rm", {
args: ["-rf", packagePath]
}, function(err) {
if (err) {
var error = new Error("Failed to remove package.");
error.code = err.code;
return callback(error);
}
// if debug > see if should be installed and put back original
// @TODO
callback();
});
}
else {
var endpoint = options.global ? api.user : api.project;
var url = "uninstall/" + packageName;
endpoint.post(url, function(err, info) {
callback(err, info);
});
}
}
}
function mkdirP(path) {
var dirs = path.split('/');
var prevDir = dirs.splice(0, 1) + "/";
while (dirs.length > 0) {
var curDir = prevDir + dirs.splice(0, 1);
if (! fs.existsSync(curDir)) {
fs.mkdirSync(curDir);
}
prevDir = curDir + '/';
}
}
function normalizePath(p) {
if (process.platform == "win32")
p = p.replace(/\\/g, "/").replace(/^(\w):/, "/$1");
return p;
}
/***** Lifecycle *****/
plugin.on("load", function() {
load();
});
plugin.on("enable", function() {
});
plugin.on("disable", function() {
});
plugin.on("unload", function() {
loaded = false;
verbose = false;
force = false;
});
/***** Register and definfe API *****/
/**
*
**/
plugin.freezePublicAPI({
/**
*
*/
install: install,
/**
*
*/
uninstall: uninstall
});
register(null, {
"cli.install": plugin
});
}
});