Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: create smoke-publish-test.sh script #7483

Merged
merged 10 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/checks-action@v1.6.0
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/checks-action@v1.6.0
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
Loading
Loading