diff --git a/.eslintrc.local.json b/.eslintrc.local.json index beb4581f41d82..e953021f7324c 100644 --- a/.eslintrc.local.json +++ b/.eslintrc.local.json @@ -1,16 +1,10 @@ { - "ignorePatterns": [ - "docs/", - "workspaces/*" - ], "rules": { "no-shadow": "off", "no-console": "error" }, "overrides": [{ "files": [ - "scripts/**", - "bin/**", "test/**" ], "rules": { diff --git a/.github/workflows/ci-libnpmaccess.yml b/.github/workflows/ci-libnpmaccess.yml index 8455f2b2a1c9f..80c6c2c376beb 100644 --- a/.github/workflows/ci-libnpmaccess.yml +++ b/.github/workflows/ci-libnpmaccess.yml @@ -118,11 +118,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci-libnpmdiff.yml b/.github/workflows/ci-libnpmdiff.yml index 10bd7c90f7fe1..a12b2bbe4219d 100644 --- a/.github/workflows/ci-libnpmdiff.yml +++ b/.github/workflows/ci-libnpmdiff.yml @@ -118,11 +118,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci-libnpmexec.yml b/.github/workflows/ci-libnpmexec.yml index 037c344c2aafb..0ecdbe591b182 100644 --- a/.github/workflows/ci-libnpmexec.yml +++ b/.github/workflows/ci-libnpmexec.yml @@ -118,11 +118,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci-libnpmfund.yml b/.github/workflows/ci-libnpmfund.yml index 78b79f530b105..8c80003297132 100644 --- a/.github/workflows/ci-libnpmfund.yml +++ b/.github/workflows/ci-libnpmfund.yml @@ -118,11 +118,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci-libnpmhook.yml b/.github/workflows/ci-libnpmhook.yml index 997a2401b585d..635d04b932e3d 100644 --- a/.github/workflows/ci-libnpmhook.yml +++ b/.github/workflows/ci-libnpmhook.yml @@ -118,11 +118,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci-libnpmorg.yml b/.github/workflows/ci-libnpmorg.yml index aac1cbc9bdaf7..2b195927cc4ea 100644 --- a/.github/workflows/ci-libnpmorg.yml +++ b/.github/workflows/ci-libnpmorg.yml @@ -118,11 +118,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci-libnpmpack.yml b/.github/workflows/ci-libnpmpack.yml index 533bcd8134b9a..09a768ee94fde 100644 --- a/.github/workflows/ci-libnpmpack.yml +++ b/.github/workflows/ci-libnpmpack.yml @@ -118,11 +118,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci-libnpmpublish.yml b/.github/workflows/ci-libnpmpublish.yml index 7ecab7a4e5777..71b5342c507f9 100644 --- a/.github/workflows/ci-libnpmpublish.yml +++ b/.github/workflows/ci-libnpmpublish.yml @@ -118,11 +118,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci-libnpmsearch.yml b/.github/workflows/ci-libnpmsearch.yml index 42e8f4f8374ae..9fdf040bcb4a5 100644 --- a/.github/workflows/ci-libnpmsearch.yml +++ b/.github/workflows/ci-libnpmsearch.yml @@ -118,11 +118,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci-libnpmteam.yml b/.github/workflows/ci-libnpmteam.yml index ec00a20c2b1d8..937f5967ef51d 100644 --- a/.github/workflows/ci-libnpmteam.yml +++ b/.github/workflows/ci-libnpmteam.yml @@ -118,11 +118,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci-libnpmversion.yml b/.github/workflows/ci-libnpmversion.yml index 567e85ebadb7f..8d2eabd99798b 100644 --- a/.github/workflows/ci-libnpmversion.yml +++ b/.github/workflows/ci-libnpmversion.yml @@ -118,11 +118,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci-npmcli-arborist.yml b/.github/workflows/ci-npmcli-arborist.yml index 86a4be73b638e..495f7eebb3e2a 100644 --- a/.github/workflows/ci-npmcli-arborist.yml +++ b/.github/workflows/ci-npmcli-arborist.yml @@ -118,11 +118,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci-npmcli-docs.yml b/.github/workflows/ci-npmcli-docs.yml index 9906b3bb791e6..ea4d5366d6a7a 100644 --- a/.github/workflows/ci-npmcli-docs.yml +++ b/.github/workflows/ci-npmcli-docs.yml @@ -111,11 +111,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index 0958704ce06fb..aaeba4b10a949 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -177,11 +177,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test @@ -284,18 +279,12 @@ jobs: run: | NPM_VERSION="$(node . --version)-$GITHUB_SHA.0" node . version $NPM_VERSION --ignore-scripts - node . run resetdeps - git clean -fd - node . ls --omit=dev >/dev/null - node . prune --omit=dev --no-save --no-audit --no-fund - node scripts/git-dirty.js - node . pack --pack-destination=$RUNNER_TEMP - node . install -g $RUNNER_TEMP/npm-$NPM_VERSION.tgz + node scripts/publish.js --pack-destination=$RUNNER_TEMP + node . install --global $RUNNER_TEMP/npm-$NPM_VERSION.tgz node . install -w smoke-tests --ignore-scripts --no-audit --no-fund - rm -rf {lib,bin,index.js} - # this one should be npm since we explicitly installed our packed - # tarball globally and the next test will make sure our the new - # globally installed version contains the git sha + node scripts/remove-files.js + # call installed npm instead of local source since we are testing + # the packed tarball that we just installed globally SMOKE_PUBLISH_NPM=1 npm test -w smoke-tests --ignore-scripts - name: Conclude Check uses: LouisBrunner/checks-action@v1.3.1 diff --git a/.github/workflows/ci-smoke-tests.yml b/.github/workflows/ci-smoke-tests.yml index 549785c4e024c..e69a3224cf1ce 100644 --- a/.github/workflows/ci-smoke-tests.yml +++ b/.github/workflows/ci-smoke-tests.yml @@ -118,11 +118,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57ea1212be5e1..0cc934215cba8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -122,11 +122,6 @@ jobs: cache: npm - name: Reset Deps run: node . run resetdeps - - name: Link - if: matrix - run: node . link -f --ignore-scripts - - name: Rebuild cmark-gfm - run: node . rebuild cmark-gfm - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - name: Test diff --git a/.github/workflows/create-cli-deps-pr.yml b/.github/workflows/create-cli-deps-pr.yml deleted file mode 100644 index e5bd2ccdd1315..0000000000000 --- a/.github/workflows/create-cli-deps-pr.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: "Create CLI Deps PR" - -on: - workflow_dispatch: - inputs: - npmVersion: - description: "6.x.x or latest" - required: true - default: 'latest' - dryRun: - description: "Do a dry run?" - default: '' - -jobs: - create-pull-request: - runs-on: ubuntu-latest - steps: - - name: Checkout npm/node - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: main - repository: npm/node - token: ${{ secrets.NODE_PULL_REQUEST_TOKEN }} - - name: Setup git user - run: | - git config --global user.email "npm CLI robot" - git config --global user.name "npm-cli+bot@github.com" - - name: Sync upstream changes - uses: aormsby/Fork-Sync-With-Upstream-action@v3.2 - with: - target_sync_branch: main - target_repo_token: ${{ secrets.NODE_PULL_REQUEST_TOKEN }} - upstream_sync_branch: main - upstream_sync_repo: nodejs/node - upstream_pull_args: --ff-only - - name: Run dependency updates and create PR - env: - GITHUB_TOKEN: ${{ secrets.NODE_PULL_REQUEST_TOKEN }} - run: | - base_dir="$( pwd )"/ - dry_run="${{ github.event.inputs.dryRun }}" - npm_version="${{ github.event.inputs.npmVersion }}" - npm_tag="" - base_branch="" - - if [ "$npm_version" == "latest" ]; then - npm_tag=`npm view npm@latest version` - base_branch="main" - else - npm_tag="$npm_version" - base_branch="v14.x-staging" - fi - - npm_vtag="v$npm_tag" - npm_branch="npm-$npm_tag" - message="deps: upgrade npm to $npm_tag" - - git checkout -b "$npm_branch" - - echo "Cloning CLI repo" - gh repo clone npm/cli - - echo "Prepping CLI repo for release" - cd cli - git checkout "$npm_vtag" - make release - - echo "Removing old npm" - deps_dir="$base_dir"deps/ - cd "$deps_dir" - rm -rf npm/ - - echo "Copying new npm" - tar zxf "$base_dir"cli/release/"$npm_branch".tgz - - echo "Removing CLI workspace" - cd "$base_dir" - rm -rf cli - - git add -A deps/npm - git commit -m "$message" - git rebase --whitespace=fix main - - if [[ "$dry_run" == "true" ]]; then - git status - git show --summary - echo $message - echo $npm_branch - echo $base_branch - echo $npm_vtag - else - git push origin "$npm_branch" - gh release view "$npm_vtag" -R npm/cli --json body -q ".body" | \ - gh pr create -R nodejs/node -B "$base_branch" -H "npm:$npm_branch" -t "$message" -F - - fi diff --git a/.github/workflows/create-node-pr.yml b/.github/workflows/create-node-pr.yml new file mode 100644 index 0000000000000..f7bf18d8095b7 --- /dev/null +++ b/.github/workflows/create-node-pr.yml @@ -0,0 +1,49 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +name: "Create Node PR" + +on: + workflow_dispatch: + inputs: + spec: + description: "The npm spec to create the PR from" + required: true + default: 'latest' + dryRun: + description: "Setting this to anything will run all the steps except opening the PR" + +jobs: + create-pull-request: + name: Create Node PR + if: github.repository_owner == 'npm' + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Git User + run: | + git config --global user.email "npm-cli+bot@github.com" + git config --global user.name "npm CLI robot" + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18.x + cache: npm + - name: Reset Deps + run: node . run resetdeps + - name: Checkout Node + uses: actions/checkout@v3 + with: + token: ${{ secrets.NODE_PULL_REQUEST_TOKEN }} + repository: nodejs/node + fetch-depth: 0 + path: node + - name: Create Node Pull Request + env: + GITHUB_TOKEN: ${{ secrets.NODE_PULL_REQUEST_TOKEN }} + run: | + DRY_RUN=$([ -z "${{ inputs.dryRun }}" ] && echo "" || echo "--dry-run") + node scripts/create-node-pr.js "${{ inputs.spec }}" "$DRY_RUN" diff --git a/.gitignore b/.gitignore index 370f45df41cd5..c857a68a63508 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,6 @@ !/AUTHORS !/bin/ !/CHANGELOG* -!/changelogs/ !/CODE_OF_CONDUCT.md !/configure !/CONTRIBUTING.md @@ -27,8 +26,6 @@ !/index.js !/lib/ !/LICENSE* -!/make.bat -!/Makefile !/map.js !/node_modules/ /node_modules/.bin/ diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index 97808f4a458be..c6fed355f3de9 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -119,6 +119,7 @@ graph LR; npm-->npmcli-config["@npmcli/config"]; npm-->npmcli-docs["@npmcli/docs"]; npm-->npmcli-eslint-config["@npmcli/eslint-config"]; + npm-->npmcli-fs["@npmcli/fs"]; npm-->npmcli-git["@npmcli/git"]; npm-->npmcli-map-workspaces["@npmcli/map-workspaces"]; npm-->npmcli-package-json["@npmcli/package-json"]; @@ -735,6 +736,7 @@ graph LR; npm-->npmcli-config["@npmcli/config"]; npm-->npmcli-docs["@npmcli/docs"]; npm-->npmcli-eslint-config["@npmcli/eslint-config"]; + npm-->npmcli-fs["@npmcli/fs"]; npm-->npmcli-git["@npmcli/git"]; npm-->npmcli-map-workspaces["@npmcli/map-workspaces"]; npm-->npmcli-package-json["@npmcli/package-json"]; @@ -1103,4 +1105,4 @@ packages higher up the chain. - @npmcli/git, make-fetch-happen, @npmcli/config, init-package-json - @npmcli/installed-package-contents, @npmcli/map-workspaces, cacache, npm-pick-manifest, @npmcli/run-script, read-package-json, readdir-scoped-modules, promzard - @npmcli/docs, npm-bundled, read-package-json-fast, @npmcli/fs, unique-filename, npm-install-checks, npm-package-arg, npm-packlist, normalize-package-data, @npmcli/package-json, bin-links, nopt, npmlog, parse-conflict-json, dezalgo, read - - ignore-walk, @npmcli/eslint-config, @npmcli/template-oss, npm-normalize-package-bin, @npmcli/name-from-folder, json-parse-even-better-errors, semver, @npmcli/move-file, fs-minipass, ssri, unique-slug, @npmcli/promise-spawn, hosted-git-info, proc-log, validate-npm-package-name, @npmcli/node-gyp, minipass-fetch, @npmcli/query, cmd-shim, read-cmd-shim, write-file-atomic, abbrev, are-we-there-yet, gauge, wrappy, treeverse, minify-registry-metadata, @npmcli/disparity-colors, @npmcli/ci-detect, mute-stream, ini, npm-audit-report, npm-user-validate \ No newline at end of file + - ignore-walk, @npmcli/eslint-config, @npmcli/template-oss, npm-normalize-package-bin, @npmcli/name-from-folder, json-parse-even-better-errors, semver, @npmcli/move-file, fs-minipass, ssri, unique-slug, @npmcli/promise-spawn, hosted-git-info, proc-log, validate-npm-package-name, @npmcli/node-gyp, minipass-fetch, @npmcli/query, cmd-shim, read-cmd-shim, write-file-atomic, abbrev, are-we-there-yet, gauge, wrappy, treeverse, minify-registry-metadata, @npmcli/disparity-colors, @npmcli/ci-detect, mute-stream, ini, npm-audit-report, npm-user-validate diff --git a/Makefile b/Makefile deleted file mode 100644 index 997a095067c28..0000000000000 --- a/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# vim: set softtabstop=2 shiftwidth=2: -SHELL = bash - -PUBLISHTAG = $(shell node scripts/publish-tag.js) - -deps: - node bin/npm-cli.js run resetdeps - -lint-all: deps - node bin/npm-cli.js run lint-all - -test-all: deps - node bin/npm-cli.js run test-all - -ls-ok: - node bin/npm-cli.js ls --omit=dev >/dev/null - -gitclean: - git clean -fd - -uninstall: - node bin/npm-cli.js rm -g -f npm - -link: uninstall - node bin/npm-cli.js link -f --ignore-scripts - -prune: deps - node bin/npm-cli.js prune --omit=dev --no-save --no-audit --no-fund - node scripts/git-dirty.js - -publish: gitclean ls-ok link lint-all test-all prune - node bin/npm-cli.js publish --tag=$(PUBLISHTAG) - -release: gitclean ls-ok prune - @bash scripts/release.sh - -.PHONY: link gitclean uninstall lint-all test-all release ls-ok deps prune diff --git a/bin/npx-cli.js b/bin/npx-cli.js index cb05e1cb706c6..75090aed41f1f 100755 --- a/bin/npx-cli.js +++ b/bin/npx-cli.js @@ -98,6 +98,7 @@ for (i = 3; i < process.argv.length; i++) { } if (removed.has(key)) { + // eslint-disable-next-line no-console console.error(`npx: the --${key} argument has been removed.`) sawRemovedFlags = true process.argv.splice(i, 1) @@ -122,6 +123,7 @@ for (i = 3; i < process.argv.length; i++) { } if (sawRemovedFlags) { + // eslint-disable-next-line no-console console.error('See `npm help exec` for more information') } diff --git a/docs/package.json b/docs/package.json index 18220d139402f..d92f558a3fcad 100644 --- a/docs/package.json +++ b/docs/package.json @@ -11,8 +11,11 @@ "lintfix": "node .. run lint -- --fix", "snap": "tap", "test": "tap", + "pretest": "node .. run rebuild-cmark", "posttest": "node .. run lint", - "build": "node bin/build.js" + "build": "node bin/build.js", + "prebuild": "node .. run rebuild-cmark", + "rebuild-cmark": "node ../scripts/rebuild.js cmark-gfm" }, "repository": { "type": "git", diff --git a/package-lock.json b/package-lock.json index 0f44f87fbd72a..16a7fe0679ddf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -164,6 +164,7 @@ "devDependencies": { "@npmcli/docs": "^1.0.0", "@npmcli/eslint-config": "^4.0.0", + "@npmcli/fs": "^3.0.0", "@npmcli/git": "^4.0.1", "@npmcli/promise-spawn": "^5.0.0", "@npmcli/template-oss": "4.6.2", @@ -5688,8 +5689,9 @@ }, "node_modules/glob": { "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", "inBundle": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", diff --git a/package.json b/package.json index c86f062aa7faf..de24066ed02c4 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,9 @@ "bin/", "lib/", "index.js", - "docs/content/**/*.md", - "docs/output/**/*.html", - "man" + "docs/content/", + "docs/output/", + "man/" ], "keywords": [ "install", @@ -201,6 +201,7 @@ "devDependencies": { "@npmcli/docs": "^1.0.0", "@npmcli/eslint-config": "^4.0.0", + "@npmcli/fs": "^3.0.0", "@npmcli/git": "^4.0.1", "@npmcli/promise-spawn": "^5.0.0", "@npmcli/template-oss": "4.6.2", @@ -213,8 +214,8 @@ "scripts": { "dependencies": "node scripts/bundle-and-gitignore-deps.js && node scripts/dependency-graph.js", "dumpconf": "env | grep npm | sort | uniq", - "authors": "bash scripts/update-authors.sh", "licenses": "licensee --production --errors-only", + "pretest": "node scripts/rebuild.js cmark-gfm", "test": "tap", "test-all": "node . run test -ws -iwr --if-present", "snap": "tap", @@ -227,8 +228,8 @@ "lintfix": "node . run lint -- --fix", "lint-all": "node . run lint -ws -iwr --if-present", "prelint": "rimraf test/npm_cache*", - "resetdeps": "bash scripts/resetdeps.sh", - "rp-pull-request": "node . run authors", + "resetdeps": "node scripts/resetdeps.js", + "rp-pull-request": "node scripts/update-authors.js", "postlint": "template-oss-check", "template-oss-apply": "template-oss-apply --force" }, diff --git a/scripts/bundle-and-gitignore-deps.js b/scripts/bundle-and-gitignore-deps.js index bbe08a7836147..e19721dade9b4 100644 --- a/scripts/bundle-and-gitignore-deps.js +++ b/scripts/bundle-and-gitignore-deps.js @@ -1,14 +1,10 @@ -#!/usr/bin/env node - const Arborist = require('@npmcli/arborist') const packlist = require('npm-packlist') -const git = require('@npmcli/git') -const { resolve, join, relative } = require('path') +const { join, relative } = require('path') const localeCompare = require('@isaacs/string-locale-compare')('en') -const fs = require('fs/promises') const PackageJson = require('@npmcli/package-json') +const { run, CWD, git, fs } = require('./util') -const RM_FLAG = '--remove-ignored-files' const ALWAYS_IGNORE = ` .bin/ .cache/ @@ -42,24 +38,19 @@ __pycache__ .gitkeep ` -const lsIgnored = async ({ cwd, dir }) => { - const { stdout } = await git.spawn([ +const lsIgnored = async (dir, { removeIgnoredFiles }) => { + const files = await git( 'ls-files', '--cached', '--ignored', `--exclude-standard`, dir, - ], { cwd }) - - const files = stdout - .trim() - .split('\n') - .map(l => l.trim()) - .filter(Boolean) + { lines: true } + ) - if (process.argv.includes(RM_FLAG)) { + if (removeIgnoredFiles) { for (const file of files) { - await git.spawn(['rm', file], { cwd }) + await git('rm', file) } return [] } @@ -184,8 +175,8 @@ const getAllowedPaths = (files) => { return [...allowPaths] } -const setBundleDeps = async (dir) => { - const pkg = await PackageJson.load(dir) +const setBundleDeps = async () => { + const pkg = await PackageJson.load(CWD) pkg.update({ bundleDependencies: Object.keys(pkg.content.dependencies).sort(localeCompare), @@ -208,13 +199,10 @@ deps source. We have to do this since everything is ignored by default, and git will not allow a nested path if its parent has not also been allowed. BUT! We also have to ignore other things in those directories. */ -const main = async (path) => { - await setBundleDeps(path) +const main = async ({ removeIgnoredFiles }) => { + await setBundleDeps() - const nodeModules = resolve(path, 'node_modules') - const gitIgnore = join(nodeModules, '.gitignore') - - const arb = new Arborist({ path }) + const arb = new Arborist({ path: CWD }) const files = await arb.loadActual().then(packlist) const ignoreFile = [ @@ -228,28 +216,24 @@ const main = async (path) => { ...ALWAYS_IGNORE.trim().split('\n'), ] - await fs.writeFile(gitIgnore, ignoreFile.join('\n') + '\n') + const NODE_MODULES = join(CWD, 'node_modules') + const res = await fs.writeFile(join(NODE_MODULES, '.gitignore'), ignoreFile.join('\n')) // After we write the file we have to check if any of the paths already checked in // inside node_modules are now going to be ignored. If we find any then fail with // a list of paths that will need to have `git rm` run on them. - const trackedAndIgnored = await lsIgnored({ cwd: path, dir: nodeModules }) + const trackedAndIgnored = await lsIgnored(NODE_MODULES, { removeIgnoredFiles }) if (trackedAndIgnored.length) { const message = [ 'The following files are checked in to git but will now be ignored.', - `Rerun this script with \`${RM_FLAG}\` to remove them.`, - ...trackedAndIgnored.map(p => relative(nodeModules, p)), + `Rerun this script with \`--remove-ignored-files\` to remove them.`, + ...trackedAndIgnored.map(p => relative(NODE_MODULES, p)), ].join('\n') throw new Error(message) } - return `Wrote to ${relative(process.cwd(), gitIgnore)}` + return res } -main(resolve(__dirname, '..')) - .then((res) => console.log(res)) - .catch((err) => { - console.error(err) - return process.exit(1) - }) +run(main) diff --git a/scripts/create-node-pr.js b/scripts/create-node-pr.js new file mode 100644 index 0000000000000..26d4c07a793e6 --- /dev/null +++ b/scripts/create-node-pr.js @@ -0,0 +1,124 @@ +const { join } = require('path') +const fsp = require('fs/promises') +const hgi = require('hosted-git-info') +const semver = require('semver') +const pacote = require('pacote') +const log = require('proc-log') +const tar = require('tar') +const { cp, withTempDir } = require('@npmcli/fs') +const { CWD, run, spawn, git, fs, gh } = require('./util.js') + +// this script expects node to already be cloned to a directory at the cli root named "node" +const NODE_DIR = join(CWD, 'node') +const gitNode = spawn.create('git', { cwd: NODE_DIR }) + +const createNodeTarball = async ({ mani, registryOnly, tag, dir: extractDir }) => { + const tarball = join(extractDir, 'npm-node.tgz') + await pacote.tarball.file(mani._from, tarball, { resolved: mani._resolved }) + + if (registryOnly) { + // a future goal is to only need files from the published tarball for + // inclusion in node. in that case, we'd be able to remove everything after + // this line since we have already fetched the tarball + return tarball + } + + // extract tarball to current dir and delete original tarball + await tar.x({ strip: 1, file: tarball, cwd: extractDir }) + await fs.rimraf(tarball) + + // checkout the tag since we need to get files from source. + await git.dirty() + tag && await git('checkout', tag) + for (const path of ['.npmrc', 'tap-snapshots/', 'test/']) { + await cp(join(CWD, path), join(extractDir, path), { recursive: true }) + } + + await tar.c({ + ...pacote.DirFetcher.tarCreateOptions(mani), + cwd: extractDir, + file: tarball, + }, ['.']) + + return tarball +} + +const main = async (spec, opts) => withTempDir(CWD, async (tmpDir) => { + const { dryRun, registryOnly, skipCheckout } = opts + + const mani = await pacote.manifest(`npm@${spec}`, { preferOnline: true }) + + const head = { + tag: `v${mani.version}`, + branch: `npm-v${mani.version}`, + host: hgi.fromUrl('npm/node'), + message: `deps: upgrade npm to ${mani.version}`, + } + log.silly(head) + + const tarball = await createNodeTarball({ + mani, + dir: tmpDir, + registryOnly, + // the only reason this is optional is for testing when updating this script. + // if we checkout an older tag, it won't have the updates we are testing. + tag: skipCheckout ? null : head.tag, + }) + + await fsp.access(NODE_DIR, fsp.constants.F_OK).catch(() => { + throw new Error(`node repo must be checked out to \`${NODE_DIR}\` to continue`) + }) + + const base = { + // we used to send PRs sometimes for old versions to the 14.x staging + // branch. this might not be needed anymore, but this is how we + // would do it, if we needed to send a PR for backport fixes + branch: semver.major(mani.version) <= 8 ? '14.x-staging' : 'main', + remote: 'origin', + host: hgi.fromUrl(await gitNode('remote', 'get-url', 'origin', { out: true })), + } + log.silly(base) + + await gh('repo', 'fork', base.host.path(), '--org', head.host.user, { quiet: true, ok: true }) + await gitNode('fetch', base.remote) + await gitNode('checkout', base.branch) + await gitNode('reset', '--hard', `${base.remote}/${base.branch}`) + await gitNode('branch', '-D', head.branch, { ok: true }) + await gitNode('checkout', '-b', head.branch) + + const npmPath = join('deps', 'npm') + const npmDir = join(NODE_DIR, npmPath) + await fs.clean(npmDir) + await tar.x({ strip: 1, file: tarball, cwd: npmDir }) + + await gitNode('add', '-A', npmPath) + await gitNode('commit', '-m', head.message) + await gitNode('rebase', '--whitespace', 'fix', base.branch) + + await gitNode('remote', 'add', head.host.user, head.host.ssh(), { ok: true }) + await gitNode('push', head.host.user, head.branch, '--force') + + const notes = await gh.json('release', 'view', head.tag, 'body') + log.silly('body', notes) + + const prArgs = [ + 'pr', 'create', + '-R', base.host.path(), + '-B', base.branch, + '-H', `${head.host.user}:${head.branch}`, + '-t', head.message, + ] + + if (dryRun) { + log.info(`gh ${prArgs.join(' ')}`) + const url = new URL(base.host.browse()) + const compare = `${base.branch}...${head.host.user}:${head.host.project}:${head.branch}` + url.pathname += `/compare/${compare}` + url.searchParams.set('expand', '1') + return url.toString() + } + + return gh(...prArgs, '-F', '-', { cwd: NODE_DIR, input: notes, out: true }) +}) + +run(({ argv, ...opts }) => main(argv.remain[0], opts)) diff --git a/scripts/dependency-graph.js b/scripts/dependency-graph.js index 41beb302dffd4..e292ce448fe74 100644 --- a/scripts/dependency-graph.js +++ b/scripts/dependency-graph.js @@ -1,22 +1,14 @@ -#!/usr/bin/env node - -'use strict' - -// Generates our dependency graph documents in DEPENDENCIES.md. - const Arborist = require('@npmcli/arborist') -const mapWorkspaces = require('@npmcli/map-workspaces') -const fs = require('fs') +const { readFileSync } = require('fs') +const { join } = require('path') const log = require('proc-log') +const { run, CWD, pkg, fs } = require('./util.js') -if (process.argv.includes('--debug')) { - process.on('log', console.error) -} +// Generates our dependency graph documents in DEPENDENCIES.md. // To re-create npm-cli-repos.txt run: -/* eslint-disable-next-line max-len */ -// npx --package=@npmcli/stafftools@latest gh repos --json | json -a name | sort > scripts/npm-cli-repos.txt -const repos = fs.readFileSync('./scripts/npm-cli-repos.txt', 'utf8').trim().split('\n') +// npx -p @npmcli/stafftools gh repos --json | json -a name | sort > scripts/npm-cli-repos.txt +const repos = readFileSync(join(CWD, 'scripts', 'npm-cli-repos.txt'), 'utf-8').trim().split('\n') // these have a different package name than the repo name, and are ours. const aliases = { @@ -85,18 +77,14 @@ function stripName (name) { const main = async function () { // add all of the cli's public workspaces as package names - const workspaces = await mapWorkspaces({ pkg: require('../package.json') }) - for (const [key, value] of workspaces.entries()) { - if (!require(value + '/package.json').private) { - repos.push(key) + for (const { name, pkg: ws } of await pkg.mapWorkspaces()) { + if (!ws.private) { + repos.push(name) } } - const arborist = new Arborist({ - prefix: process.cwd(), - path: process.cwd(), - }) - const tree = await arborist.loadVirtual({ path: process.cwd(), name: 'npm' }) + const arborist = new Arborist({ prefix: CWD, path: CWD }) + const tree = await arborist.loadVirtual({ path: CWD, name: 'npm' }) tree.name = 'npm' const [annotationsOurs, heirarchyOurs] = walk(tree, true) @@ -125,8 +113,8 @@ const main = async function () { '', ` - ${heirarchyOurs.reverse().join('\n - ')}`, ] - fs.writeFileSync('DEPENDENCIES.md', out.join('\n')) - console.log('wrote to DEPENDENCIES.md') + + return fs.writeFile(join(CWD, 'DEPENDENCIES.md'), out.join('\n')) } const walk = function (tree, onlyOurs) { @@ -179,6 +167,7 @@ const walk = function (tree, onlyOurs) { return [annotations, heirarchy] } + const iterate = function (node, dependedBy, annotations, onlyOurs) { if (!dependedBy[node.packageName]) { dependedBy[node.packageName] = new Set() @@ -198,9 +187,4 @@ const iterate = function (node, dependedBy, annotations, onlyOurs) { } } -main().then(() => { - return process.exit(0) -}).catch(err => { - console.error(err) - return process.exit(1) -}) +run(main) diff --git a/scripts/git-dirty.js b/scripts/git-dirty.js index 5730ed9006681..1c864856914ab 100644 --- a/scripts/git-dirty.js +++ b/scripts/git-dirty.js @@ -1,17 +1,3 @@ -#!/usr/bin/env node -const { spawnSync } = require('child_process') -const changes = spawnSync('git', ['status', '--porcelain', '-uall']) -const stdout = changes.stdout.toString('utf8') -const stderr = changes.stderr.toString('utf8') -const { status, signal } = changes -console.log(stdout) -console.error(stderr) -if (status || signal) { - console.error({ status, signal }) - process.exitCode = status || 1 -} -if (stdout.trim() !== '') { - throw new Error('git dirty') -} else { - console.log('git clean') -} +const { run, git } = require('./util.js') + +run(git.dirty) diff --git a/scripts/publish-tag.js b/scripts/publish-tag.js deleted file mode 100644 index fb8a48233b9c0..0000000000000 --- a/scripts/publish-tag.js +++ /dev/null @@ -1,3 +0,0 @@ -var semver = require('semver') -var version = semver.parse(require('../package.json').version) -console.log('next-%s', version.major) diff --git a/scripts/publish.js b/scripts/publish.js new file mode 100644 index 0000000000000..536c5d764371f --- /dev/null +++ b/scripts/publish.js @@ -0,0 +1,110 @@ +const semver = require('semver') +const log = require('proc-log') +const pacote = require('pacote') +const { run, git, npm, pkg, spawn } = require('./util.js') + +const resetdeps = () => npm('run', 'resetdeps') + +const op = () => spawn('op', 'item', 'get', 'npm', '--otp', { out: true, ok: true }) + +const TAGS = { + // cli is always published to next-MAJOR + root: (v) => ({ tag: `next-${semver.major(v)}` }), + // workspaces are always published to latest, except prereleases + workspace: () => ({ tag: 'latest', preTag: 'prerelease' }), +} + +const needsPublish = async ({ pkg: { private, name, version }, force, tags: getTags }) => { + if (private) { + return + } + + const tags = getTags(version) + const tag = semver.parse(version).prerelease.length && tags.preTag + ? tags.preTag + : tags.tag + + if (force) { + return tag + } + + const mani = await pacote.manifest(`${name}@${tag}`, { preferOnline: true }) + if (version !== mani.version) { + return tag + } +} + +const getPublishes = async ({ force }) => { + const publish = [] + + for (const { name, pkg: ws } of await pkg.mapWorkspaces()) { + publish.push({ + workspace: name, + tag: await needsPublish({ + force, + pkg: ws, + tags: TAGS.workspace, + }), + }) + } + + publish.push({ + tag: await needsPublish({ + force, + pkg, + tags: TAGS.root, + }), + }) + + return publish.filter(p => p.tag) +} + +const main = async (opts) => { + const packOnly = opts.pack || opts.packDestination + const publishes = await getPublishes({ force: packOnly }) + + if (!publishes.length) { + throw new Error( + 'Nothing to publish, exiting. ' + + 'All packages to publish should have their version bumped before running this script.' + ) + } + + log.info('publish', '\n' + publishes.map(JSON.stringify).join('\n')) + + await git('clean', '-fd') + await resetdeps() + await npm('ls', '--omit=dev', { quiet: true }) + await npm('rm', '--global', '--force', 'npm') + await npm('link', '--force', '--ignore-scripts') + + if (opts.test) { + await npm('run', 'lint-all', '--ignore-scripts') + await npm('run', 'postlint', '--ignore-scripts') + await npm('run', 'test-all', '--ignore-scripts') + } + + await npm('prune', '--omit=dev', '--no-save', '--no-audit', '--no-fund') + await git.dirty() + + for (const p of publishes) { + const workspace = p.workspace && `--workspace=${p.workspace}` + if (packOnly) { + await npm( + 'pack', + workspace, + opts.packDestination && `--pack-destination=${opts.packDestination}` + ) + } else { + await npm( + 'publish', + workspace, + `--tag=${p.tag}`, + opts.dryRun && '--dry-run', + opts.otp && `--otp=${opts.otp === 'op' ? await op() : opts.otp}` + ) + } + } +} + +run(main).catch(resetdeps) diff --git a/scripts/rebuild.js b/scripts/rebuild.js new file mode 100644 index 0000000000000..f3f75090c11e4 --- /dev/null +++ b/scripts/rebuild.js @@ -0,0 +1,20 @@ +const { join } = require('path') +const { promisify } = require('util') +const glob = promisify(require('glob')) +const log = require('proc-log') +const { npm, run } = require('./util') + +const main = async (pkgNames) => { + for (const name of pkgNames) { + const { path } = await npm.query(`#${name}`).then(r => r[0]) + const binding = await glob(join(path, '**', 'binding.node')) + log.info(name, binding) + if (!binding.length) { + await npm('rebuild', name) + } else { + log.info(`skipping ${name}, already built`) + } + } +} + +run(({ argv }) => main(argv.remain)) diff --git a/scripts/release.sh b/scripts/release.sh deleted file mode 100644 index a3c1356b002f7..0000000000000 --- a/scripts/release.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -# script for creating a zip and tarball for inclusion in node - -unset CDPATH - -set -e - -rm -rf release *.tgz || true -rm node_modules/node-gyp/gyp/pylib/gyp/__pycache__/*.pyc || true -rm node_modules/node-gyp/gyp/pylib/gyp/generator/__pycache__/*.pyc || true -mkdir release -node ./bin/npm-cli.js pack --loglevel error >/dev/null -mv *.tgz release -cd release -tar xzf *.tgz -cp ../.npmrc package/ -cp -r ../tap-snapshots package/ -cp -r ../test package/ - -mkdir node_modules -mv package node_modules/npm - -# make the zip for windows users -cp node_modules/npm/bin/*.cmd . -zipname=npm-$(node ../bin/npm-cli.js -v).zip -zip -q -9 -r -X "$zipname" *.cmd node_modules - -# make the tar for node's deps -cd node_modules -tarname=npm-$(node ../../bin/npm-cli.js -v).tgz -tar czf "$tarname" npm - -cd .. -mv "node_modules/$tarname" . - -rm -rf *.cmd -rm -rf node_modules - -cd .. - -echo "release/$tarname" -echo "release/$zipname" diff --git a/scripts/remove-files.js b/scripts/remove-files.js new file mode 100644 index 0000000000000..75b4385229178 --- /dev/null +++ b/scripts/remove-files.js @@ -0,0 +1,11 @@ +const { join } = require('path') +const { CWD, run, pkg, fs, git } = require('./util.js') + +const main = async () => { + await git.dirty() + for (const p of pkg.files) { + await fs.rimraf(join(CWD, p)) + } +} + +run(main) diff --git a/scripts/resetdeps.js b/scripts/resetdeps.js new file mode 100644 index 0000000000000..f0aad975386f0 --- /dev/null +++ b/scripts/resetdeps.js @@ -0,0 +1,25 @@ + +const { join } = require('path') +const { CWD, run, pkg, fs, spawn, git, npm } = require('./util.js') + +const checkout = () => git('checkout', 'node_modules/') + +const main = async ({ packageLock }) => { + await fs.rimraf(join(CWD, 'node_modules')) + for (const { path } of await pkg.mapWorkspaces()) { + await fs.rimraf(join(path, 'node_modules')) + } + + await checkout() + await npm('i', '--ignore-scripts', '--no-audit', '--no-fund', packageLock && '--package-lock') + await npm('rebuild', '--ignore-scripts') + await npm('run', 'dependencies', '--ignore-scripts') + if (process.env.CI) { + // this script can take awhile to rebuild the cmark-gfm bindings + // so we only run it in CI. locally this is handled by pretest and + // prebuild scripts, which don't run in CI due to --ignore-scripts + await spawn('node', join('scripts', 'rebuild.js'), 'cmark-gfm') + } +} + +run(main).catch(checkout) diff --git a/scripts/resetdeps.sh b/scripts/resetdeps.sh deleted file mode 100755 index 3172f8c869bd9..0000000000000 --- a/scripts/resetdeps.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -e -set -x -rm -rf node_modules -rm -rf docs/node_modules -rm -rf smoke-tests/node_modules -rm -rf "workspaces/*/node_modules" -git checkout node_modules -node . i --ignore-scripts --no-audit --no-fund "$@" -node . rebuild --ignore-scripts -node . run dependencies --ignore-scripts -# check for cmark-gfm bindings -cmarkbinding=$(find `node . ls cmark-gfm --parseable \ -| head -n 1` -name binding.node) -if [[ ! $cmarkbinding ]]; then - node . rebuild cmark-gfm -fi diff --git a/scripts/template-oss/_step-audit.yml b/scripts/template-oss/_step-audit.yml new file mode 100644 index 0000000000000..c8002e3056f0f --- /dev/null +++ b/scripts/template-oss/_step-audit.yml @@ -0,0 +1,2 @@ +- name: Run Audit + run: {{ rootNpmPath }} audit -iwr -w workspaces diff --git a/scripts/template-oss/_step-test.yml b/scripts/template-oss/_step-test.yml index 3eb0aa8971381..9471e6a5a4f55 100644 --- a/scripts/template-oss/_step-test.yml +++ b/scripts/template-oss/_step-test.yml @@ -1,8 +1,3 @@ -- name: Link - if: matrix - run: {{ rootNpmPath }} link -f --ignore-scripts -- name: Rebuild cmark-gfm - run: {{ rootNpmPath }} rebuild cmark-gfm {{> defaultStepTest }} - name: Check Git Status if: matrix && matrix.platform.os != 'windows-latest' diff --git a/scripts/template-oss/audit.yml b/scripts/template-oss/audit.yml deleted file mode 100644 index 3859c5e3dbba1..0000000000000 --- a/scripts/template-oss/audit.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Audit - -on: - workflow_dispatch: - schedule: - # "At 08:00 UTC (01:00 PT) on Monday" https://crontab.guru/#0_8_*_*_1 - - cron: "0 8 * * 1" - -jobs: - audit: - {{> job jobName="Audit Dependencies" jobDepFlags="--package-lock" }} - - name: Run Audit - run: {{ rootNpmPath }} audit -iwr -w workspaces diff --git a/scripts/template-oss/ci-release.yml b/scripts/template-oss/ci-release.yml index 6d30953715fb7..b571511c4ba4e 100644 --- a/scripts/template-oss/ci-release.yml +++ b/scripts/template-oss/ci-release.yml @@ -11,17 +11,11 @@ run: | NPM_VERSION="$({{ rootNpmPath }} --version)-$GITHUB_SHA.0" {{ rootNpmPath }} version $NPM_VERSION --ignore-scripts - {{ rootNpmPath }} run resetdeps - git clean -fd - {{ rootNpmPath }} ls --omit=dev >/dev/null - {{ rootNpmPath }} prune --omit=dev --no-save --no-audit --no-fund - node scripts/git-dirty.js - {{ rootNpmPath }} pack --pack-destination=$RUNNER_TEMP - {{ rootNpmPath }} install -g $RUNNER_TEMP/npm-$NPM_VERSION.tgz + node scripts/publish.js --pack-destination=$RUNNER_TEMP + {{ rootNpmPath }} install --global $RUNNER_TEMP/npm-$NPM_VERSION.tgz {{ rootNpmPath }} install -w smoke-tests --ignore-scripts --no-audit --no-fund - rm -rf {lib,bin,index.js} - # this one should be npm since we explicitly installed our packed - # tarball globally and the next test will make sure our the new - # globally installed version contains the git sha + node scripts/remove-files.js + # call installed npm instead of local source since we are testing + # the packed tarball that we just installed globally SMOKE_PUBLISH_NPM=1 npm test -w smoke-tests --ignore-scripts {{> stepChecks jobCheck=true }} diff --git a/scripts/template-oss/create-node-pr.yml b/scripts/template-oss/create-node-pr.yml new file mode 100644 index 0000000000000..23b267d58ee86 --- /dev/null +++ b/scripts/template-oss/create-node-pr.yml @@ -0,0 +1,28 @@ +name: "Create Node PR" + +on: + workflow_dispatch: + inputs: + spec: + description: "The npm spec to create the PR from" + required: true + default: 'latest' + dryRun: + description: "Setting this to anything will run all the steps except opening the PR" + +jobs: + create-pull-request: + {{> job jobName="Create Node PR" }} + - name: Checkout Node + uses: actions/checkout@v3 + with: + token: $\{{ secrets.NODE_PULL_REQUEST_TOKEN }} + repository: nodejs/node + fetch-depth: 0 + path: node + - name: Create Node Pull Request + env: + GITHUB_TOKEN: $\{{ secrets.NODE_PULL_REQUEST_TOKEN }} + run: | + DRY_RUN=$([ -z "$\{{ inputs.dryRun }}" ] && echo "" || echo "--dry-run") + node scripts/create-node-pr.js "$\{{ inputs.spec }}" "$DRY_RUN" diff --git a/scripts/template-oss/root.js b/scripts/template-oss/root.js index 13bd4c5faabc3..cb59c473ec032 100644 --- a/scripts/template-oss/root.js +++ b/scripts/template-oss/root.js @@ -1,13 +1,13 @@ module.exports = { rootRepo: { add: { - '.github/ISSUE_TEMPLATE/config.yml': false, - '.github/ISSUE_TEMPLATE/bug.yml': false, '.github/workflows/ci.yml': 'ci.yml', '.github/workflows/ci-release.yml': 'ci-release.yml', + '.github/workflows/create-node-pr.yml': 'create-node-pr.yml', + '.github/ISSUE_TEMPLATE/bug.yml': false, + '.github/ISSUE_TEMPLATE/config.yml': false, '.github/dependabot.yml': false, '.github/workflows/post-dependabot.yml': false, - '.github/workflows/audit.yml': 'audit.yml', }, }, workspaceRepo: { @@ -23,19 +23,16 @@ module.exports = { defaultBranch: 'latest', distPaths: [ 'index.js', - 'docs/content/**/*.md', - 'docs/output/**/*.html', - 'man', + 'docs/content/', + 'docs/output/', + 'man/', ], allowPaths: [ '/node_modules/', '/index.js', - '/Makefile', - '/make.bat', '/DEPENDENCIES.md', '/CONTRIBUTING.md', '/configure', - '/changelogs/', '/AUTHORS', '/.mailmap', '/.licensee.json', diff --git a/scripts/update-authors.js b/scripts/update-authors.js new file mode 100755 index 0000000000000..23c4430570c38 --- /dev/null +++ b/scripts/update-authors.js @@ -0,0 +1,26 @@ +const { join } = require('path') +const { CWD, run, git, fs } = require('./util.js') + +const main = async () => { + const allAuthors = await git('log', '--use-mailmap', '--reverse', '--format=%aN <%aE>', { + lines: true, + }) + + const authors = new Set() + for (const author of allAuthors) { + if ( + !author.includes('[bot]') && + !author.startsWith('npm team') && + !author.startsWith('npm CLI robot') + ) { + authors.add(author) + } + } + + return fs.writeFile(join(CWD, 'AUTHORS'), [ + `# Authors sorted by whether or not they're me`, + ...authors, + ].join('\n')) +} + +run(main) diff --git a/scripts/update-authors.sh b/scripts/update-authors.sh deleted file mode 100755 index a9c9a665ab5dc..0000000000000 --- a/scripts/update-authors.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -git log --use-mailmap --reverse --format='%aN <%aE>' | grep -v -e "\[bot\]" -e "^npm team" -e "^npm CLI robot" | perl -wnE ' -BEGIN { - say "# Authors sorted by whether or not they\x27re me"; -} - -print $seen{$_} = $_ unless $seen{$_} -' > AUTHORS diff --git a/scripts/util.js b/scripts/util.js new file mode 100644 index 0000000000000..91611bcf91be2 --- /dev/null +++ b/scripts/util.js @@ -0,0 +1,200 @@ +const fsp = require('fs/promises') +const { resolve, join, relative } = require('path') +const { formatWithOptions } = require('util') +const log = require('proc-log') +const nopt = require('nopt') +const npmGit = require('@npmcli/git') +const promiseSpawn = require('@npmcli/promise-spawn') +const mapWorkspaces = require('@npmcli/map-workspaces') + +const CWD = resolve(__dirname, '..') + +const pkg = require(join(CWD, 'package.json')) +pkg.mapWorkspaces = async () => { + const ws = [] + for (const [name, path] of await mapWorkspaces({ pkg })) { + ws.push({ name, path, pkg: require(join(path, 'package.json')) }) + } + return ws +} + +const fs = { + rimraf: (p) => fsp.rm(p, { recursive: true, force: true }), + mkdirp: (p) => fsp.mkdir(p, { recursive: true }), + clean: (p) => fs.rimraf(p).then(() => fs.mkdirp(p)), + rmAll: (p) => Promise.all(p.map(fs.rimraf)), + writeFile: async (p, d) => { + await fsp.writeFile(p, d.trim() + '\n', 'utf-8') + return `Wrote to ${relative(CWD, p)}` + }, +} + +// for spawn, allow a flat array of arguments where the +// the last arg can optionall be an options object +const getArgs = (allArgs) => { + let args = allArgs.flat().filter(Boolean) + let opts = {} + + const last = args[args.length - 1] + if (typeof last === 'object') { + args = args.slice(0, -1) + opts = last + } + + return { args, opts } +} + +const spawn = async (cmd, ...allArgs) => { + const { + args, + opts: { ok, input, out, lines, quiet, ...opts }, + } = getArgs(allArgs) + + log.info('spawn', `${cmd} ${args.join(' ')}`) + + let res = null + try { + const spawnOpts = { + stdioString: true, + stdio: quiet || out || lines ? 'pipe' : 'inherit', + cwd: CWD, + ...opts, + } + const proc = cmd === 'git' ? npmGit.spawn(args, spawnOpts) : promiseSpawn(cmd, args, spawnOpts) + if (input && proc.stdin) { + proc.stdin.write(input) + proc.stdin.end() + } + res = await proc + } catch (err) { + if (!ok) { + throw err + } + log.info('suppressed error', err.message) + } + + if (res?.stdout) { + res.stdout = res.stdout.toString().trim() + if (res.stdout) { + log.silly('stdout', res.stdout) + } + } + + if (res?.stderr) { + res.stderr = res.stderr.toString().trim() + if (res.stderr) { + log.silly('stderr', res.stderr) + } + } + + if (lines) { + return (res?.stdout || '') + .split('\n') + .map(l => l.trim()) + .filter(Boolean) + } + + if (out) { + return res?.stdout || '' + } + + return res +} + +// allows for creating spawn functions with a prefilled +// command and checking if the last arg is an options obj +spawn.create = (cmd, ...prefillArgs) => (...cmdArgs) => { + const prefill = getArgs(prefillArgs) + const command = getArgs(cmdArgs) + return spawn( + cmd, + [...prefill.args, ...command.args], + { ...prefill.opts, ...command.opts } + ) +} + +const npm = spawn.create('node', '.') +npm.query = (...args) => npm('query', ...args, { out: true }).then(JSON.parse) + +const git = spawn.create('git') +git.dirty = () => npmGit.isClean({ cwd: CWD }).then(async r => { + if (r) { + return 'git clean' + } + await git('status', '--porcelain=v1', '-uno') + throw new Error('git dirty') +}) + +const gh = spawn.create('gh') +gh.json = async (...args) => { + const keys = args.pop() + let data = await gh(...args, '--json', keys, { out: true }).then(JSON.parse) + if (keys.split(',').length === 1) { + data = data[keys] + } + return data +} + +const run = async (main) => { + const argv = {} + for (const [k, v] of Object.entries(nopt({}, {}, process.argv))) { + argv[k] = v + // create camelcase key too + argv[k.replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = v + } + + process.on('log', (l, ...args) => { + if (argv.debug || process.env.CI || l === 'error') { + for (const line of formatWithOptions({ colors: true }, ...args).split('\n')) { + // eslint-disable-next-line no-console + console.error(l.slice(0, 4).toUpperCase(), line) + } + } + }) + + log.silly('argv', argv) + + try { + const res = await main(argv) + if (res) { + // eslint-disable-next-line no-console + console.log(res) + } + } catch (err) { + process.exitCode = err.status || 1 + + const messages = [] + if (err.args) { + // its an error from promise-spawn + for (const [name, value] of Object.entries(err)) { + if (value) { + let msg = Array.isArray(value) ? value.join(' ') : value.toString() + let sep = ' ' + if (msg.includes('\n')) { + msg = ' ' + msg.split('\n').map(l => l.trim()).join('\n ').trim() + sep = '\n' + } + messages.push(`${name}:${sep}${msg}`) + } + // delete from error object so we can log them separately + delete err[name] + } + } + + log.error(err) + if (messages.length) { + log.error(messages.join('\n')) + } + } +} + +module.exports = { + CWD, + pkg, + run, + fs, + spawn, + gh, + npm, + git, +}