Skip to content

Commit

Permalink
chore: create smoke-publish-test.sh script (#7483)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukekarrys committed May 9, 2024
1 parent 1524cfd commit e9fdc9a
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 107 deletions.
42 changes: 3 additions & 39 deletions .github/workflows/ci-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -162,29 +162,12 @@ jobs:
- name: Linux
os: ubuntu-latest
shell: bash
- name: macOS
os: macos-latest
shell: bash
- name: macOS
os: macos-13
shell: bash
node-version:
- 18.17.0
- 18.x
- 20.5.0
- 20.x
- 22.x
exclude:
- platform: { name: macOS, os: macos-13, shell: bash }
node-version: 18.17.0
- platform: { name: macOS, os: macos-13, shell: bash }
node-version: 18.x
- platform: { name: macOS, os: macos-13, shell: bash }
node-version: 20.5.0
- platform: { name: macOS, os: macos-13, shell: bash }
node-version: 20.x
- platform: { name: macOS, os: macos-13, shell: bash }
node-version: 22.x
runs-on: ${{ matrix.platform.os }}
defaults:
run:
Expand Down Expand Up @@ -217,30 +200,11 @@ 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()
if: steps.create-check.outputs.check-id && always()
with:
token: ${{ secrets.GITHUB_TOKEN }}
conclusion: ${{ job.status }}
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
14 changes: 13 additions & 1 deletion mock-registry/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ const npa = require('npm-package-arg')
const Nock = require('nock')
const stringify = require('json-stringify-safe')

const logReq = (req, ...keys) => {
const obj = JSON.parse(stringify(req))
const res = {}
for (const [k, v] of Object.entries(obj)) {
if (!keys.includes(k)) {
res[k] = v
}
}
return stringify(res, null, 2)
}

class MockRegistry {
#tap
#nock
Expand Down Expand Up @@ -40,7 +51,8 @@ class MockRegistry {
// mocked with a 404, 500, etc.
// XXX: this is opt-in currently because it breaks some existing CLI
// tests. We should work towards making this the default for all tests.
t.fail(`Unmatched request: ${stringify(req, null, 2)}`)
t.comment(logReq(req, 'interceptors', 'socket', 'response', '_events'))
t.fail(`Unmatched request: ${req.method} ${req.path}`)
}
}

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
92 changes: 92 additions & 0 deletions scripts/smoke-publish-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env bash

set -eo pipefail

IS_LOCAL="false"
IS_CI="true"

if [ -z "$CI" ]; then
echo "Running locally will overwrite your globally installed npm."
GITHUB_SHA=$(git rev-parse HEAD)
RUNNER_TEMP=$(mktemp -d)
IS_LOCAL="true"
IS_CI="false"
fi

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

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

ORIGINAL_GLOBAL_NPM_VERSION=$(npm --version)
if [ ${#ORIGINAL_GLOBAL_NPM_VERSION} -gt 40 ]; then
echo "Error: Global npm version already contains a git SHA ${ORIGINAL_GLOBAL_NPM_VERSION}"
exit 1
fi

ORIGINAL_LOCAL_NPM_VERSION=$(node . --version)
if [ ${#ORIGINAL_LOCAL_NPM_VERSION} -gt 40 ]; then
echo "Error: Local npm version already contains a git SHA ${ORIGINAL_LOCAL_NPM_VERSION}"
exit 1
fi
NPM_VERSION="$ORIGINAL_LOCAL_NPM_VERSION-$GITHUB_SHA.0"

# Only cleanup locally
if [ "$IS_LOCAL" == "true" ]; then
function cleanup {
npm pkg set version=$ORIGINAL_LOCAL_NPM_VERSION
node scripts/resetdeps.js
if [ "$(git rev-parse HEAD)" != "$GITHUB_SHA" ]; then
echo "==================================="
echo "==================================="
echo "HEAD is on a different commit."
echo "==================================="
echo "==================================="
fi
if [ "$(npm --version)" == "$NPM_VERSION" ]; then
echo "==================================="
echo "==================================="
echo "Global npm version has changed to $NPM_VERSION"
echo "Run the following to change it back"
echo "npm install npm@$ORIGINAL_GLOBAL_NPM_VERSION -g"
echo "==================================="
echo "==================================="
fi
}
trap cleanup EXIT
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
node . version $NPM_VERSION --ignore-scripts --git-tag-version="$IS_CI"
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_TARBALL="$NPM_TARBALL" npm test \
-w smoke-tests \
--ignore-scripts \
-- "$@"
26 changes: 4 additions & 22 deletions scripts/template-oss/ci-release-yml.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,13 @@
jobCheckout=(obj ref="${{ inputs.ref }}")
jobCreateCheck=(obj sha="${{ inputs.check-sha }}")
windowsCI=false
macCI=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()
if: steps.create-check.outputs.check-id && always()
with:
token: $\{{ secrets.GITHUB_TOKEN }}
conclusion: $\{{ job.status }}
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
24 changes: 9 additions & 15 deletions smoke-tests/test/fixtures/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const MockRegistry = require('@npmcli/mock-registry')
const http = require('http')
const { createProxy } = require('proxy')

const { SMOKE_PUBLISH_NPM, SMOKE_PUBLISH_TARBALL, CI, PATH, Path } = process.env
const { SMOKE_PUBLISH_TARBALL, CI, PATH, Path } = process.env

const DEFAULT_REGISTRY = new URL('https://registry.npmjs.org/')
const MOCK_REGISTRY = new URL('http://smoke-test-registry.club/')
Expand Down 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_TARBALL, CI })

const cleanPaths = await getCleanPaths()

// setup fixtures
Expand Down Expand Up @@ -170,19 +172,11 @@ module.exports = async (t, { testdir = {}, debug, mockRegistry = true, useProxy
})

// In debug mode, stream stdout and stderr to console so we can debug hanging processes
if (debug) {
p.process.stdout.on('data', (c) => log('STDOUT: ' + c.toString().trim()))
p.process.stderr.on('data', (c) => log('STDERR: ' + c.toString().trim()))
}
p.process.stdout.on('data', (c) => log(c.toString().trim()))
p.process.stderr.on('data', (c) => log(c.toString().trim()))

const { stdout, stderr } = await p
// If not in debug mode, print full stderr and stdout contents separately
if (!debug) {
log(stderr)
log('-'.repeat(40))
log(stdout)
log('='.repeat(40))
}
log('='.repeat(40))

return { stderr, stdout }
}
Expand Down Expand Up @@ -225,7 +219,7 @@ module.exports = async (t, { testdir = {}, debug, mockRegistry = true, useProxy

const npmLocal = async (...args) => {
const [{ force = false }] = getOpts(...args)
if (SMOKE_PUBLISH_NPM && !force) {
if (SMOKE_PUBLISH_TARBALL && !force) {
throw new Error('npmLocal cannot be called during smoke-publish')
}
return baseNpm({
Expand Down Expand Up @@ -257,7 +251,7 @@ module.exports = async (t, { testdir = {}, debug, mockRegistry = true, useProxy
return {
npmPath,
npmLocal,
npm: SMOKE_PUBLISH_NPM ? npmPath : npm,
npm: SMOKE_PUBLISH_TARBALL ? npmPath : npm,
spawn: baseSpawn,
readFile,
getPath,
Expand All @@ -275,6 +269,6 @@ module.exports.testdir = testdirHelper
module.exports.getNpmRoot = getNpmRoot
module.exports.CLI_ROOT = CLI_ROOT
module.exports.WINDOWS = WINDOWS
module.exports.SMOKE_PUBLISH = !!SMOKE_PUBLISH_NPM
module.exports.SMOKE_PUBLISH = !!SMOKE_PUBLISH_TARBALL
module.exports.SMOKE_PUBLISH_TARBALL = SMOKE_PUBLISH_TARBALL
module.exports.MOCK_REGISTRY = MOCK_REGISTRY

0 comments on commit e9fdc9a

Please sign in to comment.