diff --git a/__tests__/orc-test.js b/__tests__/orc-test.js deleted file mode 100644 index dc7e59c..0000000 --- a/__tests__/orc-test.js +++ /dev/null @@ -1,7 +0,0 @@ -jest.dontMock('../orc.js'); - -describe('checkpoint', function() { - it('checkpoints a module by committing it', function() { - - }); -}); \ No newline at end of file diff --git a/orc b/orc index 303e93d..02f5b98 100755 --- a/orc +++ b/orc @@ -1,3 +1,3 @@ #! /usr/bin/env node var orc = require('./orc.js'); -orc.main(); \ No newline at end of file +orc.main(process.cwd(), process.argv); \ No newline at end of file diff --git a/orc.js b/orc.js index 8332d65..b76ac83 100644 --- a/orc.js +++ b/orc.js @@ -1,42 +1,169 @@ -var app = require('commander'); +var commander = require('commander'); var git = require('gitty'); var path = require('path'); - -// process.stdin.resume(); -// process.stdin.setEncoding('utf8'); -// process.stdin.on('data', function(data) { -// process.stdout.write(data); -// }); +var pushover = require('pushover'); process.on('SIGINT', function () { console.log('Got a SIGINT. Goodbye cruel world'); process.exit(0); }); -function _requireCommit() { +function init(dir, cb) { + // TODO: march up to root dir just to make output consistent + var repo = git(dir); + repo.getBranches(function(err, branches) { + if(err) { + cb('error getting branches '+err); + return; + } + if (!branches.current) { + cb('error: '+repo.name+' doesn\'t appear to be a valid git repo'); + return; + } + cb(null, { repo_dir: dir, repo: git(dir)}); + }); +} + +function noUnstaged(orc, cb) { + orc.repo.status(function(err, status) { + if(err) { + cb('error getting status checking unstaged '+err); + return; + } + if(status.unstaged.length > 0) { + cb('unstaged files, commit before running this.'); + return; + } + cb(null, true); + }); +} + +function noUntracked(orc, cb) { + orc.repo.status(function(err, status) { + if(err) { + cb('error getting status checking unstaged '+err); + return; + } + if(status.untracked.length > 0) { + cb('untracked files, add or ignore before running this.'); + return; + } + cb(null, true); + }); +} +function repoIsClean(orc, cb) { + noUnstaged(orc, function(err, res) { + if (err) { + cb(err); + return; + } + noUntracked(orc, function(err, res) { + if(err) { + cb(err); + return; + } + cb(null, true); + }); + }); } -function checkpoint() { +function repoHasChanges(orc, cb) { + orc.repo.status(function(err, status) { + if(err) { + cb('error getting status checking unstaged '+err); + return; + } + if(status.staged.length === 0 && status.unstaged.length === 0) { + cb('no changes, aborting.'); + return; + } + cb(null, true); + }); +} +// commit local changes +// push to remote +function checkpoint(orc, cb) { + noUntracked(orc, function(err, res) { + if(err) { + cb('you have unstaged files: '+err); + return; + } + repoHasChanges(orc, function(err, res) { + if(err) { + cb(err); + return; + } + orc.repo.commit('ORC-CHECKPOINT', ['-a', '--no-verify'], function(err, res) { + if(err) { + cb('checkpoint commit failed: \n'+err.message); + return; + } + orc.repo.getBranches(function(err, branches) { + if(err) { + cb('error getting branches: '+err.message); + return; + } + orc.repo.push('origin', branches.current, function(err, result) { + if (err) { + cb('error pushing checkpoint: '+err.message); + return; + } + cb(null, 'checkpoint finished.'); + }); + }); + }); + }); + }); } -function pull() { - // TODO +function pull(orc, cb) { + // TODO } -function push() { - // TODO +function push(orc, cb) { + // TODO } -function branch() { - // TODO +function createBranch(orc, branchName, cb) { + repoIsClean(orc, function(err, res) { + if(err) { + cb(err); + return; + } + orc.repo.checkout('master', function(err, res) { + if(err) { + cb('failed to checkout master'+err.message); + return; + } + orc.repo.pull('origin','master', function(err, res) { + if (err) { + cb('failed to pull from master: '+err.message); + return; + } + orc.repo.createBranch(branchName, function(err, res) { + if(err) { + cb('failed to create branch '+branchName+': '+err.message); + return; + } + orc.repo.checkout(branchName, function(err, res) { + if(err) { + cb('couldn\'t checkout branch '+branchName+': '+err.message); + return; + } + orc.repo.push('origin', branchName, ['--set-upstream'], cb); + }); + }); + }); + }); + }); } -function status() { - // TODO: incomplete, just an example - /* - { staged: +function status(orc,cb) { + // TODO: remove, just an example + /* + { staged: [ { file: '../package.json', status: 'new file' } ], unstaged: @@ -46,28 +173,40 @@ function status() { status: 'modified' } ], untracked: [ 'orc-test.js' ] } */ - r = new git.Repo('.', {}, function(err, repo) { - if(err) { - console.log('error getting repo status'); - return; - } - repo.status(function(err, stats){ - console.log('stats: '+JSON.stringify(stats)); - }); - }); + orc.repo.status(cb); } -app.command('checkpoint').alias('cp').description('commit all local changes and push to repo. use this all the time!').action(checkpoint); -app.command('pull').description('pull latest from remote master into your current branch').action(pull); -app.command('push').description("use this when you're ready to submit a pull request on github: squash your branch down to one commit, run unit tests, and push.").action(push); -app.command('branch').description('helper for switching branches').action(branch); -app.command('status').alias('st').description('helper for getting status').action(status); // REMOVE +function main(dir, argv, cb) { + if(!cb) { + cb = function(err, res) { + if(err) { + console.log('error: '+err); + } else { + // TODO: standardize result text and output + console.log('done. '+res); + } + }; + } + + init(dir, function(err, orc) { + if(err) { + cb('failed to init orc: '+err); + return; + } + + var app = new commander.Command(); + app.command('checkpoint').alias('cp').description('commit all local changes and push to repo. use this all the time!') + .action(function() { checkpoint(orc, cb); }); + app.command('pull').description('pull latest from remote master into your current branch') + .action(function() {pull(orc, cb); }); + app.command('push').description("use this when you're ready to submit a pull request on github: squash your branch down to one commit, run unit tests, and push.") + .action(function() {push(orc, cb);}); + app.command('branch [name]').alias('br').description('helper for creating branches') + .action(function(branchName) { createBranch(orc, branchName, cb); }); + app.command('status').alias('st').description('helper for getting status').action(function() { status(orc, cb);}); // REMOVE + app.parse(argv); + }); +} +// orc checkpoint change -exports.checkpoint = checkpoint; -exports.pull = pull; -exports.push = push; -exports.branch = branch; -exports.main = function() { - p = app.parse(process.argv); - console.log('main! '+JSON.stringify(process.argv)); -}; \ No newline at end of file +exports.main = main; diff --git a/package.json b/package.json index bb5bdc6..1a84b35 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "overlook research command line interface to change manager", "main": "orc.js", "scripts": { - "test": "jest", + "test": "mocha", "lint": "jshint orc *.js" }, "keywords": [ @@ -14,10 +14,14 @@ "dependencies": { "commander": "~2.6.0", "gitty": "*", - "jest": "~0.1.37", - "jshint": "~2.6.0", - "tmp": "*" + "jshint": "~2.6.0" }, "author": "Aaron Brady", - "license": "(c) Overlook Research 2014" + "license": "(c) Overlook Research 2014", + "devDependencies": { + "chai": "^1.10.0", + "mocha": "^2.1.0", + "pushover": "^1.3.6", + "tmp": "*" + } } diff --git a/test/orc-test.js b/test/orc-test.js new file mode 100644 index 0000000..f6a8421 --- /dev/null +++ b/test/orc-test.js @@ -0,0 +1,127 @@ +var child_process = require('child_process'); +var expect = require("chai").expect; +var fs = require('fs'); +var git = require('gitty'); +var path = require('path'); +var pushover = require('pushover'); +var tmp = require('tmp'); + +var orc = require('../orc.js'); + +describe('no repo', function() { + it('should fail in a directory without git', function() { + tmp.dir(function(err, dir) { + expect(err).to.be.null(); + orc.main(dir, [], function(err, res) { + expect(err).not.to.be.null(); + expect(res).to.be.undefined(); + }); + }); + }); +}); + +describe('basic repo', function() { + tmp.dir(function(err, dir) { + expect(err).to.be.null(); + var repo = git(dir, []); + repo.initSync(); + var fn = path.resolve(dir,'foo.js'); + fs.writeFileSync(fn, 'o hai'); + repo.addSync([fn]); + repo.commitSync('foo'); + + it('should start up properly in a new github repo', function(done) { + orc.main(dir, ['node', 'orc'], function(err, res) { + expect(err).to.be.null(); + expect(res).to.be.true(); + done(); + }); + }); + }); +}); + +describe('remote tests', function() { + var gitserver; + var repo; + var repo2; + var repoDir; + var rootDir; + var repoName = 'orctest'; + var branchName = 'foobranch'; + + // create a simple git server listener + before(function(done) { + tmp.dir(function(err, dir) { + rootDir = dir; + repoDir = path.resolve(dir,repoName); + fs.mkdirSync(repoDir); + + repo = git(repoDir); + repo.initSync(); + repo.addRemoteSync('origin', 'http://localhost:7001/server'); + var gitserver = pushover(dir); + gitserver.on('push', function(push) { + push.accept(); + }); + gitserver.on('fetch', function (fetch) { + fetch.accept(); + }); + require('http').createServer(function(req, res) { + gitserver.handle(req, res); + }).listen(7001, function() { + done(); + }); + }); + }); + // this is expected to have been done outside of orc + it('should create the master branch and push it', function(done) { + var fn = path.resolve(repoDir,'foo.js'); + fs.writeFileSync(fn, 'o hai'); + repo.addSync([fn]); + repo.commitSync('bar'); + repo.push('origin', 'master', function(err, result) { + expect(fs.existsSync(path.resolve(rootDir,'server.git'))).to.be.true(); + done(); + }); + }); + it('should create a branch based off of the latest commit on master', function(done) { + orc.main(repoDir,['node','orc','branch',branchName], function(err, res) { + expect(repo.getBranchesSync().current).to.equal(branchName); + done(); + }); + }); + it('should have pushed the branch so it is visible to others', function(done) { + tmp.dir(function(err, dir) { + expect(err).to.be.null(); + //console.log('root dir: '+rootDir); + //console.log('repo2 root: '+dir); + var repo2_dir = path.resolve(dir,dir); + git.clone(repo2_dir, 'http://localhost:7001/server', function(err) { + expect(err).to.be.null(); + repo2 = git(repo2_dir); + repo2.pull('origin','master',['-a'], function(err, res) { + var co_res = repo2.checkoutSync(branchName); + expect(co_res).to.be.ok(); + done(); + }); + }); + }); + }); + it('should checkpoint properly', function() { + var fn = path.resolve(repoDir,'bar.js'); + fs.writeFileSync(fn, 'o hai'); + orc.main(repoDir, ['node','orc','checkpoint'], function(err, res) { + expect(err).to.be.ok(); + expect(res).to.be.undefined(); + }); + }); + // createBranch test: success and failure +}); +/* +describe('github tests', function() { + var repo1 = null; + var repo2 = null; + tmp.dir(function(err, dir) { + child_process.execSync('git clone https://orctester:abcd1234@github.com/orctester/orctest.git', {cwd: dir}); + +});*/ \ No newline at end of file