{"_id":"551c45a448564a2f0024d7ed","category":{"_id":"551c453a23a1ee190034d19a","__v":2,"pages":["551c456223a1ee190034d19b","551c45a448564a2f0024d7ed"],"project":"54d53c7b23010a0d001aca0c","version":"54d5635532d98b0d00384afb","sync":{"url":"","isSync":false},"reference":false,"createdAt":"2015-04-01T19:21:30.273Z","from_sync":false,"order":12,"slug":"installer","title":"Installer"},"version":{"_id":"54d5635532d98b0d00384afb","project":"54d53c7b23010a0d001aca0c","__v":10,"forked_from":"54d53c7c23010a0d001aca0f","createdAt":"2015-02-07T00:59:01.934Z","releaseDate":"2015-02-07T00:59:01.934Z","categories":["54d5635632d98b0d00384afc","54d5635632d98b0d00384afd","54d5635632d98b0d00384afe","54d5635632d98b0d00384aff","54d5635632d98b0d00384b00","54d5635632d98b0d00384b01","54d5635632d98b0d00384b02","54d652097e05890d006f153e","54dd1315ca1e5219007e9daa","54e21e2b22de1c230094b147","54e68e62a43fe13500db3879","54fa1d3fe7a0ba2f00306309","551c453a23a1ee190034d19a","551df586e52a0b23000c62b6","551f39be6886f8230055f02a","55a6720751457325000e4d97"],"is_deprecated":false,"is_hidden":false,"is_beta":true,"is_stable":true,"codename":"","version_clean":"0.1.0","version":"0.1"},"user":"54cfa8e1a8a4fd0d00b7fd1d","parentDoc":null,"__v":14,"project":"54d53c7b23010a0d001aca0c","updates":[],"next":{"pages":[],"description":""},"createdAt":"2015-04-01T19:23:16.917Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"auth":"required","params":[],"url":""},"isReference":false,"order":1,"body":"The  [Cloud9 Installer](doc:adding-an-installer) can be extended with custom package managers that add a new way to install a package or manipulate the workspace.\n\nA package manager is a simple plugin with an `execute()` method that takes a task argument and an options object.\n\nIn the example below the first object is the options object and in this case the `npm` package manager gets its `execute()` method called with \"sequelize:::at:::2.0.0-beta.0\" as its task.\n\n```\nsession.install({\n    \"cwd\": \"~/.c9\"\n}, {\n    \"npm\": \"sequelize@2.0.0-beta.0\"\n});\n```\n\nIn the following example there are multiple items for the `npm` package manager. However, the `execute()` method will be called once for each.\n\n```\nsession.install({\n    \"cwd\": \"~/.c9\"\n}, {\n    \"npm\": [\"sequelize@2.0.0-beta.0\", \"sqlite3@2.1.18\"]\n});\n```\n\n# Creating a Package Manager\n\nA basic package manager starts with the following template.\n```\ndefine(function(require, exports, module) {\n    main.consumes = [\"Plugin\", \"installer\", \"proc\"];\n    main.provides = [\"installer.name\"];\n    return main;\n    \n    function main(options, imports, register) {\n        var Plugin = imports.Plugin;\n        var installer = imports.installer;\n        var proc = imports.proc;\n        \n        var binBash = options.binBash || \"bash\";\n        \n        var plugin = new Plugin(\"Your Name\", main.consumes);\n        \n        function execute(task, options, onData, callback) {\n            \n        }\n        \n        var available;\n        function isAvailable(callback){\n            \n        }\n        \n        plugin.on(\"load\", function() {\n            installer.addPackageManager(\"name\", plugin);\n            installer.addPackageManagerAlias(\"name\", \"othername\");\n        });\n        plugin.on(\"unload\", function() {\n            installer.removePackageManager(\"name\");\n            available = undefined;\n        });\n        \n        plugin.freezePublicAPI({ execute: execute, isAvailable: isAvailable });\n        \n        register(null, {\n            \"installer.name\": plugin\n        });\n    }\n});\n```\n\n# isAvailable\n\nThe `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.\n\nThe 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.\n```\nvar available;\nfunction isAvailable(callback){\n    if (typeof available != \"undefined\")\n        return callback(available);\n    \n    proc.execFile(\"which\", { args: [\"apt-get\"] }, function(err, stdout){\n        if (err) return callback(false);\n        \n        available = stdout.length > 0;\n        callback(available);\n    });\n}\n```\n\n# execute\n\nAs 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.\n\nThe 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. \n```\nfunction execute(task, options, onData, callback) {\n    var script = 'set -e\\n'\n        + 'sudo apt-get install ' + task;\n    \n    proc.pty(binBash, {\n        args: [\"-c\", script],\n        cwd: options.cwd || null\n    }, function(err, pty){\n        if (err) return callback(err);\n        \n        // Pipe the data to the onData function\n        pty.on(\"data\", function(chunk){\n            onData(chunk, pty);\n        });\n        \n        // When process exits call callback\n        pty.on(\"exit\", function(code){\n            if (!code) callback();\n            else callback(new Error(\"Failed. Exit code \" + code));\n        });\n    });\n}\n```","excerpt":"","slug":"extending-the-installer","type":"basic","title":"Extending the Installer"}

Extending the Installer


The [Cloud9 Installer](doc:adding-an-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 "sequelize@2.0.0-beta.0" as its task. ``` session.install({ "cwd": "~/.c9" }, { "npm": "sequelize@2.0.0-beta.0" }); ``` 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": ["sequelize@2.0.0-beta.0", "sqlite3@2.1.18"] }); ``` # 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)); }); }); } ```