diff --git a/.travis.yml b/.travis.yml index 5737015..63f7b35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,3 @@ language: node_js node_js: - - "0.12" - - "0.10" - - "iojs" + - "4" diff --git a/README.md b/README.md index f074de3..69deea9 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ *A node [gitlab-ci-runner](https://github.com/gitlabhq/gitlab-ci-runner)* +`gcr` v4.x will only support node v4.x+. To use `gcr` with an older version +of node, please use `gcr` v3.x + ## Install ```bash diff --git a/lib/build.js b/lib/build.js index c3f090f..46f5c7a 100644 --- a/lib/build.js +++ b/lib/build.js @@ -1,15 +1,17 @@ -var gcr = require('./gcr') - , spawn = require('child_process').spawn - , rimraf = require('rimraf') - , slide = require('slide') - , chain = slide.chain - , fs = require('fs') - , log = require('npmlog') - , path = require('path') - , util = require('util') - , mkdirp = require('mkdirp') - , argsplit = require('argsplit') - , EE = require('events').EventEmitter +'use strict' + +const gcr = require('./gcr') + , spawn = require('child_process').spawn + , rimraf = require('rimraf') + , slide = require('slide') + , chain = slide.chain + , fs = require('fs') + , log = require('npmlog') + , path = require('path') + , util = require('util') + , mkdirp = require('mkdirp') + , argsplit = require('argsplit') + , EE = require('events') module.exports = Build @@ -32,37 +34,37 @@ util.inherits(Build, EE) Build.prototype.run = function() { this.state = 'running' - var self = this - this.shouldClone(function(should) { + const self = this + this.shouldClone((should) => { if (!should) { - self.fetch(function(err) { - if (err) return self.emit('done', false) + this.fetch((err) => { + if (err) return this.emit('done', false) runCommand(0) }) } else { - rimraf(self.projectDir, function(err) { + rimraf(self.projectDir, (err) => { if (err) { - self.state = 'failed' - self.update(function() { - self.emit('done', false) + this.state = 'failed' + this.update(() => { + this.emit('done', false) }) return } - self.clone(function(err) { - if (err) return self.emit('done', false) + this.clone((err) => { + if (err) return this.emit('done', false) runCommand(0) }) }) } }) - var cmds = this.opts.commands - var len = cmds.length - var dir = this.projectDir + const cmds = this.opts.commands + const len = cmds.length + const dir = this.projectDir function runCommand(idx) { if (idx < len) { log.verbose('[build]', 'running command', idx+1, 'of', len) - var cmd = cmds[idx] + const cmd = cmds[idx] self.runCommand(cmd, dir, function(err) { if (err) return self.emit('done', false) runCommand(idx+1) @@ -77,20 +79,19 @@ Build.prototype.run = function() { } Build.prototype.clone = function(cb) { - var self = this - var cmd = ['clone', this.opts.repo_url, 'project-'+this.opts.project_id] - var dir = this.projectDir - this.gitCommand(cmd, this.buildDir, function(err) { + const cmd = ['clone', this.opts.repo_url, 'project-'+this.opts.project_id] + const dir = this.projectDir + this.gitCommand(cmd, this.buildDir, (err) => { if (err) return cb && cb(err) - self.gitCommand(['checkout', self.opts.ref], dir, cb) + this.gitCommand(['checkout', this.opts.ref], dir, cb) }) } Build.prototype.fetch = function(cb) { - var self = this + const self = this - var dir = this.projectDir - var repoUrl = this.opts.repo_url + const dir = this.projectDir + const repoUrl = this.opts.repo_url function resetHard(cb) { self.gitCommand(['reset', '--hard'], dir, cb) @@ -125,11 +126,10 @@ Build.prototype.fetch = function(cb) { } Build.prototype.shouldClone = function(cb) { - var self = this - var d = path.join(this.projectDir, '.git') - fs.exists(d, function(e) { + const d = path.join(this.projectDir, '.git') + fs.exists(d, (e) => { if (!e) return cb(true) - return cb(!self.opts.allow_git_fetch) + return cb(!this.opts.allow_git_fetch) }) } @@ -138,9 +138,9 @@ Build.prototype.append = function(str) { } Build.prototype.runCommand = function(cmd, dir, cb) { - var self = this + const self = this if ('function' === typeof dir) cb = dir, dir = process.cwd() - var env = { + const env = { CI_SERVER: true , CI_SERVER_NAME: 'GitLab CI' , CI_SERVER_VERSION: null @@ -159,7 +159,7 @@ Build.prototype.runCommand = function(cmd, dir, cb) { util._extend(env, process.env) - var opts = { + const opts = { env: env , cwd: dir , timeout: this.opts.timeout @@ -171,57 +171,73 @@ Build.prototype.runCommand = function(cmd, dir, cb) { } log.verbose('[builder]', 'cmd', cmd) - this.append(util.format('\n%s\n', cmd)) + this.append(`\n${cmd}\n`) - var child = spawn('/bin/bash', ['-c', fixedCmd.join(' ')], opts) + const child = spawn('/bin/bash', ['-c', fixedCmd.join(' ')], opts) var timedout = false - var timer = setTimeout(function() { + var timer = setTimeout(() => { timedout = true child.kill() - self.append('\n** TIMEOUT **\n') + this.append('\n** TIMEOUT **\n') }, this.opts.timeout) - child.stderr.on('data', function(d) { - var data = d.toString() + + child.stderr.on('data', (d) => { + const data = d.toString() log.silly('[builder]', 'stderr', data) - self.append(data) + this.append(data) }) - child.stdout.on('data', function(d) { - var data = d.toString() + + child.stdout.on('data', (d) => { + const data = d.toString() log.silly('[builder]', 'stdout', data) - self.append(data) + this.append(data) + }) + + child.on('error', (err) => { + if (timer) { + clearTimeout(timer) + timer = null + } + const e = new Error(`Command: [${cmd}] failed ${err.message}`) + this.append(e) + log.error(e, err) + this.state = 'failed' + this.update(function() { + cb && cb(e) + }) }) - child.on('close', function(code) { + + child.on('close', (code) => { if (timer) { clearTimeout(timer) timer = null } if (code !== 0) { - var msg = timedout + const msg = timedout ? 'process timedout' - : 'process exited with code: '+code - var e = new Error(msg) - self.append(util.format( - 'Command: [%s] exited with code: %d timedout: %s', cmd, code, - timedout ? 'yes' : 'no' - )) + : `process exited with code: ${code}` + + const e = new Error(msg) + const to = timedout ? 'yes' : 'no' + this.append(`Command: ${cmd} exited with code: ${code} timedout: ${to}`) e.command = cmd e.opts = opts - self.state = 'failed' - self.update(function() { + this.state = 'failed' + this.update(function() { cb && cb(e) }) return } - self.update(function() { + this.update(function() { cb && cb() }) }) } Build.prototype.gitCommand = function(cmd, dir, cb) { - var self = this + const self = this if ('function' === typeof dir) cb = dir, dir = process.cwd() - var env = { + const env = { CI_SERVER: true , CI_SERVER_NAME: 'GitLab CI' , CI_SERVER_VERSION: null @@ -240,32 +256,49 @@ Build.prototype.gitCommand = function(cmd, dir, cb) { util._extend(env, process.env) - var opts = { + const opts = { env: env , cwd: dir , timeout: this.opts.timeout } log.verbose('[builder]', 'cmd', cmd) - self.append(util.format('\n%s %s\n', self.git, cmd.join(' '))) - var child = spawn(this.git, cmd, opts) + self.append(`\n${self.git} ${cmd.join(' ')}\n`) + const child = spawn(this.git, cmd, opts) var timedout = false - var timer = setTimeout(function() { + var timer = setTimeout(() => { timedout = true child.kill() - self.append('\n** TIMEOUT **\n') + this.append('\n** TIMEOUT **\n') }, this.opts.timeout) - child.stderr.on('data', function(d) { - var data = d.toString() + + child.stderr.on('data', (d) => { + const data = d.toString() log.silly('[builder]', 'stderr', data) - self.append(data) + this.append(data) }) - child.stdout.on('data', function(d) { - var data = d.toString() + + child.stdout.on('data', (d) => { + const data = d.toString() log.silly('[builder]', 'stdout', data) - self.append(data) + this.append(data) }) - child.on('exit', function(code) { + + child.on('error', (err) => { + if (timer) { + clearTimeout(timer) + timer = null + } + const e = new Error(`Command: [${cmd}] failed ${err.message}`) + this.append(e) + log.error(e, err) + this.state = 'failed' + this.update(function() { + cb && cb(e) + }) + }) + + child.on('exit', (code) => { if (timer) { clearTimeout(timer) timer = null @@ -273,27 +306,26 @@ Build.prototype.gitCommand = function(cmd, dir, cb) { if (code !== 0) { var msg = timedout ? 'process timedout' - : 'process exited with code: '+code - var e = new Error(msg) - self.append(util.format( - 'Command: [%s %s] exited with code: %d timedout: %s', self.git, - cmd.join(' '), code, timedout ? 'yes' : 'no' - )) + : `process exited with code: ${code}` + const e = new Error(msg) + const to = timedout ? 'yes' : 'no' + this.append(`Command: [${self.git} ${cmd.join(' ')}] exited with code: ` + + `${code} timedout: ${to}`) e.command = cmd e.opts = opts - self.state = 'failed' - self.update(function() { + this.state = 'failed' + this.update(function() { cb && cb(e) }) return } - self.update(function() { + this.update(function() { cb && cb() }) }) } Build.prototype.update = function(cb) { - var id = this.opts.id - gcr.client.updateBuild(id, this.state, this.output, cb) + const id = this.opts.id + gcr.client.updateBuild(+id, this.state, this.output, cb) } diff --git a/lib/client.js b/lib/client.js index 55b125b..133d7d9 100644 --- a/lib/client.js +++ b/lib/client.js @@ -1,10 +1,13 @@ -var request = require('request') - , gcr = require('./gcr') - , util = require('util') - , log = require('npmlog') - , url = require('url') +'use strict' -var proxyServer = process.env.HTTPS_PROXY +const request = require('request') + , gcr = require('./gcr') + , util = require('util') + , log = require('npmlog') + , url = require('url') + , urlJoin = require('url-join') + +const proxyServer = process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy @@ -25,9 +28,8 @@ Client.prototype.apiUrl = function() { } Client.prototype.updateBuild = function(id, state, trace, cb) { - var self = this log.info('[client]', 'submitting build %d to coordinator...', id) - var opts = { + const opts = { body: { state: state , trace: trace @@ -38,9 +40,9 @@ Client.prototype.updateBuild = function(id, state, trace, cb) { } log.verbose('[client]', 'update build', id, state) - opts.uri = url.resolve( self.apiUrl() - , util.format('/api/v1/builds/%d.json', id) - ) + opts.uri = urlJoin( this.apiUrl() + , util.format('/api/v1/builds/%d.json', id) + ) log.http('PUT', opts.uri) request.put(opts, function(err, res, body) { if (err) return cb && cb(err) @@ -56,8 +58,7 @@ Client.prototype.updateBuild = function(id, state, trace, cb) { } Client.prototype.registerRunner = function(pubkey, token, cb) { - var self = this - var opts = { + const opts = { body: { public_key: pubkey , token: token @@ -66,7 +67,7 @@ Client.prototype.registerRunner = function(pubkey, token, cb) { , strictSSL: gcr.config.get('strictSSL') } - opts.uri = url.resolve(self.apiUrl(), '/api/v1/runners/register.json') + opts.uri = urlJoin(this.apiUrl(), '/api/v1/runners/register.json') log.http('POST', opts.uri) request.post(opts, function(err, res, body) { if (err) return cb && cb(err) @@ -82,10 +83,9 @@ Client.prototype.registerRunner = function(pubkey, token, cb) { } Client.prototype.getBuild = function(cb) { - var self = this log.info('[client]', 'checking for builds...') - var opts = { + const opts = { body: { token: gcr.config.get('token') } @@ -93,12 +93,12 @@ Client.prototype.getBuild = function(cb) { , strictSSL: gcr.config.get('strictSSL') } - opts.uri = url.resolve(self.apiUrl(), '/api/v1/builds/register.json') - request.post(opts, function(err, res, body) { + opts.uri = urlJoin(this.apiUrl(), '/api/v1/builds/register.json') + request.post(opts, (err, res, body) => { if (err) return cb && cb(err) log.http(res.statusCode, opts.uri) if (res.statusCode === 201) { - var o = { + const o = { id: body.id , project_id: body.project_id , commands: body.commands.replace(/\r\n/g, '\n').split('\n') diff --git a/lib/config.default.js b/lib/config.default.js index 560522b..033f0dd 100644 --- a/lib/config.default.js +++ b/lib/config.default.js @@ -1,10 +1,12 @@ -var path = require('path') - , os = require('os') - , isWin = os.platform() === 'win32' - , homedir = require('os-homedir') +'use strict' + +const path = require('path') + , os = require('os') + , isWin = os.platform() === 'win32' + , homedir = os.homedir module.exports = function(parsed) { - var o = {} + const o = {} if (parsed.url) o.url = parsed.url if (parsed.token) o.token = parsed.token if (parsed.registToken) o.registToken = parsed.registerToken diff --git a/lib/gcr.js b/lib/gcr.js index 4e132b8..1128333 100644 --- a/lib/gcr.js +++ b/lib/gcr.js @@ -1,15 +1,17 @@ -var EventEmitter = require('events').EventEmitter - , gcr = new EventEmitter - , log = require('npmlog') - , fs = require('fs') - , nconf = require('nconf') - , mkdirp = require('mkdirp') - , path = require('path') - , home = require('os-homedir')() - , confFile = path.join(home, '.config', 'gcr.json') - , slide = require('slide') - , chain = slide.chain - , which = require('which') +'use strict' + +const EventEmitter = require('events') + , gcr = new EventEmitter + , log = require('npmlog') + , fs = require('fs') + , nconf = require('nconf') + , mkdirp = require('mkdirp') + , path = require('path') + , home = require('os-homedir')() + , confFile = path.join(home, '.config', 'gcr.json') + , slide = require('slide') + , chain = slide.chain + , which = require('which') log.heading = 'gcr' diff --git a/lib/runner.js b/lib/runner.js index bfb214d..cdea92d 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -1,8 +1,10 @@ -var gcr = require('./gcr') - , util = require('util') - , EventEmitter = require('events').EventEmitter - , log = require('npmlog') - , Build = require('./build') +'use strict' + +const gcr = require('./gcr') + , util = require('util') + , EventEmitter = require('events') + , log = require('npmlog') + , Build = require('./build') module.exports = Runner @@ -11,9 +13,9 @@ function Runner() { return new Runner() EventEmitter.call(this) // { buildId: projectId } - this.builds = {} + this.builds = new Map() // { buildId: buildData } - this.queue = {} + this.queue = new Map() this.interval = null } @@ -27,14 +29,10 @@ Runner.prototype.start = function() { }, 5000) } +// id must always be a number Runner.prototype.projectIsRunning = function(id) { - var builds = Object.keys(this.builds) - , len = builds.length - - for (var i=0; i