Extending the Installer
The Cloud9 Installer can be extended with custom package managers that add a new way to install a package or manipulate the workspace.
A package manager is a simple plugin with an execute()
method that takes a task argument and an options object.
In the example below the first object is the options object and in this case the npm
package manager gets its execute()
method called with "[email protected]" as its task.
session.install({
"cwd": "~/.c9"
}, {
"npm": "[email protected]"
});
In the following example there are multiple items for the npm
package manager. However, the execute()
method will be called once for each.
session.install({
"cwd": "~/.c9"
}, {
"npm": ["[email protected]", "[email protected]"]
});
Creating a Package Manager
A basic package manager starts with the following template.
define(function(require, exports, module) {
main.consumes = ["Plugin", "installer", "proc"];
main.provides = ["installer.name"];
return main;
function main(options, imports, register) {
var Plugin = imports.Plugin;
var installer = imports.installer;
var proc = imports.proc;
var binBash = options.binBash || "bash";
var plugin = new Plugin("Your Name", main.consumes);
function execute(task, options, onData, callback) {
}
var available;
function isAvailable(callback){
}
plugin.on("load", function() {
installer.addPackageManager("name", plugin);
installer.addPackageManagerAlias("name", "othername");
});
plugin.on("unload", function() {
installer.removePackageManager("name");
available = undefined;
});
plugin.freezePublicAPI({ execute: execute, isAvailable: isAvailable });
register(null, {
"installer.name": plugin
});
}
});
isAvailable
The isAvailable()
method is called by the installer to confirm that this package manager is available on the workspace that it is run on. An implementor must cache the result of this function.
The following example is taken from the ubuntu
package manager. It checks whether apt-get
is installed on the system and caches the result in the available
variable.
var available;
function isAvailable(callback){
if (typeof available != "undefined")
return callback(available);
proc.execFile("which", { args: ["apt-get"] }, function(err, stdout){
if (err) return callback(false);
available = stdout.length > 0;
callback(available);
});
}
execute
As already mentioned above, the execute()
method executes a single task. It takes four arguments. The task
contains the string or object that describes the single task to execute. The options
object contains any options specified by the user. The onData
function should be called to stream any data that can be outputted in the installation log. Lastly the callback
must be called when execution is stopped, either successfully or not.
The following example is again from the ubuntu
package manager. As you can see below, the package manager should use proc.pty
to start processes if they are long running.
function execute(task, options, onData, callback) {
var script = 'set -e\n'
+ 'sudo apt-get install ' + task;
proc.pty(binBash, {
args: ["-c", script],
cwd: options.cwd || null
}, function(err, pty){
if (err) return callback(err);
// Pipe the data to the onData function
pty.on("data", function(chunk){
onData(chunk, pty);
});
// When process exits call callback
pty.on("exit", function(code){
if (!code) callback();
else callback(new Error("Failed. Exit code " + code));
});
});
}
Updated less than a minute ago