fix(commits): add helpful error when lastRelease not in history

Closes #61, Closes #50
This commit is contained in:
Stephan Bönnemann 2015-08-22 19:31:27 +02:00
parent a2d6db2ce5
commit 5cc7da6035
4 changed files with 74 additions and 20 deletions

View File

@ -1,25 +1,64 @@
const { exec } = require('child_process')
module.exports = function ({lastRelease}, cb) {
const log = require('npmlog')
const SemanticReleaseError = require('@semantic-release/error')
module.exports = function ({lastRelease, branch}, cb) {
const from = lastRelease.gitHead
const range = (from ? from + '..' : '') + 'HEAD'
exec(
`git log -E --format=%H==SPLIT==%B==END== ${range}`,
(err, stdout) => {
if (err) return cb(err)
if (!from) return extract()
cb(null, String(stdout).split('==END==\n')
exec(`git branch --contains ${from}`, (err, stdout) => {
if (err) return cb(err)
let inHistory = false
.filter((raw) => !!raw.trim())
const branches = stdout.split('\n')
.map((result) => {
if (branch === result.replace('*', '').trim()) {
inHistory = true
return null
}
return result.trim()
})
.filter(branch => !!branch)
.map((raw) => {
const data = raw.split('==SPLIT==')
return {
hash: data[0],
message: data[1]
}
}))
if (!inHistory) {
log.error('commits',
`The commit the last release of this package was derived from is no longer
in the direct history of the "${branch}" branch.
This means semantic-release can not extract the commits between now and then.
This is usually caused by force pushing or releasing from an unrelated branch.
You can recover from this error by publishing manually or restoring
the commit "${from}".` + (branches.length ?
`\nHere is a list of branches that still contain the commit in question: \n * ${branches.join('\n * ')}` :
''
))
return cb(new SemanticReleaseError('Commit not in history', 'ENOTINHISTORY'))
}
)
extract()
})
function extract () {
exec(
`git log -E --format=%H==SPLIT==%B==END== ${range}`,
(err, stdout) => {
if (err) return cb(err)
cb(null, String(stdout).split('==END==\n')
.filter((raw) => !!raw.trim())
.map((raw) => {
const data = raw.split('==SPLIT==')
return {
hash: data[0],
message: data[1]
}
}))
}
)
}
}

View File

@ -5,6 +5,10 @@ const rawCommits = [
module.exports = {
exec: (command, cb) => {
if (/contains/.test(command)) {
return cb(null, `whatever\nmaster\n`)
}
cb(
null,
/\.\.HEAD/.test(command) ?

View File

@ -7,7 +7,7 @@ const commits = proxyquire('../../dist/lib/commits', {
test('commits since last release', (t) => {
t.test('get all commits', (tt) => {
commits({lastRelease: {}}, (err, commits) => {
commits({lastRelease: {}, branch: 'master'}, (err, commits) => {
tt.error(err)
tt.is(commits.length, 2, 'all commits')
tt.is(commits[0].hash, 'hash-one', 'parsed hash')
@ -18,7 +18,7 @@ test('commits since last release', (t) => {
})
t.test('get commits since hash', (tt) => {
commits({lastRelease: {gitHead: 'hash'}}, (err, commits) => {
commits({lastRelease: {gitHead: 'hash'}, branch: 'master'}, (err, commits) => {
tt.error(err)
tt.is(commits.length, 1, 'specified commits')
tt.is(commits[0].hash, 'hash-one', 'parsed hash')
@ -28,5 +28,13 @@ test('commits since last release', (t) => {
})
})
t.test('get commits since hash', (tt) => {
commits({lastRelease: {gitHead: 'notinhistory'}, branch: 'notmaster'}, (err, commits) => {
tt.ok(err)
tt.is(err.code, 'ENOTINHISTORY')
tt.end()
})
})
t.end()
})

View File

@ -3,7 +3,9 @@ const proxyquire = require('proxyquire')
require('../mocks/registry')
const pre = proxyquire('../../dist/pre', {
'child_process': require('../mocks/child-process')
'./lib/commits': proxyquire('../../dist/lib/commits', {
'child_process': require('../mocks/child-process')
})
})
const versions = {
@ -14,14 +16,13 @@ const plugins = {
verifyRelease: (release, cb) => cb(null, release),
analyzeCommits: (commits, cb) => cb(null, 'major'),
getLastRelease: ({ pkg }, cb) => {
cb(null, { version: versions[pkg.name] || null, gitHead: 'HEAD' })
cb(null, {version: versions[pkg.name] || null, gitHead: 'HEAD'})
}
}
const npm = {
registry: 'http://registry.npmjs.org/',
tag: 'latest'
}
test('full pre run', (t) => {
@ -29,6 +30,7 @@ test('full pre run', (t) => {
tt.plan(3)
pre({
branch: 'master',
npm,
pkg: {name: 'available'},
plugins
@ -43,6 +45,7 @@ test('full pre run', (t) => {
tt.plan(3)
pre({
branch: 'master',
npm,
pkg: {name: 'unavailable'},
plugins