Skip to content

Commit d718afa

Browse files
committed
chore: create smoke-publish-test.sh scripts
This makes it easier to debug this test locally. Previously this was avoided because the test will install npm globally and prune deps which can be disruptive to a local checkout. This is somewhat mitigated now with some cleanup and better messaging when run locally.
1 parent 1524cfd commit d718afa

File tree

9 files changed

+167
-97
lines changed

9 files changed

+167
-97
lines changed

.github/workflows/ci-release.yml

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -217,27 +217,8 @@ jobs:
217217
run: node scripts/git-dirty.js
218218
- name: Reset Deps
219219
run: node scripts/resetdeps.js
220-
- name: Pack
221-
env:
222-
SMOKE_PUBLISH_NPM: 1
223-
run: |
224-
NPM_VERSION="$(node . --version)-$GITHUB_SHA.0"
225-
node . version $NPM_VERSION --ignore-scripts
226-
node scripts/publish.js --pack-destination=$RUNNER_TEMP
227-
export SMOKE_PUBLISH_TARBALL="$RUNNER_TEMP/npm-$NPM_VERSION.tgz"
228-
node . install --global $SMOKE_PUBLISH_TARBALL
229-
node . install -w smoke-tests --ignore-scripts --no-audit --no-fund
230-
# call installed npm instead of local source since we are testing
231-
# the packed tarball that we just installed globally
232-
NPM_GLOBAL_VERSION="$(npm --version)"
233-
npm help
234-
if [ "$NPM_GLOBAL_VERSION" == "$NPM_VERSION" ]; then
235-
npm test -w smoke-tests --ignore-scripts
236-
else
237-
echo "global npm is not the correct version for smoke-publish"
238-
echo "found: $NPM_GLOBAL_VERSION, expected: $NPM_VERSION"
239-
exit 1
240-
fi
220+
- name: Smoke Publish
221+
run: ./scripts/smoke-publish-test.sh
241222
- name: Conclude Check
242223
uses: LouisBrunner/[email protected]
243224
if: always()

docs/bin/build.js

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,3 @@
1-
if (
2-
process.env.SMOKE_PUBLISH_NPM &&
3-
!require('semver').satisfies(process.version, require('../package.json').engines.node)
4-
) {
5-
// The docs tooling is kept in sync between releases and dependencies that are not compatible
6-
// with the lower bound of npm@8 engines are used. When we run the SMOKE_PUBLISH_NPM we are
7-
// testing that npm is able to pack and install itself locally and then run its own smoke tests.
8-
// Packing will run this script automatically so in the cases where the node version is
9-
// not compatible, it is ok to bail on this script since the generated docs are not used in
10-
// the smoke tests.
11-
console.log(`Skipping docs build due to SMOKE_PUBLISH_NPM and ${process.version}`)
12-
return
13-
}
14-
151
const run = require('../lib/build.js')
162
const { paths } = require('../lib/index')
173

mock-registry/lib/index.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ const npa = require('npm-package-arg')
44
const Nock = require('nock')
55
const stringify = require('json-stringify-safe')
66

7+
const logReq = (req, ...keys) => {
8+
const obj = JSON.parse(stringify(req))
9+
const res = {}
10+
for (const [k, v] of Object.entries(obj)) {
11+
if (!keys.includes(k)) {
12+
res[k] = v
13+
}
14+
}
15+
return stringify(res, null, 2)
16+
}
17+
718
class MockRegistry {
819
#tap
920
#nock
@@ -40,7 +51,8 @@ class MockRegistry {
4051
// mocked with a 404, 500, etc.
4152
// XXX: this is opt-in currently because it breaks some existing CLI
4253
// tests. We should work towards making this the default for all tests.
43-
t.fail(`Unmatched request: ${stringify(req, null, 2)}`)
54+
t.comment(logReq(req, 'interceptors', 'socket', 'response', '_events'))
55+
t.fail(`Unmatched request: ${req.method} ${req.path}`)
4456
}
4557
}
4658

@@ -357,7 +369,7 @@ class MockRegistry {
357369
})
358370
}
359371

360-
async package ({ manifest, times = 1, query, tarballs }) {
372+
async package ({ manifest, times = 1, query, tarballs, tarballTimes = 1 }) {
361373
let nock = this.nock
362374
const spec = npa(manifest.name)
363375
nock = nock.get(this.fullPath(`/${spec.escapedName}`)).times(times)
@@ -368,17 +380,17 @@ class MockRegistry {
368380
if (tarballs) {
369381
for (const [version, tarball] of Object.entries(tarballs)) {
370382
const m = manifest.versions[version]
371-
nock = await this.tarball({ manifest: m, tarball })
383+
nock = await this.tarball({ manifest: m, tarball, times: tarballTimes })
372384
}
373385
}
374386
this.nock = nock
375387
}
376388

377-
async tarball ({ manifest, tarball }) {
389+
async tarball ({ manifest, tarball, times = 1 }) {
378390
const nock = this.nock
379391
const dist = new URL(manifest.dist.tarball)
380392
const tar = await pacote.tarball(tarball, { Arborist })
381-
nock.get(this.fullPath(dist.pathname)).reply(200, tar)
393+
nock.get(this.fullPath(dist.pathname)).times(times).reply(200, tar)
382394
return nock
383395
}
384396

scripts/publish.js

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@ const getPublishes = async ({ force }) => {
7272
}
7373

7474
const main = async (opts) => {
75-
const packOnly = opts.pack || opts.packDestination
76-
const publishes = await getPublishes({ force: packOnly })
75+
const { isLocal, smokePublish, packDestination } = opts
76+
const isPack = !!packDestination
77+
const publishes = await getPublishes({ force: isPack })
7778

7879
if (!publishes.length) {
7980
throw new Error(
@@ -88,12 +89,12 @@ const main = async (opts) => {
8889
}
8990

9091
const confirmMessage = [
91-
`Ready to ${packOnly ? 'pack' : 'publish'} the following packages:`,
92+
`Ready to ${isPack ? 'pack' : 'publish'} the following packages:`,
9293
table.toString(),
93-
packOnly ? null : 'Ok to proceed? ',
94+
isPack ? null : 'Ok to proceed? ',
9495
].filter(Boolean).join('\n')
9596

96-
if (packOnly) {
97+
if (isPack) {
9798
log.info(confirmMessage)
9899
} else {
99100
const confirm = await read({ prompt: confirmMessage, default: 'y' })
@@ -116,21 +117,26 @@ const main = async (opts) => {
116117

117118
await npm('prune', '--omit=dev', '--no-save', '--no-audit', '--no-fund')
118119
await npm('install', '-w', 'docs', '--ignore-scripts', '--no-audit', '--no-fund')
119-
await git.dirty()
120+
if (isLocal && smokePublish) {
121+
log.info(`Skipping git dirty check due to local smoke publish test being run`)
122+
} else {
123+
await git.dirty()
124+
}
120125

121126
for (const publish of publishes) {
122127
const workspace = publish.workspace && `--workspace=${publish.name}`
123-
if (packOnly) {
128+
const publishPkg = (...args) => npm('publish', workspace, `--tag=${publish.tag}`, ...args)
129+
if (isPack) {
124130
await npm(
125131
'pack',
126132
workspace,
127133
opts.packDestination && `--pack-destination=${opts.packDestination}`
128134
)
135+
if (smokePublish) {
136+
await publishPkg('--dry-run')
137+
}
129138
} else {
130-
await npm(
131-
'publish',
132-
workspace,
133-
`--tag=${publish.tag}`,
139+
await publishPkg(
134140
opts.dryRun && '--dry-run',
135141
opts.otp && `--otp=${opts.otp === 'op' ? await op() : opts.otp}`
136142
)

scripts/smoke-publish-test.sh

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env bash
2+
3+
set -eo pipefail
4+
5+
IS_LOCAL="false"
6+
IS_CI="true"
7+
8+
if [ -z "$CI" ]; then
9+
echo "Running locally will overwrite your globally installed npm."
10+
GITHUB_SHA=$(git rev-parse HEAD)
11+
RUNNER_TEMP=$(mktemp -d)
12+
IS_LOCAL="true"
13+
IS_CI="false"
14+
fi
15+
16+
if [ -z "$GITHUB_SHA" ]; then
17+
echo "Error: GITHUB_SHA is required"
18+
exit 1
19+
fi
20+
21+
if [ -z "$RUNNER_TEMP" ]; then
22+
echo "Error: RUNNER_TEMP is required"
23+
exit 1
24+
fi
25+
26+
ORIGINAL_GLOBAL_NPM_VERSION=$(npm --version)
27+
if [ ${#ORIGINAL_GLOBAL_NPM_VERSION} -gt 40 ]; then
28+
echo "Error: Global npm version already contains a git SHA ${ORIGINAL_GLOBAL_NPM_VERSION}"
29+
exit 1
30+
fi
31+
32+
ORIGINAL_LOCAL_NPM_VERSION=$(node . --version)
33+
if [ ${#ORIGINAL_LOCAL_NPM_VERSION} -gt 40 ]; then
34+
echo "Error: Local npm version already contains a git SHA ${ORIGINAL_LOCAL_NPM_VERSION}"
35+
exit 1
36+
fi
37+
NPM_VERSION="$ORIGINAL_LOCAL_NPM_VERSION-$GITHUB_SHA.0"
38+
39+
# Only cleanup locally
40+
if [ "$IS_LOCAL" == "true" ]; then
41+
function cleanup {
42+
npm pkg set version=$ORIGINAL_LOCAL_NPM_VERSION
43+
node scripts/resetdeps.js
44+
if [ "$(git rev-parse HEAD)" != "$GITHUB_SHA" ]; then
45+
echo "==================================="
46+
echo "==================================="
47+
echo "HEAD is on a different commit."
48+
echo "==================================="
49+
echo "==================================="
50+
fi
51+
if [ "$(npm --version)" == "$NPM_VERSION" ]; then
52+
echo "==================================="
53+
echo "==================================="
54+
echo "Global npm version has changed to $NPM_VERSION"
55+
echo "Run the following to change it back"
56+
echo "npm install npm@$ORIGINAL_GLOBAL_NPM_VERSION -g"
57+
echo "==================================="
58+
echo "==================================="
59+
fi
60+
}
61+
trap cleanup EXIT
62+
fi
63+
64+
# Version the local source of npm with the current git sha and
65+
# and pack and install it globally the same way we would if we
66+
# were publishing it to the registry. The only difference is in the
67+
# publish.js script which will only pack and not publish
68+
node . version $NPM_VERSION --ignore-scripts --git-tag-version="$IS_CI"
69+
node scripts/publish.js --pack-destination=$RUNNER_TEMP --smoke-publish=true --is-local="$IS_LOCAL"
70+
NPM_TARBALL="$RUNNER_TEMP/npm-$NPM_VERSION.tgz"
71+
node . install --global $NPM_TARBALL
72+
73+
# Only run the tests if we are sure we have the right version
74+
# otherwise the tests being run are pointless
75+
NPM_GLOBAL_VERSION="$(npm --version)"
76+
if [ "$NPM_GLOBAL_VERSION" != "$NPM_VERSION" ]; then
77+
echo "global npm is not the correct version for smoke-publish"
78+
echo "found: $NPM_GLOBAL_VERSION, expected: $NPM_VERSION"
79+
exit 1
80+
fi
81+
82+
# Install dev deps only for smoke tests so they can be run
83+
node . install -w smoke-tests --ignore-scripts --no-audit --no-fund
84+
# Run smoke tests with env vars so it uses the globally installed tarball we
85+
# just packed/installed. The tacked on args at the end are only used for
86+
# debugging locally when we want to pass args to the smoke-tests to limit the
87+
# files being run or grep a test, etc. Also now set CI=true so we get more
88+
# debug output in our tap tests
89+
CI="true" SMOKE_PUBLISH_NPM="1" SMOKE_PUBLISH_TARBALL="$NPM_TARBALL" npm test \
90+
-w smoke-tests \
91+
--ignore-scripts \
92+
-- -Rtap "$@"

scripts/template-oss/ci-release-yml.hbs

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,8 @@
1111
jobCreateCheck=(obj sha="${{ inputs.check-sha }}")
1212
windowsCI=false
1313
}}
14-
- name: Pack
15-
env:
16-
SMOKE_PUBLISH_NPM: 1
17-
run: |
18-
NPM_VERSION="$({{ rootNpmPath }} --version)-$GITHUB_SHA.0"
19-
{{ rootNpmPath }} version $NPM_VERSION --ignore-scripts
20-
node scripts/publish.js --pack-destination=$RUNNER_TEMP
21-
export SMOKE_PUBLISH_TARBALL="$RUNNER_TEMP/npm-$NPM_VERSION.tgz"
22-
{{ rootNpmPath }} install --global $SMOKE_PUBLISH_TARBALL
23-
{{ rootNpmPath }} install -w smoke-tests --ignore-scripts --no-audit --no-fund
24-
# call installed npm instead of local source since we are testing
25-
# the packed tarball that we just installed globally
26-
NPM_GLOBAL_VERSION="$(npm --version)"
27-
npm help
28-
if [ "$NPM_GLOBAL_VERSION" == "$NPM_VERSION" ]; then
29-
npm test -w smoke-tests --ignore-scripts
30-
else
31-
echo "global npm is not the correct version for smoke-publish"
32-
echo "found: $NPM_GLOBAL_VERSION, expected: $NPM_VERSION"
33-
exit 1
34-
fi
14+
- name: Smoke Publish
15+
run: ./scripts/smoke-publish-test.sh
3516
- name: Conclude Check
3617
uses: LouisBrunner/[email protected]
3718
if: always()

scripts/util.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ git.dirty = () => npmGit.isClean({ cwd: CWD }).then(async r => {
132132
return 'git clean'
133133
}
134134
await git('status', '--porcelain=v1', '-uno')
135-
await git('diff')
135+
await git('--no-pager', 'diff')
136136
throw new Error('git dirty')
137137
})
138138

smoke-tests/test/fixtures/setup.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ const getCleanPaths = async () => {
7575

7676
module.exports = async (t, { testdir = {}, debug, mockRegistry = true, useProxy = false } = {}) => {
7777
const debugLog = debug || CI ? (...a) => t.comment(...a) : () => {}
78+
debugLog({ SMOKE_PUBLISH_NPM, SMOKE_PUBLISH_TARBALL, CI })
79+
7880
const cleanPaths = await getCleanPaths()
7981

8082
// setup fixtures

smoke-tests/test/npm-replace-global.js

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ t.test('pack and replace global self', async t => {
9494
})
9595

9696
t.test('publish and replace global self', async t => {
97-
let publishedPackument = null
9897
const pkg = require('../../package.json')
9998
const { name, version } = pkg
10099

@@ -114,39 +113,44 @@ t.test('publish and replace global self', async t => {
114113
},
115114
})
116115

117-
const npmPackage = async ({ manifest, ...opts } = {}) => {
116+
const mockNpmPackage = async ({ manifest, ...opts } = {}) => {
118117
await registry.package({
119118
manifest: registry.manifest({ name, ...manifest }),
120119
...opts,
121120
})
121+
await fs.rm(cache, { recursive: true, force: true })
122122
}
123123

124-
const npmInstall = async (useNpm) => {
125-
await npmPackage({
126-
manifest: { packuments: [publishedPackument] },
127-
tarballs: { [version]: tarball },
128-
times: 3,
129-
})
130-
await fs.rm(cache, { recursive: true, force: true })
131-
await useNpm('install', 'npm@latest', '--global')
132-
return getPaths()
124+
const mockNpmPublish = () => {
125+
let publishedPackument = null
126+
registry.nock.put('/npm', body => {
127+
if (body._id === 'npm' && body.versions[version]) {
128+
publishedPackument = body.versions[version]
129+
return true
130+
}
131+
return false
132+
}).reply(201, {})
133+
return {
134+
get packument () {
135+
return publishedPackument
136+
},
137+
}
133138
}
134139

140+
const publish = mockNpmPublish()
141+
await mockNpmPackage()
142+
await npmLocal('publish', { proxy: true, force: true })
143+
135144
const tarball = await npmLocalTarball()
136145

137-
if (setup.SMOKE_PUBLISH) {
138-
await npmPackage()
139-
}
140-
registry.nock.put('/npm', body => {
141-
if (body._id === 'npm' && body.versions[version]) {
142-
publishedPackument = body.versions[version]
143-
return true
144-
}
145-
return false
146-
}).reply(201, {})
147-
await npmLocal('publish', { proxy: true, force: true })
146+
await mockNpmPackage({
147+
manifest: { packuments: [publish.packument] },
148+
tarballs: { [version]: tarball },
149+
times: 3,
150+
})
151+
await npm('install', 'npm@latest', '--global')
152+
const paths = await getPaths()
148153

149-
const paths = await npmInstall(npm)
150154
t.equal(paths.npmRoot, join(globalNodeModules, 'npm'), 'npm root is in the testdir')
151155
t.equal(paths.pathNpm, join(globalBin, 'npm'), 'npm bin is in the testdir')
152156
t.equal(paths.pathNpx, join(globalBin, 'npx'), 'npx bin is in the testdir')
@@ -159,7 +163,13 @@ t.test('publish and replace global self', async t => {
159163
'bin has npm and npx'
160164
)
161165

162-
t.strictSame(await npmInstall(npmPath), paths)
166+
await mockNpmPackage({
167+
manifest: { packuments: [publish.packument] },
168+
tarballs: { [version]: tarball },
169+
times: 3,
170+
})
171+
await npmPath('install', 'npm@latest', '--global')
172+
t.strictSame(await getPaths(), paths)
163173
})
164174

165175
t.test('fail when updating with lazy require', async t => {

0 commit comments

Comments
 (0)