diff --git a/README.md b/README.md index 33fddce..c4de202 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -Strider-simple-runner -===================== +## Strider-simple-runner [![Build Status](https://hosted.stridercd.com/51f050cf04568a0c00000008/strider-cd/strider-simple-worker/badge)](https://hosted.stridercd.com/strider-cd/strider-simple-worker/) @@ -8,6 +7,119 @@ Strider-simple-runner Easy-to-configure in-process runner implementation for Strider Continous Deployment. This runner comes bundled with Strider. +## Spec + +`require('strider-simple-runner').create(emitter, config, done)` + +### Config + +All options are optional. + +``` +pty(false): use 'pty' for running commands. Currently disabled +logger(console): .log, .warn, .error +io(new EventEmitter): the channel of internal communication with the job worker +processJob(core.process):function to run a job. (task, config, ondone) +pluginDirs: the directories in which to look for plugins +dataDir($HOME/.strider): the directory in which to clone/test/etc +``` + +### Events + +#### Expected to consume + +- 'job.new' +``` +{ + id: Oid, + type: 'TEST_ONLY' | 'TEST_AND_DEPLOY', + user_id: Oid, + trigger: { + }, + ref: { + branch: String, + id: String // commit id + }, + // this stuff is not part of the "job" document in mongo, but is added + project: { + // project config straight from db, includes plugin config and + // project level provider config + }, + userProvider: { // user-level config for the provider. Things like a github + } // OAuth token. Retrieved from user.providers.[providerid] +} +``` + +Ex: github provider config +```js +{ + id: 'github', + user: { + token: '1234', + username: 'hacker' + }, + project: { + url: 'example.com/repo.git', + display_url: 'http://example.com/repo', + auth: { + method: 'https', + username: null, // use user's gh auth + password: null // use user's gh token + } + } +} +``` + +Ex: git provider config +```js +{ + id: 'git', + user: {}, + project: { + url: 'example.com/repo.git', + method: 'ssh', + privkey: null, // use repo-level ssh keys + pubkey: null + } +} +``` + +Project config looks like: + +```js +{ + name: 'owner/name', + public: false, + active: true, + deploy_on_green: true, + secret: // what's this for? + runner: { + id: 'simple', // or docker, etc. + pty: false + // other config for the runner + }, + privkey: '', + pubkey: '', + provider: {}, // provider config + // owner is implicit, as it's embedded ... + collaborators: [], + plugins: [{ + id: 'heroku', + // plugin config + }, ...] +} +``` + +Plugins needed: + +- heroku +- webhooks + +Providers: + +- git +- github + Tests ===== diff --git a/index.js b/index.js index 2bc6baf..52c7c70 100644 --- a/index.js +++ b/index.js @@ -1,19 +1,12 @@ var Runner = require('./lib') + , Loader = require('strider-extension-loader') var create = function(emitter, opts, cb){ - console.log(">>>>> RUNNER:: CREATE", arguments) + console.log(">>>>> RUNNER:: CREATED") + var loader = new Loader() var runner = new Runner(emitter, opts) - - // TEMP - assume all the extensions are loaded. This - // will be replaced when we pass the extension config - // through in the run events. - - var pth = require.main.paths - loader.initWorkerExtensions(pth, runner.buildContext({}, pth), function(){ - console.log("!!!!! RUNNER CREATE>", arguments) - cb(null) - }) + runner.loadExtensions(require.main.paths, cb) } module.exports = { @@ -25,9 +18,14 @@ module.exports = { // --> 'job.new' { repo: {...}, job: {...} } // --> 'job.cancel' jobid // Events it is expected to emit - // --> 'browser.update' eventname, data - // --> 'job.queued' jobid, time - // --> 'job.done' { id: jobid, commands: [...], times: {...}, test_status: int, deploy_status: int } + // <-- 'browser.update' eventname, data + // <-- 'job.queued' jobid, time + // <-- 'job.done' { id: jobid, commands: [...], times: {...}, test_status: int, deploy_status: int } + + // Backwards compat: TODO remove + // --> queue.new_job + // <-- queue.job_update + // <-- queue.job_complete // We expose these for other modules / unit testing Runner : Runner diff --git a/lib/consts.js b/lib/consts.js index 6978ca8..f595f69 100644 --- a/lib/consts.js +++ b/lib/consts.js @@ -6,13 +6,19 @@ var phases = ['env', 'prepare', 'test', 'deploy', 'cleanup'] var skels = { job: { id: null, + data: null, phases: {}, phase: phases[0], queued: null, started: null, finished: null, test_status: -1, - deploy_status: -1 + deploy_status: -1, + std: { + out: '', + err: '', + merged: '' + } }, command: { out: '', diff --git a/lib/index.js b/lib/index.js index 2c9f4ca..2dd42bf 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,6 +3,7 @@ var _ = require('lodash') , path = require('path') , async = require('async') , EventEmitter2 = require('eventemitter2').EventEmitter2 + , Loader = require('strider-extension-loader') , core = require('strider-runner-core') @@ -34,10 +35,26 @@ function Runner(emitter, config) { this.io = this.config.io this.hooks = [] this.jobdata = new JobData(this.io) + this.attach() } Runner.prototype = { // public API + loadExtensions: function (dirs, done) { + var self = this + , loader = new Loader() + loader.collectExtensions(require.main.paths, function (err) { + if (err) return done(err) + loader.initWorkerExtensions(self.getContext(), function (err, extensions) { + if (err) return done(err) + self.extensions = extensions + done(null) + }) + }) + }, + getContext: function () { + return {} + }, // private API attach: function () { @@ -53,18 +70,54 @@ Runner.prototype = { // our running job was cancelled, so we need to start the next one async.setImmediate(self.queue.process) }) + // proxy up plugin events + this.io.on('plugin.*', function () { + self.emitter.emit.apply(self.emitter, [this.event].concat([].slice.call(arguments))) + }) + + // Backwards compat: TODO remove + this.io.on('job.status.std*', function (id, text) { + var which = this.event.split('.')[2] + , data = { + jobId: id, + stdout: '', + stderr: '' + } + , job = self.jobdata.get(id) + data.userId = job.data.user_id + // # seconds since it started + data.timeElapsed = (new Date().getTime() - job.started.getTime())/1000 + data[which] = text + data.repoUrl = job.provider.project.display_url + self.emitter.emit('queue.job_update', data) + }) + this.io.on('job.done', function (job) { + self.emitter.emit('queue.job_complete', { + stdout: job.std.out, + stderr: job.std.err, + stdmerged: job.std.merged, + testExitCode: job.test_status, + deployExitCode: job.deploy_status, + tasks: [] // what are these?? + }); + }); + /* + this.emitter.on('queue.new_job', function (old, more) { + this.emitter.emit('job.new', oldJobToNew(old, more)); + }); + */ }, - queueJob: function (task) { + queueJob: function (job) { var now = new Date() - this.jobdata.add(task.job.id) + this.jobdata.add(job.id) this.log('Got new job') - this.emitter.emit('browser.update', 'job.status.queued', {id: task.job.id, time: now}) - this.queue.push(task) + this.emitter.emit('browser.update', 'job.status.queued', {id: job.id, time: now}) + this.queue.push(job) }, cancelJob: function (id) { for (var i=0; i (http://frozenridge.co)", "contributors": [ diff --git a/static/icon.png b/static/icon.png new file mode 100644 index 0000000..d9cd402 Binary files /dev/null and b/static/icon.png differ