{"metadata":{"image":[],"title":"","description":""},"api":{"url":"","auth":"required","params":[],"results":{"codes":[]}},"next":{"description":"","pages":[]},"title":"Extending the Installer","type":"basic","slug":"extending-the-installer","excerpt":"","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\": \"[email protected]\"\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\": [\"[email protected]\", \"[email protected]\"]\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```","updates":[],"order":1,"isReference":false,"hidden":false,"sync_unique":"","link_url":"","link_external":false,"_id":"551c45a448564a2f0024d7ed","category":{"sync":{"isSync":false,"url":""},"pages":["551c456223a1ee190034d19b","551c45a448564a2f0024d7ed"],"title":"Installer","slug":"installer","order":12,"from_sync":false,"reference":false,"_id":"551c453a23a1ee190034d19a","__v":2,"project":"54d53c7b23010a0d001aca0c","version":"54d5635532d98b0d00384afb","createdAt":"2015-04-01T19:21:30.273Z"},"version":{"version":"0.1","version_clean":"0.1.0","codename":"","is_stable":true,"is_beta":true,"is_hidden":false,"is_deprecated":false,"categories":["54d5635632d98b0d00384afc","54d5635632d98b0d00384afd","54d5635632d98b0d00384afe","54d5635632d98b0d00384aff","54d5635632d98b0d00384b00","54d5635632d98b0d00384b01","54d5635632d98b0d00384b02","54d652097e05890d006f153e","54dd1315ca1e5219007e9daa","54e21e2b22de1c230094b147","54e68e62a43fe13500db3879","54fa1d3fe7a0ba2f00306309","551c453a23a1ee190034d19a","551df586e52a0b23000c62b6","551f39be6886f8230055f02a","55a6720751457325000e4d97"],"_id":"54d5635532d98b0d00384afb","project":"54d53c7b23010a0d001aca0c","__v":10,"createdAt":"2015-02-07T00:59:01.934Z","forked_from":"54d53c7c23010a0d001aca0f","releaseDate":"2015-02-07T00:59:01.934Z"},"githubsync":"","user":"54cfa8e1a8a4fd0d00b7fd1d","parentDoc":null,"__v":14,"createdAt":"2015-04-01T19:23:16.917Z","project":"54d53c7b23010a0d001aca0c"}