diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index d2b3c5291ce1e..60e4e4144e207 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -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/checks-action@v1.6.0 if: always() diff --git a/docs/bin/build.js b/docs/bin/build.js index c25fd76ed7e7f..602596bc2d494 100644 --- a/docs/bin/build.js +++ b/docs/bin/build.js @@ -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') diff --git a/mock-registry/lib/index.js b/mock-registry/lib/index.js index 03787ea7cd4e6..d74abb779cba0 100644 --- a/mock-registry/lib/index.js +++ b/mock-registry/lib/index.js @@ -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) @@ -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 } diff --git a/scripts/publish.js b/scripts/publish.js index f27b07fb8e658..a28bfd849120c 100644 --- a/scripts/publish.js +++ b/scripts/publish.js @@ -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( @@ -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' }) @@ -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}` ) diff --git a/scripts/smoke-publish-test.sh b/scripts/smoke-publish-test.sh new file mode 100755 index 0000000000000..6eea36e7d2502 --- /dev/null +++ b/scripts/smoke-publish-test.sh @@ -0,0 +1,84 @@ + #!/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 "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 [ "$(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 --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 "$@" diff --git a/scripts/template-oss/ci-release-yml.hbs b/scripts/template-oss/ci-release-yml.hbs index c3baff23c906b..9078f02c3429f 100644 --- a/scripts/template-oss/ci-release-yml.hbs +++ b/scripts/template-oss/ci-release-yml.hbs @@ -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/checks-action@v1.6.0 if: always() diff --git a/scripts/util.js b/scripts/util.js index c842bd26004f3..9c9973914b1f3 100644 --- a/scripts/util.js +++ b/scripts/util.js @@ -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') }) diff --git a/smoke-tests/test/fixtures/setup.js b/smoke-tests/test/fixtures/setup.js index 91e0ddec2415f..7f35ccb643e6d 100644 --- a/smoke-tests/test/fixtures/setup.js +++ b/smoke-tests/test/fixtures/setup.js @@ -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