greenkeeper-lockfile
Advanced tools
Comparing version 2.0.0 to 2.1.0
'use strict' | ||
const _ = require('lodash') | ||
const gitHelpers = require('../lib/git-helpers') | ||
const env = process.env | ||
/** | ||
* Last commit is a lockfile update | ||
*/ | ||
function isLockfileUpdate () { | ||
const reUpdateLockfile = /^chore\(package\): update lockfiles*$/mi | ||
const lastCommitMessage = gitHelpers.getLastCommitMessage() | ||
return reUpdateLockfile.test(lastCommitMessage) | ||
} | ||
module.exports = { | ||
repoSlug: `${env.CIRCLE_PROJECT_USERNAME}/${env.CIRCLE_PROJECT_REPONAME}`, | ||
branchName: env.CIRCLE_BRANCH, | ||
correctBuild: _.isEmpty(env.CI_PULL_REQUEST), | ||
uploadBuild: env.CIRCLE_NODE_INDEX === `${env.BUILD_LEADER_ID || 0}` | ||
// update, CIRCLE_PREVIOUS_BUILD_NUM is only null on the first job of the first workflow on the branch | ||
correctBuild: _.isEmpty(env.CI_PULL_REQUEST) && !env.CIRCLE_PREVIOUS_BUILD_NUM, | ||
// upload when last commit is lockfile update | ||
uploadBuild: env.CIRCLE_NODE_INDEX === `${env.BUILD_LEADER_ID || 0}` && isLockfileUpdate() | ||
} |
@@ -8,3 +8,3 @@ 'use strict' | ||
const gitUrl = env.CHANGE_URL || env.GIT_URL | ||
const origBranch = env.CHANGE_BRANCH || env.GIT_BRANCH | ||
const origBranch = env.CHANGE_TARGET || env.CHANGE_BRANCH || env.BRANCH_NAME || env.GIT_BRANCH | ||
@@ -11,0 +11,0 @@ // Different Jenkins plugins format the branch name differently |
{ | ||
"name": "greenkeeper-lockfile", | ||
"description": "Your lockfile, up to date, all the time", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"author": "Stephan Bönnemann <stephan@boennemann.me> (http://boennemann.me)", | ||
@@ -25,3 +25,3 @@ "bin": { | ||
"semantic-release": "^15.0.3", | ||
"sinon": "^5.0.0", | ||
"sinon": "^6.0.0", | ||
"standard": "^11.0.0" | ||
@@ -28,0 +28,0 @@ }, |
@@ -126,3 +126,6 @@ # Greenkeeper Lockfile | ||
In order to use `greenkeeper-lockfile` with CircleCI workflows, it must be in the first job run. Use [sequential job execution](https://circleci.com/docs/2.0/workflows/#sequential-job-execution-example) to ensure the job that runs `greenkeeper-lockfile` is always executed first. For example, if `greenkeeper-lockfile` is run in the `lockfile` job, all other jobs in the workflow must require the `lockfile` job to finish before running: | ||
In order to use `greenkeeper-lockfile` with CircleCI workflows, `greenkeeper-lockfile-update` must be run in the first job, while `greenkeeper-lockfile-upload` can be run in any job. | ||
If you want to upload the lockfile in a later job, the `.git` directory needs to be saved to cache after updating, and restored before uploading. ([example workflow config](https://github.com/patkub/test-greenkeeper-lockfile-circleci-first-push/blob/master/.circleci/config.yml)) | ||
Use [sequential job execution](https://circleci.com/docs/2.0/workflows/#sequential-job-execution-example) to ensure the job that runs `greenkeeper-lockfile-update` is always executed first. | ||
For example, if `greenkeeper-lockfile-update` is run in the `lockfile` job, all other jobs in the workflow must require the `lockfile` job to finish before running: | ||
@@ -129,0 +132,0 @@ ```yml |
@@ -29,7 +29,4 @@ 'use strict' | ||
const updateMessage = 'chore(package): update lockfile\n\nhttps://npm.im/greenkeeper-lockfile' | ||
test('do shrinkwrap for old npm versions', () => { | ||
prepare() | ||
expect.assertions(1) | ||
exec.withArgs('npm --version').returns('2.0.0') | ||
@@ -39,3 +36,3 @@ updateLockfile({}, {}) | ||
commitLockfiles() | ||
expect(exec.secondCall.calledWith('npm shrinkwrap')).toBeTruthy() | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
}) | ||
@@ -45,3 +42,2 @@ | ||
prepare() | ||
expect.assertions(1) | ||
exec.withArgs('npm --version').returns('3.0.0') | ||
@@ -51,3 +47,3 @@ updateLockfile(dependency, { yarn: true }) | ||
commitLockfiles() | ||
expect(exec.getCall(0).calledWith("yarn add 'my-dependency@1.0.0'")).toBeTruthy() | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
}) | ||
@@ -57,3 +53,2 @@ | ||
prepare() | ||
expect.assertions(1) | ||
const tildeDep = Object.assign({}, dependency, { | ||
@@ -67,3 +62,3 @@ prefix: null | ||
commitLockfiles() | ||
expect(exec.getCall(0).calledWith("yarn add 'my-dependency@1.0.0'")).toBeTruthy() | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
}) | ||
@@ -73,3 +68,2 @@ | ||
prepare() | ||
expect.assertions(1) | ||
process.env.GK_LOCK_YARN_OPTS = '--ignore-engines' | ||
@@ -80,3 +74,3 @@ exec.withArgs('npm --version').returns('3.0.0') | ||
commitLockfiles() | ||
expect(exec.getCall(0).calledWith("yarn add --ignore-engines 'my-dependency@1.0.0'")).toBeTruthy() | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
delete process.env.GK_LOCK_YARN_OPTS | ||
@@ -87,3 +81,2 @@ }) | ||
prepare() | ||
expect.assertions(1) | ||
exec.withArgs('npm --version').returns('3.0.0') | ||
@@ -94,3 +87,3 @@ exec.withArgs('npm5 -v').throws() | ||
commitLockfiles() | ||
expect(exec.getCall(2).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
}) | ||
@@ -100,3 +93,2 @@ | ||
prepare() | ||
expect.assertions(1) | ||
exec.withArgs('npm --version').returns('3.0.0') | ||
@@ -107,3 +99,3 @@ exec.withArgs('npm5 -v').returns('5.0.0') | ||
commitLockfiles() | ||
expect(exec.getCall(2).calledWith('npm5 install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
}) | ||
@@ -114,8 +106,6 @@ | ||
exec.withArgs('npm --version').returns('3.0.0') | ||
expect.assertions(2) | ||
updateLockfile(dependency, { npm: true }) | ||
stageLockfile() | ||
commitLockfiles() | ||
expect(exec.getCall(7).calledWith('git config user.email "support@greenkeeper.io"')).toBeTruthy() | ||
expect(exec.getCall(8).calledWith('git config user.name "greenkeeperio-bot"')).toBeTruthy() | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
}) | ||
@@ -128,8 +118,6 @@ | ||
exec.withArgs('npm --version').returns('3.0.0') | ||
expect.assertions(2) | ||
updateLockfile(dependency, { npm: true }) | ||
stageLockfile() | ||
commitLockfiles() | ||
expect(exec.getCall(7).calledWith('git config user.email "testbot@test.de"')).toBeTruthy() | ||
expect(exec.getCall(8).calledWith('git config user.name "testbot"')).toBeTruthy() | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
delete process.env.GK_LOCK_COMMIT_EMAIL | ||
@@ -141,3 +129,2 @@ delete process.env.GK_LOCK_COMMIT_NAME | ||
prepare() | ||
expect.assertions(2) | ||
const tildeDep = Object.assign({}, dependency, { | ||
@@ -152,4 +139,3 @@ prefix: '~', | ||
commitLockfiles() | ||
expect(exec.getCall(0).calledWith("yarn add 'my-dependency@~1.0.0'")).toBeTruthy() | ||
expect(exec.getCall(2).calledWith('npm install -S --save-prefix="~" my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
}) | ||
@@ -164,4 +150,3 @@ | ||
commitLockfiles() | ||
expect.assertions(1) | ||
expect(exec.callCount).toBe(8) | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
}) | ||
@@ -171,3 +156,2 @@ | ||
prepare() | ||
expect.assertions(1) | ||
exec.withArgs('npm --version').returns('3.0.0') | ||
@@ -177,18 +161,13 @@ updateLockfile(dependency, {}) | ||
commitLockfiles() | ||
expect(exec.getCall(8).calledWith(`git commit -m "${updateMessage}"`)).toBeTruthy() | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
}) | ||
test('with truthy GK_LOCK_COMMIT_AMEND', () => { | ||
const fixtures = ['1', 'true', 'foo'] | ||
expect.assertions(fixtures.length) | ||
fixtures.forEach((fixture) => { | ||
prepare() | ||
process.env.GK_LOCK_COMMIT_AMEND = fixture | ||
exec.withArgs('npm --version').returns('3.0.0') | ||
updateLockfile(dependency, {}) | ||
stageLockfile() | ||
commitLockfiles() | ||
console.log(exec.args) | ||
expect(exec.getCall(7).calledWith(`git commit --amend --author="greenkeeperio-bot <support@greenkeeper.io>" --no-edit`)).toBeTruthy() | ||
}) | ||
prepare() | ||
process.env.GK_LOCK_COMMIT_AMEND = 'true' | ||
exec.withArgs('npm --version').returns('3.0.0') | ||
updateLockfile(dependency, {}) | ||
stageLockfile() | ||
commitLockfiles() | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
delete process.env.GK_LOCK_COMMIT_AMEND | ||
@@ -198,15 +177,10 @@ }) | ||
test('with truthy GK_LOCK_COMMIT_AMEND and GK_LOCK_COMMIT_NAME/EMAIL', () => { | ||
const fixtures = ['1', 'true', 'foo'] | ||
expect.assertions(fixtures.length) | ||
process.env.GK_LOCK_COMMIT_NAME = 'Example Person' | ||
process.env.GK_LOCK_COMMIT_EMAIL = 'example@website.com' | ||
fixtures.forEach((fixture) => { | ||
prepare() | ||
process.env.GK_LOCK_COMMIT_AMEND = fixture | ||
exec.withArgs('npm --version').returns('3.0.0') | ||
updateLockfile(dependency, {}) | ||
stageLockfile() | ||
commitLockfiles() | ||
expect(exec.getCall(7).calledWith(`git commit --amend --author="Example Person <example@website.com>" --no-edit`)).toBeTruthy() | ||
}) | ||
prepare() | ||
process.env.GK_LOCK_COMMIT_AMEND = '1' | ||
exec.withArgs('npm --version').returns('3.0.0') | ||
updateLockfile(dependency, {}) | ||
stageLockfile() | ||
commitLockfiles() | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
delete process.env.GK_LOCK_COMMIT_AMEND | ||
@@ -218,14 +192,10 @@ delete process.env.GK_LOCK_COMMIT_NAME | ||
test('with falsy GK_LOCK_COMMIT_AMEND', () => { | ||
const fixtures = [undefined, '0', 'false', 'null', 'undefined'] | ||
expect.assertions(fixtures.length) | ||
fixtures.forEach((fixture) => { | ||
prepare() | ||
process.env.GK_LOCK_COMMIT_AMEND = fixture | ||
exec.withArgs('npm --version').returns('3.0.0') | ||
updateLockfile(dependency, {}) | ||
stageLockfile() | ||
commitLockfiles() | ||
expect(exec.getCall(8).calledWith(`git commit -m "${updateMessage}"`)).toBeTruthy() | ||
}) | ||
prepare() | ||
process.env.GK_LOCK_COMMIT_AMEND = 'false' | ||
exec.withArgs('npm --version').returns('3.0.0') | ||
updateLockfile(dependency, {}) | ||
stageLockfile() | ||
commitLockfiles() | ||
expect(exec.args.map(args => args[0])).toMatchSnapshot() | ||
delete process.env.GK_LOCK_COMMIT_AMEND | ||
}) |
@@ -15,3 +15,7 @@ 'use strict' | ||
const prepare = () => { | ||
afterAll(() => { | ||
childProcess.execSync.restore() | ||
}) | ||
function testFixture (fixtureDirectory, noLog) { | ||
exec.reset() | ||
@@ -21,102 +25,27 @@ exec.withArgs('git status --porcelain').returns('1') | ||
exec.withArgs('npm5 -v').throws() | ||
} | ||
if (!noLog) exec.withArgs(`git log --oneline origin/greenkeeper/my-dependency-1.0.0...master | grep 'chore(package): update lockfile'`).throws({status: 1, stdout: '', stderr: ''}) | ||
// Partial function for stubbing the complicated command used for finding branch commits | ||
const logPartial = (branchName) => { | ||
return exec.withArgs(`git log --oneline origin/${branchName}...master | grep 'chore(package): update lockfile'`) | ||
process.chdir(path.join(__dirname, fixtureDirectory)) | ||
update() | ||
expect(exec.getCalls().map(call => call.args[0])).toMatchSnapshot() | ||
} | ||
// Common `exec` errors that can be thrown | ||
const logResponses = { | ||
errored: {status: 2}, | ||
noMatch: {status: 1, stdout: '', stderr: ''} | ||
const tests = { | ||
'monorepo: root package': 'fixtures/root-package', | ||
'monorepo: root and one sub package': 'fixtures/root-and-one-sub', | ||
'monorepo: root and two sub package': 'fixtures/root-and-two-sub', | ||
'monorepo: root and two sub package at different levels': 'fixtures/root-and-two-diff-sub', | ||
'monorepo: no root and one sub package': 'fixtures/no-root-and-one-sub', | ||
'monorepo: no root and two sub package': 'fixtures/no-root-and-two-sub', | ||
'monorepo: no root and two sub package at different levels': 'fixtures/no-root-and-two-diff-sub' | ||
} | ||
// Tip for writing more assertions: console.log(exec.args) shows the list of invocations | ||
afterAll(() => { | ||
childProcess.execSync.restore() | ||
}) | ||
function runUpdateInSubdir (dir) { | ||
process.chdir(path.join(__dirname, dir)) | ||
update() | ||
for (const name in tests) { | ||
test(name, () => testFixture(tests[name])) | ||
} | ||
// Monorepo tests | ||
test('monorepo: root package', () => { | ||
prepare() | ||
logPartial('greenkeeper/my-dependency-1.0.0').throws(logResponses.noMatch) | ||
expect.assertions(2) | ||
runUpdateInSubdir('fixtures/root-package') | ||
expect(exec.getCall(9).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.callCount).toEqual(18) | ||
}) | ||
// special case | ||
test('monorepo: no package.json', () => { | ||
prepare() | ||
expect.assertions(1) | ||
runUpdateInSubdir('fixtures/no-package') | ||
expect(exec.callCount).toEqual(6) | ||
testFixture('fixtures/no-package', true) | ||
}) | ||
test('monorepo: root and one sub package', () => { | ||
prepare() | ||
logPartial('greenkeeper/my-dependency-1.0.0').throws(logResponses.noMatch) | ||
expect.assertions(3) | ||
runUpdateInSubdir('fixtures/root-and-one-sub') | ||
expect(exec.getCall(9).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.getCall(16).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.callCount).toEqual(25) | ||
}) | ||
test('monorepo: root and two sub package', () => { | ||
prepare() | ||
logPartial('greenkeeper/my-dependency-1.0.0').throws(logResponses.noMatch) | ||
expect.assertions(4) | ||
runUpdateInSubdir('fixtures/root-and-two-sub') | ||
expect(exec.getCall(9).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.getCall(16).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.getCall(23).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.callCount).toEqual(32) | ||
}) | ||
test('monorepo: root and two sub package at different levels', () => { | ||
prepare() | ||
logPartial('greenkeeper/my-dependency-1.0.0').throws(logResponses.noMatch) | ||
expect.assertions(4) | ||
runUpdateInSubdir('fixtures/root-and-two-diff-sub') | ||
expect(exec.getCall(9).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.getCall(16).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.getCall(23).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.callCount).toEqual(32) | ||
}) | ||
test('monorepo: no root and one sub package', () => { | ||
prepare() | ||
logPartial('greenkeeper/my-dependency-1.0.0').throws(logResponses.noMatch) | ||
expect.assertions(2) | ||
runUpdateInSubdir('fixtures/no-root-and-one-sub') | ||
expect(exec.getCall(9).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.callCount).toEqual(18) | ||
}) | ||
test('monorepo: no root and two sub package', () => { | ||
prepare() | ||
logPartial('greenkeeper/my-dependency-1.0.0').throws(logResponses.noMatch) | ||
expect.assertions(3) | ||
runUpdateInSubdir('fixtures/no-root-and-two-sub') | ||
expect(exec.getCall(9).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.getCall(16).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.callCount).toEqual(25) | ||
}) | ||
test('monorepo: no root and two sub package at different levels', () => { | ||
prepare() | ||
logPartial('greenkeeper/my-dependency-1.0.0').throws(logResponses.noMatch) | ||
expect.assertions(3) | ||
runUpdateInSubdir('fixtures/no-root-and-two-diff-sub') | ||
expect(exec.getCall(9).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.getCall(16).calledWith('npm install -S my-dependency@1.0.0')).toBeTruthy() | ||
expect(exec.callCount).toEqual(25) | ||
}) |
Sorry, the diff of this file is not supported yet
58406
44
188
39
813