├── .gitignore ├── .vscode └── settings.json ├── README.md ├── commands ├── add-user.js ├── index.js ├── init.js ├── install.js ├── list.js ├── prune-jobs.js ├── restart.js ├── run-test.js ├── uninstall.js └── upgrade.js ├── index.js ├── lib ├── add-user.js ├── create-user.js ├── plugin_manager │ ├── create_new_plugin.js │ ├── git.js │ ├── index.js │ ├── install_plugin.js │ ├── list_local_plugins.js │ ├── list_remote_plugins.js │ ├── local_plugins.js │ ├── npm.js │ ├── uninstall_plugin.js │ └── upgrade_plugin.js ├── prune-jobs.js ├── resilient.js ├── run-test.js ├── run.js ├── save-user.js └── start.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | 30 | # IDEA files 31 | .idea 32 | *.iml 33 | 34 | # vi temp files 35 | *.*~ 36 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | strider-cli 2 | =========== 3 | 4 | CLI for Strider 5 | 6 | [![NPM](https://nodei.co/npm/strider-cli.png)](https://nodei.co/npm/strider-cli/) 7 | 8 | ## Available Options 9 | 10 | 11 | ```no-highlight 12 | Usage: node strider [command] [options] 13 | 14 | command 15 | addUser Create a Strider user 16 | restart Restart strider (touch .strider) 17 | list List local plugins. Use --all to fetch all. 18 | install Install a plugin from the ecosystem. 19 | uninstall Uninstall a plugin 20 | upgrade Replace a plugin with the latest version 21 | init Initialize a new plugin for development 22 | runTest Run a test and optionally deploy 23 | pruneJobs Cleanup all jobs except for the latest 20 or custom 24 | 25 | Options: 26 | -v, --version Print version and exit 27 | -m, --plugin_path Specify path to plugins (defaults to node_modules) 28 | ``` 29 | 30 | ## Command Descriptions and options 31 | 32 | ### addUser 33 | 34 | Create a Strider user 35 | 36 | ``` 37 | Options: 38 | -l User's email address 39 | -p User's password 40 | -a Specify if this is an admin (flag) (default: false) 41 | -f Force create user, existing users with the same email address get updated (flag) (default: false) 42 | ``` 43 | 44 | If a user exists with the given email address, you will have an option to update 45 | that user, or cancel the process. 46 | 47 | ### restart 48 | 49 | Restart strider (touch .strider) 50 | 51 | ### list 52 | 53 | Include remote plugins available for install 54 | 55 | ``` 56 | Options: 57 | -a Include remote plugins available for install 58 | ``` 59 | 60 | ### install 61 | 62 | Install a plugin from the ecosystem. 63 | 64 | i.e. `$ strider install plugin-name` 65 | 66 | _Note: `plugin-name` comes from the [ecosystem index](https://github.com/Strider-CD/ecosystem-index/blob/master/plugins.yml), 67 | the plugin name is the top level id, e.g. 'buildbadge'._ 68 | 69 | ### uninstall 70 | 71 | Uninstall a plugin 72 | 73 | i.e. `$ strider uninstall plugin-name` 74 | 75 | ### upgrade 76 | 77 | Replace a plugin with the latest version 78 | 79 | i.e. `$ strider upgrade plugin-name` 80 | 81 | ### init 82 | 83 | Initialize a new plugin for development 84 | 85 | ### runTest 86 | 87 | Run a test and optionally deploy 88 | 89 | ``` 90 | Options: 91 | -l User's email address 92 | -p User's password 93 | -j Project name (include organization name i.e. org/repo-name) 94 | -b Branch name (default: master) 95 | -m Job message (optional) 96 | -d Deploy on green (optional) (flag) 97 | ``` 98 | 99 | ### pruneJobs 100 | 101 | Cleanup/prune all jobs except for the latest 20 or custom (use `-k [your number]`) 102 | 103 | ``` 104 | Options: 105 | -k Number of latest jobs to keep, defaults to 20 106 | -p Project to target, defaults to all projects 107 | -d Just print stats about what will be removed, but do not remove any jobs 108 | ``` -------------------------------------------------------------------------------- /commands/add-user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (deps, parser) { 4 | var addUser = require('../lib/add-user')(deps); 5 | 6 | parser.command('addUser') 7 | .option('email', { 8 | abbr: 'l', 9 | help: 'User\'s email address' 10 | }) 11 | .option('password', { 12 | abbr: 'p', 13 | help: 'User\'s password' 14 | }) 15 | .option('admin', { 16 | abbr: 'a', 17 | help: 'Specify if this user is an admin', 18 | 'default': false, 19 | flag: true 20 | }) 21 | .option('force', { 22 | abbr: 'f', 23 | help: 'Force create user, existing users with the same email address get updated', 24 | 'default': false, 25 | flag: true 26 | }) 27 | .callback(function (opts) { 28 | deps.connect(function (err) { 29 | if (err) { 30 | throw err; 31 | } 32 | 33 | if (opts.email) { 34 | opts.email = opts.email.toString(); 35 | } 36 | 37 | if (opts.password) { 38 | opts.password = opts.password.toString(); 39 | } 40 | 41 | addUser(opts.email, opts.password, opts.admin, opts.force); 42 | }) 43 | }) 44 | .help('Create a Strider user'); 45 | }; -------------------------------------------------------------------------------- /commands/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var order = [ 4 | require('./add-user'), 5 | require('./restart'), 6 | require('./list'), 7 | require('./install'), 8 | require('./uninstall'), 9 | require('./upgrade'), 10 | require('./init'), 11 | require('./run-test'), 12 | require('./prune-jobs') 13 | ]; 14 | 15 | module.exports.setup = function (deps, parser) { 16 | order.forEach(function (fn) { 17 | fn(deps, parser) 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /commands/init.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(deps, parser) { 4 | var pluginManager = require('../lib/plugin_manager')(deps.getPluginPath()()); 5 | 6 | parser.command('init') 7 | .help('Initialize a new plugin for development') 8 | .callback(function (opts) { 9 | pluginManager.createNew(opts); 10 | }); 11 | }; -------------------------------------------------------------------------------- /commands/install.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(deps, parser) { 4 | var pluginManager = require('../lib/plugin_manager')(deps.getPluginPath()()); 5 | 6 | parser.command('install') 7 | .help('Install a plugin from the ecosystem.') 8 | .callback(function(opts){ 9 | var plugin = opts._[1]; 10 | 11 | if (plugin) { 12 | pluginManager.install(plugin, function(err, restart) { 13 | if (err) { 14 | console.error(err.stack); 15 | } else { 16 | if (restart) { 17 | require('../lib/resilient')(deps).restart(); 18 | } 19 | } 20 | }) 21 | } else { 22 | console.error('Please pass in a plugin name. Find plugins with `strider list --all`.'); 23 | } 24 | }); 25 | }; 26 | -------------------------------------------------------------------------------- /commands/list.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(deps, parser) { 4 | var pluginManager = require('../lib/plugin_manager')(deps.getPluginPath()()); 5 | 6 | parser.command('list') 7 | .option('all', { 8 | abbr: 'a', 9 | flag: true, 10 | help: 'Include remote plugins available for install' 11 | }) 12 | .help('List local plugins. Use --all to fetch all.') 13 | .callback(function (opts) { 14 | if (opts.all) { 15 | pluginManager.listRemote(opts); 16 | } else { 17 | console.log('Listing only installed plugins. Use flag `-a` to show all'); 18 | pluginManager.listLocal(opts); 19 | } 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /commands/prune-jobs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (deps, parser) { 4 | var pruneJobs = require('../lib/prune-jobs')(deps); 5 | 6 | parser.command('pruneJobs') 7 | .option('project', { 8 | abbr: 'p', 9 | help: 'Project to target, defaults to all projects if not specified', 10 | }) 11 | .option('keepJobs', { 12 | abbr: 'k', 13 | help: 'Number of latest jobs to keep', 14 | default: 20 15 | }) 16 | .option('dryRun', { 17 | abbr: 'd', 18 | help: 'Just print stats about what will be removed, but do not remove any jobs', 19 | flag: true, 20 | default: false 21 | }) 22 | .callback(function (opts) { 23 | deps.connect(function (err) { 24 | if (err) { 25 | throw err; 26 | } 27 | 28 | pruneJobs(opts.keepJobs, opts.project, opts.dryRun); 29 | }); 30 | }) 31 | .help('Cleanup all jobs except for the latest 20 or custom'); 32 | }; 33 | -------------------------------------------------------------------------------- /commands/restart.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (deps, parser) { 4 | parser.command('restart') 5 | .help('Restart strider (touch .strider)') 6 | .callback(function() { 7 | require('../lib/resilient')(deps).restart(); 8 | }); 9 | }; -------------------------------------------------------------------------------- /commands/run-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(deps, parser) { 4 | var runTest = require('../lib/run-test')(deps); 5 | 6 | parser.command('runTest') 7 | .option('email', { 8 | abbr: 'l', 9 | help: 'User\'s email address' 10 | }) 11 | .option('password', { 12 | abbr: 'p', 13 | help: 'User\'s password' 14 | }) 15 | .option('project', { 16 | abbr: 'j', 17 | help: 'Project name' 18 | }) 19 | .option('branch', { 20 | abbr: 'b', 21 | help: 'Branch name (default: master)' 22 | }) 23 | .option('message', { 24 | abbr: 'm', 25 | help: 'Commit message (optional)' 26 | }) 27 | .option('deploy', { 28 | abbr: 'd', 29 | flag: true, 30 | help: 'Deploy on green' 31 | }) 32 | .callback(function(opts){ 33 | if (opts.email) { 34 | opts.email = opts.email.toString(); 35 | } 36 | 37 | if (opts.password) { 38 | opts.password = opts.password.toString(); 39 | } 40 | 41 | if (opts.project) { 42 | opts.project = opts.project.toString(); 43 | } 44 | 45 | if (opts.branch) { 46 | opts.branch = opts.branch.toString(); 47 | } 48 | 49 | if (opts.message) { 50 | opts.message = opts.message.toString(); 51 | } 52 | 53 | runTest(opts.email, opts.password, opts.project, opts.branch, opts.message, opts.deploy); 54 | }) 55 | .help('Run a test and optionally deploy'); 56 | }; -------------------------------------------------------------------------------- /commands/uninstall.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(deps, parser) { 4 | var pluginManager = require('../lib/plugin_manager')(deps.getPluginPath()()); 5 | 6 | parser.command('uninstall') 7 | .help('Uninstall a plugin') 8 | .callback(function (opts) { 9 | var plugin = opts._[1]; 10 | 11 | if (plugin) { 12 | pluginManager.uninstall(plugin, function (err, restart) { 13 | if (err) { 14 | console.error(err.stack); 15 | } else { 16 | if (restart) { 17 | require('../lib/resilient')(deps).restart(); 18 | } 19 | } 20 | }) 21 | } else { 22 | console.error('Please pass in a plugin name. See installed plugins with `strider list`.'); 23 | } 24 | }); 25 | }; -------------------------------------------------------------------------------- /commands/upgrade.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (deps, parser) { 4 | var pluginManager = require('../lib/plugin_manager')(deps.getPluginPath()()); 5 | 6 | parser.command('upgrade') 7 | .help('Upgrade a plugin') 8 | .callback(function (opts) { 9 | var plugin = opts._[1]; 10 | 11 | if (plugin) { 12 | pluginManager.upgrade(plugin, function (err, restart) { 13 | if (err) { 14 | console.error(err.stack); 15 | } else { 16 | if (restart) { 17 | require('../lib/resilient')(deps).restart(); 18 | } 19 | } 20 | }) 21 | } else { 22 | console.error('Please pass in a plugin name. See installed plugins with `strider list`.'); 23 | } 24 | }); 25 | }; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parser = require('nomnom'); 4 | 5 | module.exports = function(deps) { 6 | var start = require('./lib/start')(deps); 7 | 8 | parser 9 | .option('version', { 10 | abbr: 'v', 11 | flag: true, 12 | help: 'Print version and exit', 13 | callback: function() { 14 | return deps.version; 15 | } 16 | }) 17 | .option('plugin_path', { 18 | abbr: 'm', 19 | help: 'Specify path to plugins (defaults to node_modules)' 20 | }); 21 | 22 | var commands = require('./commands'); 23 | 24 | commands.setup(deps, parser); 25 | 26 | parser.nocommand('start') 27 | .option('no-cluster', { 28 | help: 'Bypass the cluster module when starting Strider. Disables self-restart.' 29 | }) 30 | .callback(function(opts) { 31 | start(opts.extension_path, opts); 32 | }); 33 | 34 | return { 35 | parser: parser, 36 | start: start 37 | }; 38 | }; -------------------------------------------------------------------------------- /lib/add-user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Step = require('step'); 4 | var fs = require('fs'); 5 | var readline = require('readline'); 6 | var pw = require('pw'); 7 | 8 | module.exports = function(deps) { 9 | var saveUser = require('./save-user')(deps); 10 | var User = deps.models().User; 11 | 12 | function addUser(email, password, admin, force) { 13 | var level = admin ? 1 : 0; 14 | 15 | if (!email || !password) { 16 | var rl = readline.createInterface({ 17 | input: process.stdin, 18 | output: process.stdout 19 | }); 20 | 21 | Step(function getEmail() { 22 | var next = this; 23 | 24 | if (email) { 25 | next(); 26 | } else { 27 | rl.question('Enter email []: ', function (em) { 28 | email = em; 29 | next(); 30 | }); 31 | } 32 | }, 33 | 34 | function getPwd() { 35 | var next = this; 36 | 37 | if (password) { 38 | next(); 39 | } else { 40 | rl.close(); 41 | process.stdout.write('Enter password []: '); 42 | 43 | pw(function (pwd) { 44 | password = pwd; 45 | 46 | rl = readline.createInterface({ 47 | input: process.stdin, 48 | output: process.stdout 49 | }); 50 | 51 | next(); 52 | }) 53 | } 54 | }, 55 | 56 | function getAdmin() { 57 | var next = this; 58 | 59 | if (level) { 60 | next(); 61 | } else { 62 | rl.question('Is admin? (y/n) [n]', function (a) { 63 | level = a ? 1 : undefined; 64 | next(); 65 | }); 66 | } 67 | }, 68 | 69 | function confirm() { 70 | var next = this; 71 | 72 | process.stdout.write('\nEmail:\t\t' + email + '\n'); 73 | process.stdout.write('Password:\t' + password.replace(/.*/, '****') + '\n'); 74 | process.stdout.write('isAdmin:\t' + (level ? 'y' : 'n') + '\n'); 75 | 76 | if (force) { 77 | next(); 78 | } 79 | else { 80 | rl.question('OK? (y/n) [y]', function (ok) { 81 | if (ok === 'y' || ok === '') { 82 | next(); 83 | } else { 84 | console.log('Goodbye!'); 85 | process.exit(); 86 | } 87 | }); 88 | } 89 | }, 90 | 91 | function save() { 92 | saveUser(email, password, level, rl, force); 93 | }); 94 | } else { 95 | saveUser(email, password, level, rl, force); 96 | } 97 | } 98 | 99 | return addUser; 100 | } 101 | -------------------------------------------------------------------------------- /lib/create-user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(deps) { 4 | var User = deps.models().User; 5 | 6 | function createUser(email, password, admin, rl, force) { 7 | User.findByEmail(email, function (err, users) { 8 | if (err) { 9 | console.error('Failed to lookup users, please let us know at https://github.com/Strider-CD/strider-cli/issues: ', err); 10 | process.exit(1); 11 | } 12 | 13 | if (users.length) { 14 | overwrite(rl, force, function (yes) { 15 | if (yes) { 16 | User.update({ email: email }, { 17 | password: password, 18 | account_level: admin 19 | }, function (err) { 20 | if (err) { 21 | console.log('Error updating user:', err); 22 | process.exit(1); 23 | } 24 | 25 | console.log('User updated successfully! Enjoy.'); 26 | process.exit(); 27 | }); 28 | } else { 29 | console.log('addUser cancelled'); 30 | process.exit(); 31 | } 32 | }); 33 | } else { 34 | var u = new User(); 35 | 36 | u.email = email; 37 | u.created = new Date(); 38 | u.set('password', password); 39 | u.account_level = admin; 40 | 41 | u.save(function(err) { 42 | if (err) { 43 | console.log('Error adding user:', err); 44 | process.exit(1); 45 | } 46 | 47 | console.log('User created successfully! Enjoy.'); 48 | process.exit(); 49 | }); 50 | } 51 | }); 52 | } 53 | 54 | return createUser; 55 | } 56 | 57 | function overwrite(rl, force, cb) { 58 | if (force) { 59 | process.nextTick(function () { 60 | cb(force); 61 | }); 62 | } 63 | else { 64 | rl.question('User already exists, overwrite? (y/n) [n]: ', function (overwrite) { 65 | rl.close(); 66 | cb(overwrite === 'y' || overwrite === ''); 67 | }); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/plugin_manager/create_new_plugin.js: -------------------------------------------------------------------------------- 1 | var prompt = require('prompt') 2 | var path = require('path') 3 | var git = require('./git') 4 | var fs = require('fs') 5 | 6 | module.exports = function (pluginsPath) { 7 | var localPlugins = require('./local_plugins')(pluginsPath) 8 | var pluginsPath = localPlugins.path() 9 | prompt.message = "plugin"; 10 | prompt.start() 11 | var schema = { 12 | properties: { 13 | name: { 14 | pattern: /^[a-z\_\-]+$/, 15 | message: 'Name must be only lowercase letters, underscores, or dashes', 16 | required: true 17 | }, 18 | description: { 19 | required: true 20 | }, 21 | author: {} 22 | } 23 | } 24 | prompt.get(schema, function (err, res) { 25 | if (err) { 26 | console.error(err.message) 27 | return; 28 | } 29 | var pluginPath = path.join(pluginsPath, res.name) 30 | if (fs.existsSync(pluginPath)) { 31 | console.error(res.name+' already exists: '+pluginPath) 32 | } else { 33 | git.clone('https://github.com/Strider-CD/strider-template.git', 'v1.0.1', pluginPath, function(err) { 34 | if (err) throw err; 35 | var pkgPath = path.join(pluginPath, 'package.json') 36 | fs.readFile(pkgPath, function (err, jsonFile) { 37 | if (err) throw err; 38 | var pkg = JSON.parse(jsonFile) 39 | Object.keys(schema.properties).forEach(function (key) { 40 | pkg[key] = res[key] 41 | }) 42 | pkg["strider"]["id"] = res.name 43 | fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2), function () { 44 | console.log( 45 | ["", "A strider plugin template has been prepared for you in the following directory" 46 | ,"\t"+pluginPath 47 | ,"Please view the README and begin editing the package.json." 48 | ,"Make sure to change the git remote to wherever you're hosting your plugin source code" 49 | ,"When you're ready to publish, submit a pull request to https://github.com/Strider-CD/strider-plugins" 50 | ,"If you have any questions or need help, you can find us in irc.freenode.net #strider" 51 | ].join("\n")) 52 | }) 53 | }) 54 | }) 55 | } 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /lib/plugin_manager/git.js: -------------------------------------------------------------------------------- 1 | var spawn = require('spawn-cmd').spawn; 2 | 3 | module.exports = { 4 | clone: function(repo, tag, path, cb) { 5 | 6 | var proc = spawn('git', [ 7 | 'clone', 8 | '--branch', tag, 9 | '--depth', '1', 10 | repo, path 11 | ]); 12 | 13 | var errors = [] 14 | 15 | proc.stderr.on('data', function(chk) { 16 | errors.push(chk.toString()); 17 | }) 18 | 19 | proc.on('error', function(err) { 20 | return cb(err); 21 | }); 22 | 23 | proc.on('close', function (code) { 24 | if (code !== 0) { 25 | return cb(new Error('git clone failed with non-zero status '+code+".\n"+errors.join('\n'))) 26 | } else { 27 | return cb(null) 28 | } 29 | }) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/plugin_manager/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function (pluginsPath) { 2 | 3 | return { 4 | listLocal: function() { 5 | return require('./list_local_plugins')(pluginsPath) 6 | }, 7 | listRemote: function() { 8 | return require('./list_remote_plugins')(pluginsPath) 9 | }, 10 | createNew: function() { 11 | return require('./create_new_plugin')(pluginsPath) 12 | }, 13 | install: function(name, cb) { 14 | return require('./install_plugin')(pluginsPath)(name, cb) 15 | }, 16 | uninstall: function(name, cb) { 17 | return require('./uninstall_plugin')(pluginsPath)(name, cb) 18 | }, 19 | upgrade: function(name, cb) { 20 | return require('./upgrade_plugin')(pluginsPath)(name, cb) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/plugin_manager/install_plugin.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash') 2 | , fs = require('fs') 3 | , path = require('path') 4 | , git = require('./git') 5 | , npm = require('./npm') 6 | , client = require('strider-ecosystem-client') 7 | 8 | module.exports = function(pluginsPath) { 9 | var local = require('./local_plugins')(pluginsPath) 10 | var home = pluginsPath[0] 11 | 12 | /* 13 | * Callback signature: 14 | * cb(Error anyError, Boolean restartOrNot) 15 | */ 16 | return function(name, cb) { 17 | local.listAllZipped(function (err, localPlugins) { 18 | if (err) return cb(err); 19 | client.fetchPlugins().then(function(remotePlugins) { 20 | var local = localPlugins[name] 21 | var remote = remotePlugins[name] 22 | if (!remote) { 23 | console.error(name+' is not a valid plugin') 24 | return cb(); 25 | } 26 | 27 | var pluginPath = path.join(home, remote.module_name) 28 | 29 | if (local || fs.existsSync(pluginPath)) { 30 | afterCloned(pluginPath, cb) 31 | } else if (remote) { 32 | git.clone(remote.repo, remote.tag, pluginPath, function(err) { 33 | if (err) return cb(err) 34 | afterCloned(pluginPath, cb) 35 | }) 36 | } 37 | }).error(cb).catch(cb) 38 | }) 39 | } 40 | } 41 | 42 | function afterCloned(pluginPath, cb) { 43 | npm(pluginPath).install(function(err) { 44 | if (err) return cb(err); 45 | else return cb(null, true) 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /lib/plugin_manager/list_local_plugins.js: -------------------------------------------------------------------------------- 1 | module.exports = function(pluginsPath) { 2 | var Table = require('cli-table'); 3 | var localPlugins = require('./local_plugins')(pluginsPath) 4 | var table = new Table({ 5 | chars: {'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''}, 6 | head: ['name', 'version'] 7 | }); 8 | localPlugins.listAll(function (err, plugins) { 9 | if (err) throw err; 10 | plugins.forEach(function (plugin) { 11 | table.push([ 12 | plugin.name, 13 | plugin.version 14 | ]) 15 | }) 16 | console.log(table.toString()); 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /lib/plugin_manager/list_remote_plugins.js: -------------------------------------------------------------------------------- 1 | module.exports = function(pluginsPath) { 2 | var _ = require('lodash') 3 | var Table = require('cli-table'); 4 | var table = new Table({ 5 | chars: {'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''}, 6 | head: ['name', 'description', 'stable', 'installed'] 7 | }); 8 | 9 | var local = require('./local_plugins')(pluginsPath) 10 | 11 | var client = require('strider-ecosystem-client') 12 | 13 | client.fetchPlugins().then(function(remotePlugins) { 14 | local.listAllZipped(function (err, localPlugins) { 15 | Object.keys(remotePlugins).forEach(function (name) { 16 | var remote = remotePlugins[name] 17 | var local = localPlugins[name] 18 | console.log(remote); 19 | table.push([ 20 | name, 21 | remote.description, 22 | remote.tag, 23 | local ? local.version : 'no' 24 | ]) 25 | }) 26 | console.log(table.toString()); 27 | }) 28 | }).error(errHandle).catch(errHandle) 29 | } 30 | 31 | function errHandle(err) { 32 | console.error('Error!\n'+err.message+'\n'+err.stack) 33 | } 34 | -------------------------------------------------------------------------------- /lib/plugin_manager/local_plugins.js: -------------------------------------------------------------------------------- 1 | module.exports = function(pluginsPath) { 2 | var Loader = require('strider-extension-loader') 3 | var loader = new Loader(); 4 | 5 | var path = require('path') 6 | var _ = require('lodash') 7 | 8 | return { 9 | path: fullPath, 10 | listAll: listAll, 11 | listAllZipped: listAllZipped 12 | } 13 | 14 | function zip(plugins) { 15 | var ids = _.pluck(plugins, 'name') 16 | return _.zipObject(ids, plugins) 17 | } 18 | 19 | function fullPath() { 20 | return pluginsPath[0] 21 | } 22 | 23 | function getVersion(pluginPath) { 24 | return require(path.join(pluginPath, 'package.json')).version 25 | } 26 | 27 | function listAll(cb) { 28 | loader.collectExtensions(pluginsPath, function(err) { 29 | var plugins = [] 30 | var extensions = loader.extensions; 31 | for (var groupName in extensions) { 32 | var group = extensions[groupName] 33 | for (var pluginName in group) { 34 | var plugin = group[pluginName] 35 | plugins.push({ 36 | group: groupName, 37 | name: pluginName, 38 | path: plugin.dir, 39 | version: getVersion(plugin.dir), 40 | title: plugin.title 41 | }) 42 | } 43 | } 44 | cb(err, plugins); 45 | }) 46 | } 47 | 48 | function listAllZipped(cb) { 49 | listAll(function(err, plugins) { 50 | cb(err, zip(plugins)) 51 | }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/plugin_manager/npm.js: -------------------------------------------------------------------------------- 1 | var spawn = require('spawn-cmd').spawn; 2 | 3 | module.exports = function(cwd) { 4 | return { 5 | install: function(cb) { 6 | var proc = spawn('npm', [ 'install', '--production', '--ignore-scripts' ], { 7 | stdio: 'inherit', 8 | cwd: cwd 9 | }) 10 | 11 | proc.on('error', function(err) { 12 | return cb(err); 13 | }); 14 | 15 | proc.on('close', function (code) { 16 | if (code !== 0) { 17 | return cb(new Error('npm install failed with non-zero status '+code)) 18 | } else { 19 | return cb(null) 20 | } 21 | }) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/plugin_manager/uninstall_plugin.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash') 2 | , fs = require('fs') 3 | , path = require('path') 4 | , rimraf = require('rimraf') 5 | 6 | // these are plugins you can't uninstall 7 | var crucialPlugins = [ 8 | 'git', 9 | 'simple-runner' 10 | ] 11 | 12 | module.exports = function(pluginsPath) { 13 | var local = require('./local_plugins')(pluginsPath) 14 | 15 | /* 16 | * Callback signature: 17 | * cb(Error anyError, Boolean restartOrNot) 18 | */ 19 | return function(name, cb) { 20 | if (crucialPlugins.indexOf(name) > -1) { 21 | return cb(new Error('This plugin cannot be uninstalled')) 22 | } 23 | local.listAllZipped(function (err, plugins) { 24 | var plugin = plugins[name] 25 | if (plugin) { 26 | rimraf(plugin.path, function(err) { 27 | cb(err, true) 28 | console.log('removed '+plugin.path) 29 | }) 30 | } else { 31 | console.error(name+' not found') 32 | cb() 33 | } 34 | }) 35 | } 36 | } 37 | 38 | function afterDelete(pluginPath, cb) { 39 | if (err) return cb(err); 40 | else return cb(null, true) 41 | } 42 | -------------------------------------------------------------------------------- /lib/plugin_manager/upgrade_plugin.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash') 2 | , fs = require('fs') 3 | , path = require('path') 4 | , rimraf = require('rimraf') 5 | , install = require('./install_plugin') 6 | 7 | module.exports = function(pluginsPath) { 8 | var local = require('./local_plugins')(pluginsPath) 9 | 10 | /* 11 | * Callback signature: 12 | * cb(Error anyError, Boolean restartOrNot) 13 | */ 14 | return function(name, cb) { 15 | local.listAllZipped(function (err, plugins) { 16 | var plugin = plugins[name] 17 | if (plugin) { 18 | rimraf(plugin.path, function(err) { 19 | console.log('removed '+plugin.path) 20 | install(pluginsPath)(name, cb) 21 | }) 22 | } else { 23 | console.error(name+' not found') 24 | cb() 25 | } 26 | }) 27 | } 28 | } 29 | 30 | function afterDelete(pluginPath, cb) { 31 | if (err) return cb(err); 32 | else return cb(null, true) 33 | } 34 | -------------------------------------------------------------------------------- /lib/prune-jobs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Bluebird = require('bluebird'); 4 | var colors = require('colors'); 5 | 6 | module.exports = function(deps) { 7 | var models = deps.models(); 8 | var Project = models.Project; 9 | var Job = models.Job; 10 | 11 | return function pruneJobs(keepNumJobs, projectName, dryRun) { 12 | if (dryRun) { 13 | console.log('** Dry Run **\n'); 14 | } 15 | 16 | findProjects(projectName, Project).then(function (projects) { 17 | return Bluebird.map(projects, function (project) { 18 | return removeJobsAfterLatest(keepNumJobs, project.name, dryRun, Job); 19 | }) 20 | }).catch(function (err) { 21 | console.log(colors.red(err.message || err)); 22 | }).finally(function () { 23 | process.exit(1); 24 | }); 25 | }; 26 | }; 27 | 28 | /** 29 | * Find all projects, or just the one specified 30 | * 31 | * @private 32 | * @param {String} name name identified or project, user/name format 33 | * @param {Model} Project mongoose model 34 | * @returns {Promise} resolves to an array of projects 35 | */ 36 | function findProjects(name, Project) { 37 | var query; 38 | 39 | if (name) { 40 | query = Project.findOne({ name: name }).exec(); 41 | } else { 42 | query = Project.find().exec(); 43 | } 44 | 45 | return query.then(function (res) { 46 | return res.length ? res : [res]; 47 | }); 48 | } 49 | 50 | /** 51 | * Finds all jobs for project, and removes if not a dry run; 52 | * 53 | * @private 54 | * @param {Number} keepJobs number of jobs to keep, defaults to 20 55 | * @param {String} projectName user/name format 56 | * @param {Boolean} dryRun just print the stats, no actual removal done 57 | * @returns {Promise} resolves once stats printed, or jobs removed 58 | */ 59 | function removeJobsAfterLatest(keepJobs, projectName, dryRun, Job) { 60 | return Job.find({ project: projectName }) 61 | .select('created') 62 | .sort('-created') 63 | .exec().then(function (jobs) { 64 | var toRemove = jobs.slice(keepJobs); 65 | 66 | logStats(projectName, jobs, toRemove, dryRun); 67 | 68 | if (!dryRun && toRemove.length) { 69 | return Bluebird.map(toRemove, function (job) { 70 | return job.remove(); 71 | }).then(function () { 72 | console.log(colors.yellow(' ' + toRemove.length + ' jobs removed')); 73 | }); 74 | } 75 | }); 76 | } 77 | 78 | /** 79 | * Log stats for job removal of a project 80 | * 81 | * @private 82 | * @param {String} projectName 83 | * @param {Array} allJobs 84 | * @param {Array} toRemove 85 | */ 86 | function logStats(projectName, allJobs, toRemove) { 87 | if (!toRemove.length) { 88 | return console.log(colors.green('No jobs to remove')); 89 | } 90 | 91 | var log = [ 92 | colors.green(colors.bold('Removing jobs for "' + projectName + '":')), 93 | ' Keeping Latest ' + (allJobs.length - toRemove.length) + ' jobs.', 94 | ' Total Jobs: ' + allJobs.length, 95 | ' Latest job created on: ' + allJobs[0].created, 96 | ' Oldest job created on: ' + allJobs[allJobs.length - 1].created, 97 | ' Jobs To Remove: ' + toRemove.length, 98 | ' Latest job created on: ' + toRemove[0].created, 99 | ' Oldest job created on: ' + toRemove[toRemove.length - 1].created 100 | ]; 101 | 102 | console.log(log.join('\n')); 103 | } -------------------------------------------------------------------------------- /lib/resilient.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var cluster = require('cluster'); 4 | var chokidar = require('chokidar'); 5 | var touch = require('touch'); 6 | 7 | module.exports = function (deps) { 8 | var flag = deps.restartFile(); 9 | 10 | return { 11 | restart: function () { 12 | touch.sync(flag); 13 | console.log('touched ' + flag); 14 | }, 15 | spawn: function (work, noCluster) { 16 | if (noCluster) return work(); 17 | if (cluster.isMaster) { 18 | var watcher = chokidar.watch(flag) 19 | 20 | cluster.on('online', function(worker) { 21 | console.log(worker.process.pid +' forked'); 22 | watcher.removeAllListeners().on('change', function() { 23 | console.log('restart flag touched'); 24 | worker.kill(); 25 | }); 26 | }); 27 | 28 | cluster.on('exit', function(worker, code, signal) { 29 | console.log(worker.process.pid + ' died', code, signal); 30 | cluster.fork(); 31 | }); 32 | 33 | cluster.fork(); 34 | } else { 35 | work(); 36 | } 37 | } 38 | }; 39 | }; -------------------------------------------------------------------------------- /lib/run-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Step = require('step'); 4 | var readline = require('readline'); 5 | var run = require('./run'); 6 | var debug = require('debug')('strider:cli'); 7 | 8 | module.exports = function(deps) { 9 | var runOpts = { 10 | server_name: deps.config().server_name 11 | }; 12 | 13 | function runTest(email, password, project, branch, message, deploy) { 14 | if(!email || !password || !project){ 15 | var rl = readline.createInterface({ 16 | input: process.stdin, 17 | output: process.stdout 18 | }); 19 | 20 | Step( 21 | function getEmail() { 22 | var next = this; 23 | 24 | if (email){ 25 | next(); 26 | } 27 | else { 28 | rl.question('Enter email []: ', function (em) { 29 | email = em; 30 | next(); 31 | }); 32 | } 33 | }, 34 | 35 | function getPwd() { 36 | var next = this; 37 | 38 | if (password){ 39 | next(); 40 | } else { 41 | rl.question('Enter password []: ', function (pwd) { 42 | password = pwd; 43 | next(); 44 | }); 45 | } 46 | }, 47 | 48 | function getProject() { 49 | var next = this; 50 | 51 | if (project){ 52 | next(); 53 | } else { 54 | rl.question('Project name []: ', function (pr) { 55 | project = pr; 56 | next(); 57 | }); 58 | } 59 | }, 60 | 61 | function getMessage() { 62 | var next = this; 63 | if (message) { 64 | next(); 65 | } else { 66 | rl.question('Commit message (optional): ', function (msg) { 67 | message = msg; 68 | next(); 69 | }) 70 | } 71 | }, 72 | 73 | function getBranch() { 74 | var next = this; 75 | if (branch) { 76 | next(); 77 | } else { 78 | rl.question('Branch (default: master): ', function (br) { 79 | branch = br; 80 | next(); 81 | }) 82 | } 83 | }, 84 | 85 | function getDeploy() { 86 | var next = this; 87 | if (deploy) { 88 | next(); 89 | } else { 90 | rl.question('Deploy (y/n) (default: n): ', function (a) { 91 | deploy = a === 'y' ? true : undefined; 92 | next(); 93 | }) 94 | } 95 | }, 96 | 97 | function runTest() { 98 | runOpts.email = email; 99 | runOpts.password = password; 100 | runOpts.message = message; 101 | runOpts.project = project; 102 | runOpts.branch = branch; 103 | runOpts.deploy = deploy; 104 | run(runOpts); 105 | } 106 | ); 107 | } 108 | else { 109 | runOpts.email = email; 110 | runOpts.password = password; 111 | runOpts.message = message; 112 | runOpts.project = project; 113 | runOpts.branch = branch; 114 | runOpts.deploy = deploy; 115 | run(runOpts); 116 | } 117 | } 118 | 119 | return runTest; 120 | } 121 | -------------------------------------------------------------------------------- /lib/run.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var request = require('superagent'); 4 | var debug = require('debug')('strider:cli'); 5 | 6 | function run(opts) { 7 | var agent = request.agent(); 8 | var serverName = opts.server_name; 9 | var project = opts.project; 10 | var branch = opts.branch; 11 | var message = opts.message; 12 | var deploy = opts.deploy; 13 | var url = serverName + '/api/session'; 14 | 15 | debug('opts: %j', opts); 16 | 17 | request.post(url) 18 | .send({ 19 | email: opts.email, 20 | password: opts.password 21 | }) 22 | .end(function (err, res) { 23 | if (!err && res.statusCode !== 404) { 24 | agent.saveCookies(res); 25 | url = serverName + '/' + project + '/start'; 26 | 27 | var req = request.post(url); 28 | var postData = { branch: branch || 'master' }; 29 | 30 | agent.attachCookies(req); 31 | 32 | if (message) { 33 | postData.message = message; 34 | } 35 | 36 | if (deploy) { 37 | postData.type = 'TEST_AND_DEPLOY'; 38 | } 39 | 40 | req.send(postData); 41 | 42 | req.end(function (err, res) { 43 | if (!err && res.statusCode !== 404) { 44 | console.log('Job started'); 45 | } 46 | else if (res.statusCode === 404) { 47 | console.log('Error: Repo was not found'); 48 | } 49 | else { 50 | console.log('Error: ', err); 51 | } 52 | 53 | process.exit(); 54 | }); 55 | } 56 | else { 57 | console.error('Login error', err, res); 58 | process.exit(1); 59 | } 60 | }); 61 | } 62 | 63 | module.exports = run; 64 | -------------------------------------------------------------------------------- /lib/save-user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(deps) { 4 | var createUser = require('./create-user')(deps); 5 | var Upgrade = deps.upgrade(); 6 | var Config = deps.models().Config; 7 | 8 | function saveUser(email, password, admin, rl, force) { 9 | Upgrade.isFreshDb(function (err, isFresh) { 10 | if (isFresh) { 11 | Upgrade.needConfigObj(function (err, needsConfig) { 12 | if (needsConfig) { 13 | var c = new Config(); 14 | 15 | c.version = Config.SCHEMA_VERSION; 16 | 17 | c.save(function () { 18 | createUser(email, password, admin, rl, force); 19 | }); 20 | } else { 21 | createUser(email, password, admin, rl, force); 22 | } 23 | }); 24 | } else { 25 | Upgrade.ensure(Config.SCHEMA_VERSION, function () { 26 | createUser(email, password, admin, rl, force); 27 | }); 28 | } 29 | }); 30 | } 31 | 32 | return saveUser; 33 | } 34 | -------------------------------------------------------------------------------- /lib/start.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (deps) { 4 | var resilient = require('./resilient')(deps); 5 | 6 | return function(extpath, opts) { 7 | var path = require('path'); 8 | var extdir = deps.getPluginPath()(extpath); 9 | // Save extension dir 10 | deps.common().extdir = extdir; 11 | 12 | resilient.spawn(function() { 13 | deps.main()(extdir); 14 | }, opts.cluster === false); 15 | } 16 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strider-cli", 3 | "version": "2.0.2", 4 | "description": "CLI for Strider", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/Strider-CD/strider-cli" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/Strider-CD/strider-cli/issues" 17 | }, 18 | "homepage": "https://github.com/Strider-CD/strider-cli", 19 | "dependencies": { 20 | "bluebird": "^3.4.7", 21 | "chokidar": "^0.9.0", 22 | "cli-table": "^0.3.0", 23 | "colors": "^1.1.2", 24 | "debug": "^2.1.1", 25 | "glob": "^4.0.6", 26 | "lodash": "^2.4.1", 27 | "nomnom": "^1.8.0", 28 | "prompt": "^0.2.14", 29 | "pw": "0.0.4", 30 | "rimraf": "^2.2.8", 31 | "spawn-cmd": "0.0.2", 32 | "step": "0.0.5", 33 | "strider-ecosystem-client": "1.2.1", 34 | "strider-extension-loader": "^0.4.4", 35 | "superagent": "^0.20.0", 36 | "touch": "0.0.3" 37 | } 38 | } 39 | --------------------------------------------------------------------------------