Skip to content

Commit

Permalink
feat: skip postinstall steps when templateVersion has not changed
Browse files Browse the repository at this point in the history
  • Loading branch information
nlf committed Aug 30, 2021
1 parent 6cdfd80 commit 2386051
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 9 deletions.
8 changes: 5 additions & 3 deletions bin/postinstall.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ const main = async () => {

const root = dirname(pkgPath)

await patchPackage(root)
await copyContent(root)
return installPackages(root)
const needsAction = await patchPackage(root)
if (needsAction) {
await copyContent(root)
return installPackages(root)
}
}

// we export the promise so it can be awaited in tests
Expand Down
22 changes: 20 additions & 2 deletions lib/package.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
const PackageJson = require('@npmcli/package-json')

const TEMPLATE_VERSION = require('../package.json').version

const changes = {
author: 'GitHub Inc.',
files: ['bin', 'lib'],
license: 'ISC',
templateVersion: TEMPLATE_VERSION,
scripts: {
lint: `eslint '**/*.js'`,
lintfix: 'npm run lint -- --fix',
Expand All @@ -19,9 +22,24 @@ const changes = {
const patchPackage = async (root) => {
const pkg = await PackageJson.load(root)

pkg.update(changes)
// if the target package.json has a templateVersion field matching our own
// current version, we return false here so the postinstall script knows to
// exit early instead of running everything again
if (pkg.content.templateVersion === TEMPLATE_VERSION) {
return false
}

// we build a new object here so our exported set of changes is not modified
pkg.update({
...changes,
scripts: {
...pkg.content.scripts,
...changes.scripts,
},
})

return pkg.save()
await pkg.save()
return true
}
patchPackage.changes = changes

Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"prepublishOnly": "git push origin --follow-tags",
"snap": "tap",
"test": "tap",
"posttest": "npm run lint"
"posttest": "npm run lint",
"postinstall": "bin/postinstall.js"
},
"repository": {
"type": "git",
Expand All @@ -38,5 +39,6 @@
"eslint-plugin-node": "^11.1.0",
"spawk": "^1.7.1",
"tap": "^15.0.9"
}
},
"templateVersion": "1.0.0"
}
35 changes: 33 additions & 2 deletions test/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const { join } = require('path')
const fs = require('@npmcli/fs')
const t = require('tap')

const TEMPLATE_VERSION = require('../package.json').version

const patchPackage = require('../lib/package.js')

t.test('can patch a package.json', async (t) => {
Expand All @@ -11,16 +13,45 @@ t.test('can patch a package.json', async (t) => {
author: 'someone else',
files: [],
license: 'MIT',
scripts: {
foo: 'echo bar',
},
}

const project = t.testdir({
'package.json': JSON.stringify(pkg, null, 2),
})

const needsAction = await patchPackage(project)
t.equal(needsAction, true, 'returned true')

const contents = await fs.readFile(join(project, 'package.json'), {
encoding: 'utf8',
})
const parsed = JSON.parse(contents)
t.match(parsed, patchPackage.changes, 'all changes were applied')
t.equal(parsed.scripts.foo, 'echo bar', 'did not remove existing script')
})

t.test('returns false when templateVersion matches own version', async (t) => {
const pkg = {
name: '@npmcli/foo',
templateVersion: TEMPLATE_VERSION,
version: '1.0.0',
author: 'someone else',
files: [],
license: 'MIT',
}

const project = t.testdir({
'package.json': JSON.stringify(pkg, null, 2),
})

await patchPackage(project)
const needsAction = await patchPackage(project)
t.equal(needsAction, false, 'returned false')

const contents = await fs.readFile(join(project, 'package.json'), {
encoding: 'utf8',
})
t.match(JSON.parse(contents), patchPackage.changes, 'all changes were applied')
t.notMatch(JSON.parse(contents), patchPackage.changes, 'changes were NOT applied')
})
31 changes: 31 additions & 0 deletions test/postinstall.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ const fs = require('@npmcli/fs')
const spawk = require('spawk')
const t = require('tap')

const TEMPLATE_VERSION = require('../package.json').version

const copyContents = require('../lib/content/index.js')
const installPackages = require('../lib/install.js')
const patchPackage = require('../lib/package.js')
Expand Down Expand Up @@ -32,6 +34,35 @@ t.test('when npm_package_json is unset logs stack and sets exitCode', async (t)
t.equal(process.exitCode, 1, 'set process.exitCode')
})

t.test('when patchPackage returns false no further action is taken', async (t) => {
const pkg = {
name: '@npmcli/foo-test',
templateVersion: TEMPLATE_VERSION,
}

// normalize is necessary here until https://github.com/tapjs/libtap/pull/40
// is shipped in a new release of tap, otherwise the spawk interceptors
// below will not match and tests will fail in windows
const root = normalize(t.testdir({
'package.json': JSON.stringify(pkg, null, 2),
}))

const _env = process.env.npm_package_json
process.env.npm_package_json = join(root, 'package.json')

t.teardown(() => {
process.env.npm_package_json = _env
})

// t.mock instead of require so the cache doesn't interfere
// this will reject if actions are taken due to spawk preventing unmatched
// spawn calls
await t.mock('../bin/postinstall.js')
// not setting process.exitCode tells us that the postinstall script
// finished successfully since it would be set if it failed
t.equal(process.exitCode, undefined, 'did not set process.exitCode')
})

t.test('sets up a new project', async (t) => {
const pkg = {
name: '@npmcli/foo-test',
Expand Down

0 comments on commit 2386051

Please sign in to comment.