Skip to content

Commit

Permalink
Merge 9066040 into 9dc0a9e
Browse files Browse the repository at this point in the history
  • Loading branch information
nknapp committed Feb 24, 2016
2 parents 9dc0a9e + 9066040 commit adf8df2
Show file tree
Hide file tree
Showing 24 changed files with 199 additions and 107 deletions.
6 changes: 4 additions & 2 deletions README.md
@@ -1,8 +1,10 @@
# thoughtful-release

[![NPM version](https://badge.fury.io/js/thoughtful-release.svg)](http://badge.fury.io/js/thoughtful-release)
[![Build Status](https://travis-ci.org/nknapp/thoughtful-release.svg?branch=master)](https://travis-ci.org/nknapp/thoughtful-release)
[![Coverage Status](https://img.shields.io/coveralls/nknapp/thoughtful-release.svg)](https://coveralls.io/r/nknapp/thoughtful-release)
[![Travis Build Status](https://travis-ci.org/nknapp/thoughtful-release.svg?branch=master)](https://travis-ci.org/nknapp/thoughtful-release)
[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/nknapp/thoughtful-release?svg=true&branch=master)](https://ci.appveyor.com/project/nknapp/thoughtful-release)
[![Coverage Status](https://img.shields.io/coveralls/nknapp/thoughtful-release.svg)](https://coveralls.io/r/nknapp/thoughtful-release)


> Create high quality releases with less work
Expand Down
27 changes: 27 additions & 0 deletions appveyor.yml
@@ -0,0 +1,27 @@
# Test against this version of Node.js
environment:
matrix:
- nodejs_version: "4"
- nodejs_version: "5"

# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# install modules
- npm install

# Post-install test scripts.
test_script:
# Output useful info for debugging.
- node --version
- npm --version
# run tests
- npm install -g istanbul
- istanbul cover node_modules/mocha/bin/_mocha --report lcovonly
- npm install -g coveralls
- cat ./coverage/lcov.info | coveralls

# Don't actually build.
build: off

2 changes: 1 addition & 1 deletion bin/thoughtful.js
Expand Up @@ -47,7 +47,7 @@ program
.command('cleanup-history [target-branch]')
.description('Rebase the current branch onto another branch, condensing the whole branch into a single commit.')
.action((targetBranch) => {
thoughtful.cleanupHistory({targetBranch: targetBranch}).done(() => console.log(`Cleanup complete`))
thoughtful.cleanupHistory({targetBranch: targetBranch}).done(() => console.log('Cleanup complete'))
})

program.parse(process.argv)
Expand Down
6 changes: 5 additions & 1 deletion lib/changelog.js
Expand Up @@ -48,7 +48,11 @@ function Changelog (cwd) {
*/
this.formatRelease = function (version, date, changes) {
var dateStr = date.toUTCString()
return `# Version ${version} (${dateStr})\n\n${changes}\n\n`
return `# Version ${version} (${dateStr})
${changes}
`
}

/**
Expand Down
17 changes: 12 additions & 5 deletions lib/git.js
Expand Up @@ -25,6 +25,12 @@ var _ = {
*/
module.exports = (dir) => new Git(dir)

/**
* Path to a node-script to be used instead of the real 'git' for test-cases
* @type {undefined}
*/
module.exports.mockCmd = undefined

/**
* The Git class for accessing git repositories
* @param {string} dir the working directory
Expand All @@ -35,7 +41,7 @@ function Git (dir) {
'LANG': 'en_US'
}, process.env)

var gitcmd = process.env['THOUGHTFUL_GIT_CMD'] || 'git'
var gitcmd = module.exports.mockCmd ? [ 'node', module.exports.mockCmd ] : 'git'

/**
* Execute git with a list of cmd-line arguments in the working directory
Expand All @@ -55,7 +61,7 @@ function Git (dir) {
* that need a tty to work
* @returns {*}
*/
this.spawn = function runGit () {
this.spawn = function spawnGit () {
debug(`Running in ${dir}`, gitcmd, _.toArray(arguments))
return cpp.spawn(gitcmd, _.toArray(arguments), {env: env, cwd: dir, stdio: 'inherit'})
}
Expand Down Expand Up @@ -181,8 +187,9 @@ function Git (dir) {
this.cleanupHistory = function cleanupHistory (options) {
var targetBranch = (options && options.targetBranch) || 'master'
var thoughtful = process.argv[1]
/* istanbul ignore else The default value for "thoughtful" is the current process. It
is hardly possible to use this default value in a coverage test */
/* istanbul ignore else
The default value for "thoughtful" is the current process. It
is hardly possible to use this default value in a unit-test */
if (options && options.thoughtful) {
thoughtful = options.thoughtful
}
Expand All @@ -192,7 +199,7 @@ function Git (dir) {
.then((tagOutput) => {
return this.run('merge-base', 'HEAD', targetBranch) // Get merge-base
// Squash
.then((mergeBase) => this.spawn('-c', `sequence.editor=${thoughtful} sequence-editor`, 'rebase', '--interactive', mergeBase.stdout.trim()))
.then((mergeBase) => this.spawn('-c', `sequence.editor='${thoughtful}' sequence-editor`, 'rebase', '--interactive', mergeBase.stdout.trim()))
// Rebase on target branch
.then(() => this.run('rebase', targetBranch).invoke('appendTo', tagOutput.appendTo('')))
})
Expand Down
55 changes: 29 additions & 26 deletions lib/q-child-process.js
@@ -1,6 +1,9 @@
var cp = require('child_process')
var Q = require('q')
var debug = require('debug')('thoughtful:q-child-process')
var _ = {
isArray: require('lodash.isarray')
}

/**
* Promise-Based functions from child_process
Expand All @@ -20,6 +23,12 @@ module.exports = {
* @returns {{ stdout: string, stderr: string}} the output of the process on stdout and stderr
*/
function execFile (cmd, args, options) {
// if the cmd is an array, use the first entry as cmd and insert the rest as argument
if (_.isArray(cmd)) {
args = cmd.concat(args)
args.shift()
cmd = cmd[0]
}
var defer = Q.defer()
cp.execFile(cmd, args, options, (err, stdout, stderr) => {
if (err) {
Expand All @@ -33,8 +42,10 @@ function execFile (cmd, args, options) {
* @param {string} output the previous output
*/
appendTo: function (output) {
var myOutput = `${stdout.trim()}\n${stderr.trim()}`
return `${output}\n${myOutput.trim()}`.trim()
var myOutput = `${stdout.trim()}
${stderr.trim()}`
return `${output}
${myOutput.trim()}`.trim()
}
})
})
Expand All @@ -44,12 +55,19 @@ function execFile (cmd, args, options) {
/**
* Spawn a process and return a promise that is resolved when the process exits with exit-code zero, or rejected
* on a non-zero exit-code.
* @param {string} cmd the command to execute
* @param {string|array<string>} cmd the command to execute
* @param {string[]} args command line arguments for the command
* @param {object} options options for the {@link child_process#spawn}-method.
* @returns {*} a promise that is resolved or rejected based on the exit-code of the child-process
*/
function spawn (cmd, args, options) {
// if the cmd is an array, use the first entry as cmd and insert the rest as argument
if (_.isArray(cmd)) {
args = cmd.concat(args)
args.shift()
cmd = cmd[0]
}

debug('spawn', cmd, args, options)
var defer = Q.defer()
var child = cp.spawn(cmd, args, options)
Expand All @@ -58,7 +76,7 @@ function spawn (cmd, args, options) {
defer.resolve()
debug('spawn resolved', cmd, args, options)
} else {
defer.reject(new Error(`Command "${cmd} ${args && args.join(' ')}" exited with ${code}`))
defer.reject(new Error(`Command "${cmd} ${args && JSON.stringify(args)}" exited with ${code}`))
}
})
return defer.promise
Expand All @@ -75,29 +93,14 @@ function spawn (cmd, args, options) {
* @param {object=} options options for the {@link child_process#spawn}-method.
*/
function spawnWithShell (command, options) {
var os = require('os')
switch (os.type()) {
case 'Linux':
case 'Dawin':
return spawn('/bin/sh', ['-c', command], options)
case 'Windows_NT':
return spawn('cmd.exe', ['/s', '/c', command], options)
default:
throw new Error(`Unexpected OS-type ${os.type()}`)
}
return spawn('sh', ['-c', command], options)
}

/**
* Escape parameters for use with the `spawnWithShell`
**/
function escapeParam (param) {
var os = require('os')
switch (os.type()) {
case 'Linux':
case 'Dawin':
// Escape " -> \" and \ -> \\
return `"${param.replace(/[\\"]/g, '\\$&')}"`
case 'Windows_NT':
// No escaping in windows until somebody files an issue to do otherwise
return param
default:
throw new Error(`Unexpected OS-type ${os.type()}`)
}
// Same on windows and linux, since we are spawning the git-shell
// Wrap the parameter in quotes and escape '"' and '\' with a backslash to allow shell parameters to be recognized as a unit
return `"${param.replace(/[\\"]/g, '\\$&')}"`
}
7 changes: 5 additions & 2 deletions package.json
Expand Up @@ -35,9 +35,11 @@
"dependencies": {
"commander": "^2.9.0",
"debug": "^2.2.0",
"head": "^1.0.0",
"lodash.contains": "^2.4.3",
"lodash.defaults": "^3.1.2",
"lodash.defaultsdeep": "^3.10.0",
"lodash.isarray": "^4.0.0",
"lodash.isplainobject": "^3",
"lodash.isstring": "^3.0.1",
"lodash.toarray": "^3.0.2",
Expand All @@ -50,8 +52,9 @@
"chai": "^3.2.0",
"chai-as-promised": "^5.2.0",
"ghooks": "^1.0.1",
"lodash.escaperegexp": "^3.0.1",
"mocha": "^2.2.5",
"lodash.escaperegexp": "^3.0.1"
"trace": "^2.3.0"
},
"files": [
"bin",
Expand All @@ -65,7 +68,7 @@
},
"config": {
"ghooks": {
"pre-commit": "thoughtful precommit && standard --format && thought run -a"
"pre-commit": "thoughtful precommit && standard && thought run -a"
}
},
"keywords": []
Expand Down
29 changes: 23 additions & 6 deletions test/changelog-spec.js
Expand Up @@ -21,6 +21,7 @@ var expect = chai.expect
var changelog = require('../lib/changelog.js')
var qfs = require('q-io/fs')
var path = require('path')
require('trace')

function fixture (filename) {
return require('fs').readFileSync(path.join('test', 'fixtures', filename), {encoding: 'utf-8'})
Expand Down Expand Up @@ -85,16 +86,16 @@ describe('changelog-library:', () => {
it('should be able to load, store a CHANGELOG.md file without modifying it.', () => {
var changelogContents = qfs.copy('test/fixtures/changelog-with-a-release.md', workDir('CHANGELOG.md'))
.then(() => changelog(workDir())
.save())
.save())
.then(() => qfs.read(workDir('CHANGELOG.md')))
return expect(changelogContents).to.eventually.equal(fixture('changelog-with-a-release.md'))
})

it('should be able to load a CHANGELOG.md and store a modified version', () => {
var changelogContents = qfs.copy('test/fixtures/changelog-with-a-release.md', workDir('CHANGELOG.md'))
.then(() => changelog(workDir())
.newRelease('2.0.0', new Date('2015-11-25T13:06:00Z'), '* Change 1\n* Change 2')
.save())
.newRelease('2.0.0', new Date('2015-11-25T13:06:00Z'), '* Change 1\n* Change 2')
.save())
.then(() => qfs.read(workDir('CHANGELOG.md')))
return expect(changelogContents).to.eventually.equal(fixture('changelog-with-two-releases.md'))
})
Expand Down Expand Up @@ -124,12 +125,28 @@ describe('changelog-library:', () => {

it('should call THOUGHTFUL_CHANGELOG_EDITOR as editor, if set', () => {
const dummyEditor = path.resolve(__dirname, 'dummy-editor', 'dummy-editor.js')
process.env['THOUGHTFUL_CHANGELOG_EDITOR'] = `${dummyEditor} changelog-editor`
process.env['EDITOR'] = `${dummyEditor} default-editor`
// Dummy editor prepends contents with first argument ("changelog editor")
process.env['THOUGHTFUL_CHANGELOG_EDITOR'] = `node "${dummyEditor}" changelog-editor`
process.env['EDITOR'] = `node "${dummyEditor}" default-editor`
var changelogContents = changelog(workDir()).openEditor()
.then(() => qfs.read(workDir('CHANGELOG.md')))
return expect(changelogContents).to.eventually.equal(`changelog-editor
# Release-Notes
<a name="current-release"></a>
`)
})

it('should call escape the changelog-path propery if it contains spaces', () => {
const dummyEditor = path.resolve(__dirname, 'dummy-editor', 'dummy-editor.js')
// Dummy editor prepends contents with first argument ("changelog editor")
process.env['THOUGHTFUL_CHANGELOG_EDITOR'] = `node "${dummyEditor}" changelog-editor`

var changelogContents = qfs.makeTree(workDir('with spaces'))
.then(() => changelog(workDir('with spaces')).openEditor())
.then(() => qfs.read(workDir('with spaces/CHANGELOG.md')))
return expect(changelogContents).to.eventually.equal(`changelog-editor
# Release-Notes
<a name="current-release"></a>
`)
Expand All @@ -138,7 +155,7 @@ describe('changelog-library:', () => {
it('should call EDITOR as editor, if THOUGHTFUL_CHANGELOG_EDITOR is not set', () => {
const dummyEditor = path.resolve(__dirname, 'dummy-editor', 'dummy-editor.js')
process.env['THOUGHTFUL_CHANGELOG_EDITOR'] = ''
process.env['EDITOR'] = `${dummyEditor} default-editor`
process.env['EDITOR'] = `node "${dummyEditor}" default-editor`
var changelogContents = changelog(workDir()).openEditor()
.then(() => qfs.read(workDir('CHANGELOG.md')))
return expect(changelogContents).to.eventually.equal(`default-editor
Expand Down
5 changes: 4 additions & 1 deletion test/dummy-editor/dummy-editor.js
Expand Up @@ -10,4 +10,7 @@
var fs = require('fs')
var contents = fs.readFileSync(process.argv[3], {encoding: 'utf-8'})
contents = contents.replace(/\n+/g, '\n')
fs.writeFileSync(process.argv[3], `${process.argv[2]}\n\n${contents.trim()}\n`)
fs.writeFileSync(process.argv[3], `${process.argv[2]}
${contents.trim()}
`)
5 changes: 4 additions & 1 deletion test/dummy-git/commit-editor.js
Expand Up @@ -8,4 +8,7 @@
var fs = require('fs')
var contents = fs.readFileSync(process.argv[3], {encoding: 'utf-8'})
contents = contents.replace(/\n+/g, '\n')
fs.writeFileSync(process.argv[3], `${process.argv[2]}\n\n${contents.trim()}\n`)
fs.writeFileSync(process.argv[3], `${process.argv[2]}
${contents.trim()}
`)
3 changes: 3 additions & 0 deletions test/dummy-git/commit-editor.sh
@@ -0,0 +1,3 @@
#!/bin/sh

node "${0%.sh}.js" "$@"
3 changes: 3 additions & 0 deletions test/dummy-git/git-add.sh
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

node "${0%.sh}.js" "$@"
3 changes: 3 additions & 0 deletions test/dummy-git/git-branch-feature.sh
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

node "${0%.sh}.js" "$@"
3 changes: 3 additions & 0 deletions test/dummy-git/git-lastRelease-error-exit0.sh
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

node "${0%.sh}.js" "$@"
3 changes: 3 additions & 0 deletions test/dummy-git/git-lastRelease-error.sh
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

node "${0%.sh}.js" "$@"
3 changes: 3 additions & 0 deletions test/dummy-git/git-lastRelease-no-version-tag.sh
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

node "${0%.sh}.js" "$@"
3 changes: 3 additions & 0 deletions test/dummy-git/git-lastRelease-non-matching-tag.sh
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

node "${0%.sh}.js" "$@"
3 changes: 3 additions & 0 deletions test/dummy-git/git-lastRelease-v0.8.3-beta.sh
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

node "${0%.sh}.js" "$@"

0 comments on commit adf8df2

Please sign in to comment.