├── .gitignore ├── README.md ├── bin └── upstarter ├── index.js ├── package.json └── service.hbs /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | upstarter 2 | --------- 3 | 4 | Easily create upstart services for your node daemons 5 | 6 | Made for Ubuntu. Sorry, everyone else. 7 | 8 | Idea 9 | ==== 10 | 11 | So you made your cool Node.js HTTP server thing, but how do you run it as a proper 12 | service when your system boots? 13 | 14 | **upstarter** to the rescue! 15 | 16 | Install 17 | ======= 18 | 19 | ```bash 20 | $ npm install -g upstarter 21 | ``` 22 | 23 | Usage 24 | ===== 25 | 26 | In the root of your project, run: 27 | 28 | ```bash 29 | $ sudo upstarter 30 | ``` 31 | 32 | **upstarter** will ask you some stuff. When the survey is complete, you'll have 33 | a service conf in `/etc/init`. Then you can: 34 | 35 | ```bash 36 | $ sudo service my-service start 37 | ``` 38 | 39 | For more info, try the [Upstart cookbook](http://upstart.ubuntu.com/cookbook/). 40 | 41 | Happy days! 42 | 43 | License 44 | ======= 45 | 46 | MIT -------------------------------------------------------------------------------- /bin/upstarter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../') 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var program = require('commander') 2 | , Handlebars = require('handlebars') 3 | , fs = require('fs') 4 | , path = require('path') 5 | , async = require('async') 6 | 7 | fs.existsSync || (fs.existsSync = path.existsSync); 8 | 9 | var pkgPath = path.resolve('package.json'); 10 | 11 | function defaultName () { 12 | if (fs.existsSync(pkgPath)) return require(pkgPath).name; 13 | return path.basename(process.cwd()); 14 | } 15 | 16 | function defaultDesc () { 17 | if (fs.existsSync(pkgPath)) return require(pkgPath).description; 18 | return 'generated by node-upstarter'; 19 | } 20 | 21 | program 22 | .version(require('./package').version) 23 | .usage('[options] [ -- [args...] ]') 24 | .option('--no-ask', 'Run in non-interactive mode') 25 | .option('-n, --name ', 'Upstart service name (must be alpha-numeric/dashes)', defaultName()) 26 | .option('-d, --description ', 'Upstart service description', defaultDesc()) 27 | .option('--no-log', 'Don\'t log output to /var/log/upstart') 28 | .option('-u, --user ', 'System user to run under (default: root)', 'root') 29 | .option('-f, --files ', 'Set max file descriptors (default: 1000000)', Number, 1000000) 30 | .option('-c, --cwd ', 'Working directory for process (default: cwd)', process.cwd()) 31 | .option('--no-respawn', 'Don\'t respawn automatically') 32 | 33 | var idx = process.argv.indexOf('--'); 34 | 35 | if (~idx) { 36 | program.cmd = process.argv.splice(idx + 1).join(' '); 37 | process.argv.pop(); 38 | } 39 | 40 | program.parse(process.argv); 41 | 42 | program.start = 'started networking'; 43 | program.stop = 'runlevel [016]'; 44 | 45 | var template = Handlebars.compile(fs.readFileSync(path.resolve(__dirname, 'service.hbs'), 'utf8')); 46 | 47 | if (program.cmd) { 48 | confirmOk(); 49 | } 50 | else { 51 | if (!program.ask) { 52 | console.error('must supply for non-interactive mode'); 53 | process.exit(1); 54 | } 55 | var prompts = { 56 | name: 'Upstart service name: ', 57 | cmd: 'Command(s) to run: (hit enter twice when done)', 58 | description: 'Upstart service description: ', 59 | log: 'Log output to /var/log/upstart? (y/n): ', 60 | user: 'System user to run under: ', 61 | files: 'Set max file descriptors: ', 62 | cwd: 'Working directory for process: ', 63 | respawn: 'Respawn automatically? (y/n): ' 64 | }, tasks = []; 65 | Object.keys(prompts).forEach(function (k) { 66 | tasks.push(function (cb) { 67 | var label = prompts[k]; 68 | if (typeof program[k] !== 'boolean' && typeof program[k] !== 'undefined') label = label.replace(/: /, ' (' + program[k] + '): '); 69 | var fn = ~label.indexOf('y/n') ? program.confirm : program.prompt; 70 | fn.call(program, label, function (val) { 71 | if ((typeof val === 'string' && val.length) || typeof val === 'boolean') program[k] = val; 72 | cb(); 73 | }); 74 | }); 75 | }); 76 | async.series(tasks, confirmOk); 77 | } 78 | 79 | function confirmOk () { 80 | if (program.user === 'root') delete program.user; 81 | var p = '/etc/init/' + program.name + '.conf'; 82 | var data = template(program); 83 | if (program.ask) { 84 | console.log('\033[31m', data, '\033[0m'); 85 | program.confirm('about to write this to \033[31m' + p + '\033[0m. is this ok? (y/n) ', function (ok) { 86 | if (ok) writeConf(p, data); 87 | else { console.log('cancelled.'); process.exit() } 88 | }); 89 | } 90 | else { 91 | writeConf(p, data); 92 | } 93 | } 94 | 95 | function writeConf (p, data) { 96 | fs.writeFile(p, data, function(err) { 97 | if (err) { 98 | console.error('error writing file! (did you forget to `sudo`?)'); 99 | console.error(err); 100 | process.exit(1); 101 | } 102 | console.log('wrote ' + p); 103 | process.exit(); 104 | }); 105 | } 106 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Carlos Rodriguez (http://s8f.org/)", 3 | "name": "upstarter", 4 | "description": "Easily create upstart services for your node apps", 5 | "version": "0.2.6", 6 | "bin": "./bin/upstarter", 7 | "dependencies": { 8 | "handlebars": "~1.0.5beta", 9 | "commander": "~1.0.4", 10 | "async": "~0.1.22" 11 | }, 12 | "preferGlobal": true, 13 | "devDependencies": {}, 14 | "optionalDependencies": {}, 15 | "keywords": [ 16 | "upstart", 17 | "ubuntu", 18 | "service", 19 | "daemon", 20 | "daemonize" 21 | ], 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/carlos8f/node-upstarter.git" 25 | }, 26 | "engines": { 27 | "node": ">= 0.6.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /service.hbs: -------------------------------------------------------------------------------- 1 | # {{name}}.conf 2 | 3 | description "{{description}}" 4 | 5 | start on {{start}} 6 | stop on {{stop}} 7 | {{#if user}} 8 | setuid {{user}} 9 | {{/if}} 10 | {{#if files}} 11 | limit nofile {{files}} {{files}} 12 | {{/if}} 13 | {{#if log}} 14 | console log 15 | {{/if}} 16 | script 17 | {{#if cwd}} 18 | mkdir -p {{cwd}} 19 | cd {{cwd}} 20 | {{/if}} 21 | {{cmd}} 22 | end script 23 | {{#if respawn}} 24 | respawn 25 | {{/if}} 26 | --------------------------------------------------------------------------------