Skip to content

Commit

Permalink
chore: create smoke-publish-test.sh scripts
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
lukekarrys committed May 8, 2024
1 parent 1524cfd commit 1f4d73c
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 130 deletions.
23 changes: 2 additions & 21 deletions .github/workflows/ci-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -217,27 +217,8 @@ jobs:
run: node scripts/git-dirty.js
- name: Reset Deps
run: node scripts/resetdeps.js
- name: Pack
env:
SMOKE_PUBLISH_NPM: 1
run: |
NPM_VERSION="$(node . --version)-$GITHUB_SHA.0"
node . version $NPM_VERSION --ignore-scripts
node scripts/publish.js --pack-destination=$RUNNER_TEMP
export SMOKE_PUBLISH_TARBALL="$RUNNER_TEMP/npm-$NPM_VERSION.tgz"
node . install --global $SMOKE_PUBLISH_TARBALL
node . install -w smoke-tests --ignore-scripts --no-audit --no-fund
# call installed npm instead of local source since we are testing
# the packed tarball that we just installed globally
NPM_GLOBAL_VERSION="$(npm --version)"
npm help
if [ "$NPM_GLOBAL_VERSION" == "$NPM_VERSION" ]; then
npm test -w smoke-tests --ignore-scripts
else
echo "global npm is not the correct version for smoke-publish"
echo "found: $NPM_GLOBAL_VERSION, expected: $NPM_VERSION"
exit 1
fi
- name: Smoke Publish
run: ./scripts/smoke-publish-test.sh
- name: Conclude Check
uses: LouisBrunner/[email protected]
if: always()
Expand Down
14 changes: 0 additions & 14 deletions docs/bin/build.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
if (
process.env.SMOKE_PUBLISH_NPM &&
!require('semver').satisfies(process.version, require('../package.json').engines.node)
) {
// The docs tooling is kept in sync between releases and dependencies that are not compatible
// with the lower bound of npm@8 engines are used. When we run the SMOKE_PUBLISH_NPM we are
// testing that npm is able to pack and install itself locally and then run its own smoke tests.
// Packing will run this script automatically so in the cases where the node version is
// not compatible, it is ok to bail on this script since the generated docs are not used in
// the smoke tests.
console.log(`Skipping docs build due to SMOKE_PUBLISH_NPM and ${process.version}`)
return
}

const run = require('../lib/build.js')
const { paths } = require('../lib/index')

Expand Down
8 changes: 4 additions & 4 deletions mock-registry/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ class MockRegistry {
})
}

async package ({ manifest, times = 1, query, tarballs }) {
async package ({ manifest, times = 1, query, tarballs, tarballTimes = 1 }) {
let nock = this.nock
const spec = npa(manifest.name)
nock = nock.get(this.fullPath(`/${spec.escapedName}`)).times(times)
Expand All @@ -368,17 +368,17 @@ class MockRegistry {
if (tarballs) {
for (const [version, tarball] of Object.entries(tarballs)) {
const m = manifest.versions[version]
nock = await this.tarball({ manifest: m, tarball })
nock = await this.tarball({ manifest: m, tarball, times: tarballTimes })
}
}
this.nock = nock
}

async tarball ({ manifest, tarball }) {
async tarball ({ manifest, tarball, times = 1 }) {
const nock = this.nock
const dist = new URL(manifest.dist.tarball)
const tar = await pacote.tarball(tarball, { Arborist })
nock.get(this.fullPath(dist.pathname)).reply(200, tar)
nock.get(this.fullPath(dist.pathname)).times(times).reply(200, tar)
return nock
}

Expand Down
28 changes: 17 additions & 11 deletions scripts/publish.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ const getPublishes = async ({ force }) => {
}

const main = async (opts) => {
const packOnly = opts.pack || opts.packDestination
const publishes = await getPublishes({ force: packOnly })
const { isLocal, smokePublish, packDestination } = opts
const isPack = !!packDestination
const publishes = await getPublishes({ force: isPack })

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

const confirmMessage = [
`Ready to ${packOnly ? 'pack' : 'publish'} the following packages:`,
`Ready to ${isPack ? 'pack' : 'publish'} the following packages:`,
table.toString(),
packOnly ? null : 'Ok to proceed? ',
isPack ? null : 'Ok to proceed? ',
].filter(Boolean).join('\n')

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

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

for (const publish of publishes) {
const workspace = publish.workspace && `--workspace=${publish.name}`
if (packOnly) {
const publishPkg = (...args) => npm('publish', workspace, `--tag=${publish.tag}`, ...args)
if (isPack) {
await npm(
'pack',
workspace,
opts.packDestination && `--pack-destination=${opts.packDestination}`
)
if (smokePublish) {
await publishPkg('--dry-run')
}
} else {
await npm(
'publish',
workspace,
`--tag=${publish.tag}`,
await publishPkg(
opts.dryRun && '--dry-run',
opts.otp && `--otp=${opts.otp === 'op' ? await op() : opts.otp}`
)
Expand Down
79 changes: 79 additions & 0 deletions scripts/smoke-publish-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env bash

set -eo pipefail

IS_LOCAL="false"

if [ -z "$CI" ]; then
echo "Running locally will overwrite your globally installed npm."
read -p "Press any key to continue"
GITHUB_SHA=$(git rev-parse HEAD)
RUNNER_TEMP=$(mktemp -d)
IS_LOCAL="true"
fi

if [ -z "$GITHUB_SHA" ]; then
echo "GITHUB_SHA is required"
exit 1
fi

if [ -z "$RUNNER_TEMP" ]; then
echo "RUNNER_TEMP is required"
exit 1
fi

# Version the local source of npm with the current git sha and
# and pack and install it globally the same way we would if we
# were publishing it to the registry. The only difference is in the
# publish.js script which will only pack and not publish
ORIGINAL_NPM_VERSION=$(node . --version)
NPM_VERSION="$ORIGINAL_NPM_VERSION-$GITHUB_SHA.0"

# Only cleanup locally
if [ "$IS_LOCAL" == "true" ]; then
function cleanup {
echo "==================================="
echo "==================================="
echo "Cleaning up"
echo "==================================="
echo "==================================="
npm pkg set version=$ORIGINAL_NPM_VERSION
node scripts/resetdeps.js
if [ "$(npm --version)" == "$NPM_VERSION" ]; then
echo "==================================="
echo "==================================="
echo "Global npm version has changed to $NPM_VERSION"
echo "You should change it back to using"
echo "npm install npm@$ORIGINAL_NPM_VERSION -g"
echo "==================================="
echo "==================================="
fi
}
trap cleanup EXIT
fi

node . version $NPM_VERSION --ignore-scripts --no-git-tag-version
node scripts/publish.js --pack-destination=$RUNNER_TEMP --smoke-publish=true --is-local="$IS_LOCAL"
NPM_TARBALL="$RUNNER_TEMP/npm-$NPM_VERSION.tgz"
node . install --global $NPM_TARBALL

# Only run the tests if we are sure we have the right version
# otherwise the tests being run are pointless
NPM_GLOBAL_VERSION="$(npm --version)"
if [ "$NPM_GLOBAL_VERSION" != "$NPM_VERSION" ]; then
echo "global npm is not the correct version for smoke-publish"
echo "found: $NPM_GLOBAL_VERSION, expected: $NPM_VERSION"
exit 1
fi

# Install dev deps only for smoke tests so they can be run
node . install -w smoke-tests --ignore-scripts --no-audit --no-fund
# Run smoke tests with env vars so it uses the globally installed tarball we
# just packed/installed. The tacked on args at the end are only used for
# debugging locally when we want to pass args to the smoke-tests to limit the
# files being run or grep a test, etc. Also now set CI=true so we get more
# debug output in our tap tests
CI="true" SMOKE_PUBLISH_NPM="1" SMOKE_PUBLISH_TARBALL="$NPM_TARBALL" npm test \
-w smoke-tests \
--ignore-scripts \
-- -Rtap "$@"
23 changes: 2 additions & 21 deletions scripts/template-oss/ci-release-yml.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,8 @@
jobCreateCheck=(obj sha="${{ inputs.check-sha }}")
windowsCI=false
}}
- name: Pack
env:
SMOKE_PUBLISH_NPM: 1
run: |
NPM_VERSION="$({{ rootNpmPath }} --version)-$GITHUB_SHA.0"
{{ rootNpmPath }} version $NPM_VERSION --ignore-scripts
node scripts/publish.js --pack-destination=$RUNNER_TEMP
export SMOKE_PUBLISH_TARBALL="$RUNNER_TEMP/npm-$NPM_VERSION.tgz"
{{ rootNpmPath }} install --global $SMOKE_PUBLISH_TARBALL
{{ rootNpmPath }} install -w smoke-tests --ignore-scripts --no-audit --no-fund
# call installed npm instead of local source since we are testing
# the packed tarball that we just installed globally
NPM_GLOBAL_VERSION="$(npm --version)"
npm help
if [ "$NPM_GLOBAL_VERSION" == "$NPM_VERSION" ]; then
npm test -w smoke-tests --ignore-scripts
else
echo "global npm is not the correct version for smoke-publish"
echo "found: $NPM_GLOBAL_VERSION, expected: $NPM_VERSION"
exit 1
fi
- name: Smoke Publish
run: ./scripts/smoke-publish-test.sh
- name: Conclude Check
uses: LouisBrunner/[email protected]
if: always()
Expand Down
2 changes: 1 addition & 1 deletion scripts/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ git.dirty = () => npmGit.isClean({ cwd: CWD }).then(async r => {
return 'git clean'
}
await git('status', '--porcelain=v1', '-uno')
await git('diff')
await git('--no-pager', 'diff')
throw new Error('git dirty')
})

Expand Down
2 changes: 2 additions & 0 deletions smoke-tests/test/fixtures/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ const getCleanPaths = async () => {

module.exports = async (t, { testdir = {}, debug, mockRegistry = true, useProxy = false } = {}) => {
const debugLog = debug || CI ? (...a) => t.comment(...a) : () => {}
debugLog({ SMOKE_PUBLISH_NPM, SMOKE_PUBLISH_TARBALL, CI })

const cleanPaths = await getCleanPaths()

// setup fixtures
Expand Down
60 changes: 2 additions & 58 deletions smoke-tests/test/npm-replace-global.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,63 +36,6 @@ const setupNpmGlobal = async (t, opts) => {
}
}

t.test('pack and replace global self', async t => {
const {
npm,
npmLocalTarball,
npmPath,
getPaths,
paths: { globalBin, globalNodeModules },
} = await setupNpmGlobal(t, {
testdir: {
project: {
'package.json': { name: 'npm', version: '999.999.999' },
},
},
})

const tarball = await npmLocalTarball()
await npm('install', tarball, '--global')

t.equal(
await fs.realpath(join(globalBin, 'npm')),
setup.WINDOWS ? join(globalBin, 'npm') : join(globalNodeModules, 'npm/bin/npm-cli.js'),
'npm realpath is in the testdir'
)
t.equal(
await fs.realpath(join(globalBin, 'npx')),
setup.WINDOWS ? join(globalBin, 'npx') : join(globalNodeModules, 'npm/bin/npx-cli.js'),
'npx realpath is in the testdir'
)

const prePaths = await getPaths()
t.equal(prePaths.npmRoot, join(globalNodeModules, 'npm'), 'npm root is in the testdir')
t.equal(prePaths.pathNpm, join(globalBin, 'npm'), 'npm bin is in the testdir')
t.equal(prePaths.pathNpx, join(globalBin, 'npx'), 'npx bin is in the testdir')
t.not(prePaths.pathNpm, prePaths.globalNpm, 'npm bin is not the same as the global one')
t.not(prePaths.pathNpx, prePaths.globalNpx, 'npm bin is not the same as the global one')
t.ok(prePaths.nodeModulesContents.length > 1, 'node modules has npm contents')
t.ok(prePaths.nodeModulesContents.includes('node_modules'), 'npm has its node_modules')

t.strictSame(
prePaths.binContents,
['npm', 'npx'].flatMap(p => setup.WINDOWS ? [p, `${p}.cmd`, `${p}.ps1`] : p),
'bin has npm and npx'
)

await npmPath('pack')
await npmPath('install', 'npm-999.999.999.tgz', '--global')

const postPaths = await getPaths()
t.not(prePaths.npmRoot, postPaths.npmRoot, 'npm roots are different')
t.equal(postPaths.pathNpm, postPaths.globalNpm, 'npm bin is the same as the global one')
t.equal(postPaths.pathNpx, postPaths.globalNpx, 'npx bin is the same as the global one')
t.equal(postPaths.pathNpm, prePaths.globalNpm, 'after install npm bin is same as previous global')
t.equal(postPaths.pathNpx, prePaths.globalNpx, 'after install npx bin is same as previous global')
t.strictSame(postPaths.binContents, [], 'bin is empty')
t.strictSame(postPaths.nodeModulesContents, ['package.json'], 'contents is only package.json')
})

t.test('publish and replace global self', async t => {
let publishedPackument = null
const pkg = require('../../package.json')
Expand Down Expand Up @@ -121,11 +64,12 @@ t.test('publish and replace global self', async t => {
})
}

const npmInstall = async (useNpm) => {
const npmInstall = async (useNpm, opts) => {
await npmPackage({
manifest: { packuments: [publishedPackument] },
tarballs: { [version]: tarball },
times: 3,
...opts,
})
await fs.rm(cache, { recursive: true, force: true })
await useNpm('install', 'npm@latest', '--global')
Expand Down

0 comments on commit 1f4d73c

Please sign in to comment.