diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 96d8eafb..8da2a452 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,7 +4,7 @@ version: 2 updates: - package-ecosystem: npm - directory: "/" + directory: / schedule: interval: daily allow: diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index ad5724cb..60bb334b 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -10,19 +10,28 @@ on: jobs: audit: + name: Audit Dependencies if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund --package-lock - - run: npm audit + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund --package-lock + - name: Run Audit + run: npm audit diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml new file mode 100644 index 00000000..405ac3cc --- /dev/null +++ b/.github/workflows/ci-release.yml @@ -0,0 +1,154 @@ +# This file is automatically added by @npmcli/template-oss. Do not edit. + +name: CI - Release + +on: + workflow_call: + inputs: + ref: + required: true + type: string + check-sha: + required: true + type: string + +jobs: + lint-all: + name: Lint All + if: github.repository_owner == 'npm' + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + + with: + token: ${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Lint All + sha: ${{ inputs.check-sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ inputs.ref }} + - 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 + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Lint + run: npm run lint --ignore-scripts + - name: Post Lint + run: npm run postlint --ignore-scripts + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() + with: + token: ${{ secrets.GITHUB_TOKEN }} + conclusion: ${{ job.status }} + check_id: ${{ steps.check.outputs.check_id }} + + test-all: + name: Test All - ${{ matrix.platform.name }} - Node ${{ matrix.node-version }} + if: github.repository_owner == 'npm' + strategy: + fail-fast: false + matrix: + platform: + - name: Linux + os: ubuntu-latest + shell: bash + - name: macOS + os: macos-latest + shell: bash + - name: Windows + os: windows-latest + shell: cmd + node-version: + - 14.17.0 + - 14.x + - 16.13.0 + - 16.x + - 18.0.0 + - 18.x + runs-on: ${{ matrix.platform.os }} + defaults: + run: + shell: ${{ matrix.platform.shell }} + steps: + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + + with: + token: ${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Test All - ${{ matrix.platform.name }} - Node ${{ matrix.node-version }} + sha: ${{ inputs.check-sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ inputs.ref }} + - 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: ${{ matrix.node-version }} + - name: Update Windows npm + # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows + if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) + run: | + curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz + tar xf npm-7.5.4.tgz + cd package + node lib/npm.js install --no-fund --no-audit -g ..\npm-7.5.4.tgz + cd .. + rmdir /s /q package + - name: Install npm@7 + if: startsWith(matrix.node-version, '10.') + run: npm i --prefer-online --no-fund --no-audit -g npm@7 + - name: Install npm@latest + if: ${{ !startsWith(matrix.node-version, '10.') }} + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Add Problem Matcher + run: echo "::add-matcher::.github/matchers/tap.json" + - name: Test + run: npm test --ignore-scripts -ws -iwr --if-present + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() + with: + token: ${{ secrets.GITHUB_TOKEN }} + conclusion: ${{ job.status }} + check_id: ${{ steps.check.outputs.check_id }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccd10921..16f8f014 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,8 +5,6 @@ name: CI on: workflow_dispatch: pull_request: - branches: - - '*' push: branches: - main @@ -17,28 +15,50 @@ on: jobs: lint: + name: Lint if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Lint + run: npm run lint --ignore-scripts + - name: Post Lint + run: npm run postlint --ignore-scripts test: + name: Test All - ${{ matrix.platform.name }} - Node ${{ matrix.node-version }} if: github.repository_owner == 'npm' strategy: fail-fast: false matrix: + platform: + - name: Linux + os: ubuntu-latest + shell: bash + - name: macOS + os: macos-latest + shell: bash + - name: Windows + os: windows-latest + shell: cmd node-version: - 14.17.0 - 14.x @@ -46,27 +66,22 @@ jobs: - 16.x - 18.0.0 - 18.x - platform: - - os: ubuntu-latest - shell: bash - - os: macos-latest - shell: bash - - os: windows-latest - shell: cmd runs-on: ${{ matrix.platform.os }} defaults: run: shell: ${{ matrix.platform.shell }} steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - - name: Update to workable npm (windows) + - name: Update Windows npm # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) run: | @@ -76,15 +91,17 @@ jobs: node lib/npm.js install --no-fund --no-audit -g ..\npm-7.5.4.tgz cd .. rmdir /s /q package - - name: Update npm to 7 - # If we do test on npm 10 it needs npm7 + - name: Install npm@7 if: startsWith(matrix.node-version, '10.') run: npm i --prefer-online --no-fund --no-audit -g npm@7 - - name: Update npm to latest + - name: Install npm@latest if: ${{ !startsWith(matrix.node-version, '10.') }} run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: add tap problem matcher + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm test --ignore-scripts + - name: Test + run: npm test --ignore-scripts -iwr diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index cfd0db30..66b9498a 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,6 +1,6 @@ # This file is automatically added by @npmcli/template-oss. Do not edit. -name: "CodeQL" +name: CodeQL on: push: @@ -8,7 +8,6 @@ on: - main - latest pull_request: - # The branches below must be a subset of the branches above branches: - main - latest @@ -24,21 +23,16 @@ jobs: actions: read contents: read security-events: write - - strategy: - fail-fast: false - matrix: - language: [ javascript ] - steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: - languages: ${{ matrix.language }} + languages: javascript - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/post-dependabot.yml b/.github/workflows/post-dependabot.yml index d077d36a..98acf69c 100644 --- a/.github/workflows/post-dependabot.yml +++ b/.github/workflows/post-dependabot.yml @@ -1,43 +1,91 @@ # This file is automatically added by @npmcli/template-oss. Do not edit. -name: Post Dependabot Actions +name: Post Dependabot on: pull_request -# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions permissions: contents: write jobs: - template-oss-apply: + template-oss: + name: template-oss if: github.repository_owner == 'npm' && github.actor == 'dependabot[bot]' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head_ref }} - - name: Setup git user + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Dependabot metadata + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Fetch Dependabot Metadata id: metadata - uses: dependabot/fetch-metadata@v1.1.1 + uses: dependabot/fetch-metadata@v1 with: - github-token: "${{ secrets.GITHUB_TOKEN }}" - - name: Apply @npmcli/template-oss changes and lint + github-token: ${{ secrets.GITHUB_TOKEN }} + + # Dependabot can update multiple directories so we output which directory + # it is acting on so we can run the command for the correct root or workspace + - name: Get Dependabot Directory if: contains(steps.metadata.outputs.dependency-names, '@npmcli/template-oss') + id: flags + run: | + if [[ "${{ steps.metadata.outputs.directory }}" == "/" ]]; then + echo "::set-output name=workspace::-iwr" + else + echo "::set-output name=workspace::-w ${{ steps.metadata.outputs.directory }}" + fi + + - name: Apply Changes + if: steps.flags.outputs.workspace + id: apply + run: | + npm run template-oss-apply ${{ steps.flags.outputs.workspace }} + if [[ `git status --porcelain` ]]; then + echo "::set-output name=changes::true" + fi + + # This step will fail if template-oss has made any workflow updates. It is impossible + # for a workflow to update other workflows. In the case it does fail, we continue + # and then try to apply only a portion of the changes in the next step + - name: Push All Changes + if: steps.apply.outputs.changes + id: push + continue-on-error: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - npm run template-oss-apply git commit -am "chore: postinstall for dependabot template-oss PR" git push - npm run lint + + - name: Push All Changes Except Workflows + if: steps.push.outcome == 'failure' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git reset HEAD~ + git checkout HEAD -- .github/workflows/ + git clean -fd .github/workflows/ + git commit -am "chore: postinstall for dependabot template-oss PR" + git push + + - name: Check Changes + if: steps.apply.outputs.changes + run: | + npm exec --offline ${{ steps.flags.outputs.workspace }} -- template-oss-check diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index cb11e7a2..1a1d1ee8 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -1,6 +1,6 @@ # This file is automatically added by @npmcli/template-oss. Do not edit. -name: Pull Request Linting +name: Pull Request on: pull_request: @@ -11,28 +11,38 @@ on: - synchronize jobs: - check: - name: Check PR Title or Commits + commitlint: + name: Lint Commits if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 with: fetch-depth: 0 - - name: Setup git user + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Check commits or PR title - env: - PR_TITLE: ${{ github.event.pull_request.title }} + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Commitlint on Commits + id: commit + continue-on-error: true run: | - npx --offline commitlint -V --from origin/main --to ${{ github.event.pull_request.head.sha }} \ - || echo $PR_TITLE | npx --offline commitlint -V + npx --offline commitlint -V --from origin/${{ github.base_ref }} --to ${{ github.event.pull_request.head.sha }} + - name: Run Commitlint on PR Title + if: steps.commit.outcome == 'failure' + run: | + echo ${{ github.event.pull_request.title }} | npx --offline commitlint -V diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml deleted file mode 100644 index ae8dc424..00000000 --- a/.github/workflows/release-please.yml +++ /dev/null @@ -1,102 +0,0 @@ -# This file is automatically added by @npmcli/template-oss. Do not edit. - -name: Release Please - -on: - push: - branches: - - main - - latest - -permissions: - contents: write - pull-requests: write - -jobs: - release-please: - outputs: - pr: ${{ steps.release.outputs.pr }} - release: ${{ steps.release.outputs.release }} - if: github.repository_owner == 'npm' - runs-on: ubuntu-latest - steps: - - 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" - - uses: actions/setup-node@v3 - with: - node-version: 18.x - - name: Update npm to latest - run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Release Please - id: release - run: npx --offline template-oss-release-please ${{ github.ref_name }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - post-pr: - needs: release-please - if: needs.release-please.outputs.pr - runs-on: ubuntu-latest - outputs: - ref: ${{ steps.ref.outputs.branch }} - steps: - - name: Output ref - id: ref - run: echo "::set-output name=branch::${{ fromJSON(needs.release-please.outputs.pr).headBranchName }}" - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ steps.ref.outputs.branch }} - - name: Setup git user - run: | - git config --global user.email "npm-cli+bot@github.com" - git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 - with: - node-version: 18.x - - name: Update npm to latest - run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Post pull request actions - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - npm run rp-pull-request --ignore-scripts --if-present -ws -iwr - git commit -am "chore: post pull request" || true - git push - - release-test: - needs: post-pr - if: needs.post-pr.outputs.ref - uses: ./.github/workflows/release.yml - with: - ref: ${{ needs.post-pr.outputs.ref }} - - post-release: - needs: release-please - if: github.repository_owner == 'npm' && needs.release-please.outputs.release - runs-on: ubuntu-latest - steps: - - 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" - - uses: actions/setup-node@v3 - with: - node-version: 18.x - - name: Update npm to latest - run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Post release actions - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - npm run rp-release --ignore-scripts --if-present -ws -iwr diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3bc79b94..01a8d6a9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,86 +3,232 @@ name: Release on: - workflow_call: - inputs: - ref: - required: true - type: string + push: + branches: + - main + - latest + +permissions: + contents: write + pull-requests: write + checks: write jobs: - lint-all: + release: + outputs: + pr: ${{ steps.release.outputs.pr }} + releases: ${{ steps.release.outputs.releases }} + release-flags: ${{ steps.release.outputs.release-flags }} + branch: ${{ steps.release.outputs.pr-branch }} + pr-number: ${{ steps.release.outputs.pr-number }} + comment-id: ${{ steps.pr-comment.outputs.result }} + check-id: ${{ steps.check.outputs.check_id }} + name: Release if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - with: - ref: ${{ inputs.ref }} - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint -ws -iwr --if-present + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Release Please + id: release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + npx --offline template-oss-release-please ${{ github.ref_name }} + - name: Post Pull Request Comment + if: steps.release.outputs.pr-number + uses: actions/github-script@v6 + id: pr-comment + env: + PR_NUMBER: ${{ steps.release.outputs.pr-number }} + with: + script: | + const repo = { owner: context.repo.owner, repo: context.repo.repo } + const issue = { ...repo, issue_number: process.env.PR_NUMBER } - test-all: - if: github.repository_owner == 'npm' - strategy: - fail-fast: false - matrix: - node-version: - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - - 18.0.0 - - 18.x - platform: - - os: ubuntu-latest - shell: bash - - os: macos-latest - shell: bash - - os: windows-latest - shell: cmd - runs-on: ${{ matrix.platform.os }} + const { data: workflow } = await github.rest.actions.getWorkflowRun({ ...repo, run_id: context.runId }) + + let body = '## Release Manager\n\n' + + const comments = await github.paginate(github.rest.issues.listComments, issue) + let commentId = comments?.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id + + body += `- Release workflow run: ${workflow.html_url}` + if (commentId) { + await github.rest.issues.updateComment({ ...repo, comment_id: commentId, body }) + } else { + const { data: comment } = await github.rest.issues.createComment({ ...issue, body }) + commentId = comment?.id + } + + return commentId + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + if: steps.release.outputs.pr-number + with: + token: ${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Release + sha: ${{ steps.release.outputs.pr-sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + + update: + needs: release + outputs: + sha: ${{ steps.commit.outputs.sha }} + check-id: ${{ steps.check.outputs.check_id }} + name: Update - Release + if: github.repository_owner == 'npm' && needs.release.outputs.pr + runs-on: ubuntu-latest defaults: run: - shell: ${{ matrix.platform.shell }} + shell: bash steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 with: - ref: ${{ inputs.ref }} - - name: Setup git user + fetch-depth: 0 + ref: ${{ needs.release.outputs.branch }} + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: - node-version: ${{ matrix.node-version }} - - name: Update to workable npm (windows) - # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows - if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) + node-version: 18.x + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Post Pull Request Actions + env: + RELEASE_PR_NUMBER: ${{ needs.release.outputs.pr-number }} + RELEASE_COMMENT_ID: ${{ needs.release.outputs.comment-id }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + npm run rp-pull-request --ignore-scripts -ws -iwr --if-present + - name: Commit + id: commit + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz - tar xf npm-7.5.4.tgz - cd package - node lib/npm.js install --no-fund --no-audit -g ..\npm-7.5.4.tgz - cd .. - rmdir /s /q package - - name: Update npm to 7 - # If we do test on npm 10 it needs npm7 - if: startsWith(matrix.node-version, '10.') - run: npm i --prefer-online --no-fund --no-audit -g npm@7 - - name: Update npm to latest - if: ${{ !startsWith(matrix.node-version, '10.') }} + git commit --all --amend --no-edit || true + git push --force-with-lease + echo "::set-output name=sha::$(git rev-parse HEAD)" + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + + with: + token: ${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Release + sha: ${{ steps.commit.outputs.sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() + with: + token: ${{ secrets.GITHUB_TOKEN }} + conclusion: ${{ job.status }} + check_id: ${{ needs.release.outputs.check-id }} + + ci: + name: CI - Release + needs: [ release, update ] + if: needs.release.outputs.pr + uses: ./.github/workflows/ci-release.yml + with: + ref: ${{ needs.release.outputs.branch }} + check-sha: ${{ needs.update.outputs.sha }} + + post-ci: + needs: [ release, update, ci ] + name: Post CI - Release + if: github.repository_owner == 'npm' && needs.release.outputs.pr && always() + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Get Needs Result + id: needs-result + run: | + result="" + if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then + result="failure" + elif [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + result="cancelled" + else + result="success" + fi + echo "::set-output name=result::$result" + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() + with: + token: ${{ secrets.GITHUB_TOKEN }} + conclusion: ${{ steps.needs-result.outputs.result }} + check_id: ${{ needs.update.outputs.check-id }} + + post-release: + needs: release + name: Post Release - Release + if: github.repository_owner == 'npm' && needs.release.outputs.releases + 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 + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: add tap problem matcher - run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm run test -ws -iwr --if-present + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Post Release Actions + env: + RELEASES: ${{ needs.release.outputs.releases }} + run: | + npm run rp-release --ignore-scripts --if-present ${{ join(fromJSON(needs.release.outputs.release-flags), ' ') }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 34a3350a..83f9eb80 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "4.2.0" + ".": "4.3.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 397e2792..3d50175b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [4.3.0](https://github.com/npm/template-oss/compare/v4.2.0...v4.3.0) (2022-09-19) + +### Features + +* [`3640080`](https://github.com/npm/template-oss/commit/36400808bf9da35e10aab0c6663fb284624b8dd6) add checks to release pull request (@lukekarrys) +* [`5b65537`](https://github.com/npm/template-oss/commit/5b655374771b62c572800dd56f44c102f863ba73) add names to all jobs and steps (@lukekarrys) +* [`caf393c`](https://github.com/npm/template-oss/commit/caf393c6d01b7608eeafe4503fb73fd47d21193c) add dependabot configuration for workspaces (@lukekarrys) +* [`e43ee70`](https://github.com/npm/template-oss/commit/e43ee70c03e41c6bc25b1938a360ccfc33a30319) [#198](https://github.com/npm/template-oss/pull/198) update codeql actions to v2 (@lukekarrys) + ## [4.2.0](https://github.com/npm/template-oss/compare/v4.1.2...v4.2.0) (2022-09-15) ### Features diff --git a/bin/release-please.js b/bin/release-please.js index 23e4e5f5..7fd9d153 100755 --- a/bin/release-please.js +++ b/bin/release-please.js @@ -6,30 +6,24 @@ const main = require('../lib/release-please/index.js') const dryRun = !process.env.CI const [branch] = process.argv.slice(2) -const setOutput = (key, val) => { - if (val && (!Array.isArray(val) || val.length)) { - if (dryRun) { - if (key === 'pr') { - console.log('PR:', val.title.toString()) - console.log('='.repeat(40)) - console.log(val.body.toString()) - console.log('='.repeat(40)) - for (const update of val.updates.filter(u => u.updater.changelogEntry)) { - console.log('CHANGELOG:', update.path) - console.log('-'.repeat(40)) - console.log(update.updater.changelogEntry) - console.log('-'.repeat(40)) - } - for (const update of val.updates.filter(u => u.updater.rawContent)) { - console.log('package:', update.path) - console.log('-'.repeat(40)) - console.log(JSON.parse(update.updater.rawContent).name) - console.log(JSON.parse(update.updater.rawContent).version) - console.log('-'.repeat(40)) - } - } - } else { - core.setOutput(key, JSON.stringify(val)) +const debugPr = (val) => { + if (dryRun) { + console.log('PR:', val.title.toString()) + console.log('='.repeat(40)) + console.log(val.body.toString()) + console.log('='.repeat(40)) + for (const update of val.updates.filter(u => u.updater.changelogEntry)) { + console.log('CHANGELOG:', update.path) + console.log('-'.repeat(40)) + console.log(update.updater.changelogEntry) + console.log('-'.repeat(40)) + } + for (const update of val.updates.filter(u => u.updater.rawContent)) { + console.log('package:', update.path) + console.log('-'.repeat(40)) + console.log(JSON.parse(update.updater.rawContent).name) + console.log(JSON.parse(update.updater.rawContent).version) + console.log('-'.repeat(40)) } } } @@ -39,10 +33,30 @@ main({ repo: process.env.GITHUB_REPOSITORY, dryRun, branch, -}).then(({ pr, releases, release }) => { - setOutput('pr', pr) - setOutput('releases', releases) - setOutput('release', release) +}).then(({ pr, release, releases }) => { + if (pr) { + debugPr(pr) + core.setOutput('pr', JSON.stringify(pr)) + core.setOutput('pr-branch', pr.headBranchName) + core.setOutput('pr-number', pr.number) + core.setOutput('pr-sha', pr.sha) + } + + if (release) { + core.setOutput('release', JSON.stringify(release)) + core.setOutput('release-path', release.path) + core.setOutput('release-version', release.version) + core.setOutput('release-tag', release.tagName) + core.setOutput('release-url', release.url) + } + + if (releases) { + core.setOutput('releases', JSON.stringify(releases)) + core.setOutput('release-flags', JSON.stringify(releases.map((r) => { + return r.path === '.' ? '-iwr' : `-w ${r.path}` + }))) + } + return null }).catch(err => { if (dryRun) { diff --git a/lib/config.js b/lib/config.js index d0a34079..e5871f49 100644 --- a/lib/config.js +++ b/lib/config.js @@ -15,7 +15,10 @@ const DEFAULT_CONTENT = require.resolve(NAME) const merge = withArrays('branches', 'distPaths', 'allowPaths', 'ignorePaths') -const makePosix = (str) => str.split(win32.sep).join(posix.sep) +const makePosix = (v) => v.split(win32.sep).join(posix.sep) +const deglob = (v) => makePosix(v).replace(/[/*]+$/, '') +const posixDir = (v) => `${v === '.' ? '' : deglob(v).replace(/\/$/, '')}${posix.sep}` +const posixGlob = (str) => `${posixDir(str)}**` const getCmdPath = (key, { rootConfig, defaultConfig, isRoot, path, root }) => { // Make a path relative from a workspace to the root if we are in a workspace @@ -78,27 +81,27 @@ const getFiles = (path, rawConfig) => { } const getFullConfig = async ({ + // the path to the root of the repo root, + // the path to the package being operated on + // this is the same as root when operating on the root path, - pkg, + // the full contents of the package.json for this package + pkgJson, + // an array of all package info {pkgJson,path,config}[] pkgs, + // an array of all workspaces in this repo workspaces, + // the config from the package.json in the root rootConfig: _rootConfig, + // the config from the package.json being operated on pkgConfig: _pkgConfig, }) => { const isRoot = root === path - const isRootMono = isRoot && workspaces.length > 0 const isLatest = _pkgConfig.version === LATEST_VERSION - const isDogFood = pkg.name === NAME + const isDogFood = pkgJson.name === NAME const isForce = process.argv.includes('--force') - // this is written to ci yml files so it needs to always use posix - const pkgRelPath = makePosix(relative(root, path)) - - const workspacePkgs = pkgs.filter((p) => p.path !== path) - const workspaceDirs = isRootMono && workspaces.map((p) => makePosix(relative(root, p))) - const workspaceGlobs = isRootMono && pkg.workspaces.map(p => p.replace(/[/*]+$/, '')) - // These config items are merged betweent the root and child workspaces and only come from // the package.json because they can be used to read configs from other the content directories const mergedConfig = mergeConfigs(_rootConfig, _pkgConfig) @@ -112,6 +115,7 @@ const getFullConfig = async ({ // The content config only gets set from the package we are in, it doesn't inherit // anything from the root + const rootPkgConfig = merge(useDefault, rootConfig) const pkgConfig = merge(useDefault, getConfig(_pkgConfig.content, _pkgConfig)) const [pkgFiles, pkgDir] = getFiles(mergedConfig.content, mergedConfig) @@ -128,16 +132,24 @@ const getFullConfig = async ({ ...isRoot ? [ // in the root allow all repo files ...getAddedFiles(repoFiles), - // and allow all workspace repo level files - ...workspacePkgs.filter(p => p.config.workspaceRepo !== false).flatMap((p) => - getAddedFiles(files.workspaceRepo) - ), + // and allow all workspace repo level files in the root + ...pkgs + .filter(p => p.path !== root && p.config.workspaceRepo !== false) + .flatMap(() => getAddedFiles(files.workspaceRepo)), ] : [], ] + // root only configs const npmPath = getCmdPath('npm', { rootConfig, defaultConfig, isRoot, path, root }) const npxPath = getCmdPath('npx', { rootConfig, defaultConfig, isRoot, path, root }) + // these are written to ci yml files so it needs to always use posix + const pkgPath = makePosix(relative(root, path)) || '.' + + // we use the raw paths from the package.json workspaces as ignore patterns in + // some cases. the workspaces passed in have already been run through map workspaces + const workspacePaths = (pkgJson.workspaces || []).map(deglob) + // all derived keys const derived = { isRoot, @@ -147,8 +159,8 @@ const getFullConfig = async ({ // For these cases it is helpful to know if we are in a // monorepo since template-oss might be used only for // workspaces and not the root or vice versa. - isRootMono, - isMono: isRootMono || !isRoot, + isRootMono: isRoot && !!workspaces.length, + isMono: !!workspaces.length, // repo repoDir: root, repoFiles, @@ -158,13 +170,16 @@ const getFullConfig = async ({ moduleFiles, applyModule: !!moduleFiles, // package - pkgName: pkg.name, - pkgNameFs: pkg.name.replace(/\//g, '-').replace(/@/g, ''), - pkgRelPath: pkgRelPath, - pkgPrivate: !!pkg.private, - pkgPublic: !pkg.private, - workspaces: workspaceDirs, - workspaceGlobs, + pkgName: pkgJson.name, + pkgNameFs: pkgJson.name.replace(/\//g, '-').replace(/@/g, ''), + // paths + pkgPath, + pkgDir: posixDir(pkgPath), + pkgGlob: posixGlob(pkgPath), + pkgFlags: isRoot ? '-iwr' : `-w ${pkgJson.name}`, + allFlags: '-ws -iwr --if-present', + workspacePaths, + workspaceGlobs: workspacePaths.map(posixGlob), // booleans to control application of updates isForce, isDogFood, @@ -175,6 +190,9 @@ const getFullConfig = async ({ rootNpmPath: npmPath.root, localNpmPath: npmPath.local, rootNpxPath: npxPath.root, + // lockfiles are only present at the root, so this only should be set for + // all workspaces based on the root + lockfile: rootPkgConfig.lockfile, // gitignore ignorePaths: [ ...gitignore.sort([ @@ -185,7 +203,7 @@ const getFullConfig = async ({ ]), // these cant be sorted since they rely on order // to allow a previously ignored directoy - ...gitignore.allowDir(workspaceDirs || []), + ...isRoot ? gitignore.allowDir(workspaces.map((p) => makePosix(relative(root, p)))) : [], ], // needs update if we are dogfooding this repo, with force argv, or its // behind the current version @@ -210,7 +228,7 @@ const getFullConfig = async ({ derived.repository = { type: 'git', url: gitUrl, - ...(pkgRelPath ? { directory: pkgRelPath } : {}), + ...(!isRoot ? { directory: pkgPath } : {}), } } diff --git a/lib/content/_setup-job-matrix.yml b/lib/content/_job-matrix.yml similarity index 64% rename from lib/content/_setup-job-matrix.yml rename to lib/content/_job-matrix.yml index 5a614cc1..66ec5415 100644 --- a/lib/content/_setup-job-matrix.yml +++ b/lib/content/_job-matrix.yml @@ -1,27 +1,29 @@ +name: {{ jobName }} if: github.repository_owner == 'npm' strategy: fail-fast: false matrix: - node-version: - {{#each ciVersions}} - - {{.}} - {{/each}} platform: - - os: ubuntu-latest + - name: Linux + os: ubuntu-latest shell: bash {{#if macCI}} - - os: macos-latest + - name: macOS + os: macos-latest shell: bash {{/if}} {{#if windowsCI}} - - os: windows-latest + - name: Windows + os: windows-latest shell: cmd {{/if}} + node-version: + {{#each ciVersions}} + - {{ . }} + {{/each}} runs-on: $\{{ matrix.platform.os }} defaults: run: shell: $\{{ matrix.platform.shell }} steps: - {{> setupGit}} - {{> setupNode useMatrix=true}} - {{> setupDeps}} + {{> stepsSetup jobNodeMatrix=true }} diff --git a/lib/content/_job.yml b/lib/content/_job.yml new file mode 100644 index 00000000..48c6100a --- /dev/null +++ b/lib/content/_job.yml @@ -0,0 +1,8 @@ +name: {{ jobName }} +if: github.repository_owner == 'npm' {{~#if jobIf}} && {{{ jobIf }}}{{/if}} +runs-on: ubuntu-latest +defaults: + run: + shell: bash +steps: + {{> stepsSetup }} diff --git a/lib/content/_setup-ci-on.yml b/lib/content/_on-ci.yml similarity index 54% rename from lib/content/_setup-ci-on.yml rename to lib/content/_on-ci.yml index 4ba9bd59..1352a8b1 100644 --- a/lib/content/_setup-ci-on.yml +++ b/lib/content/_on-ci.yml @@ -1,30 +1,28 @@ workflow_dispatch: pull_request: - branches: - - '*' - {{#if pkgRelPath}} + {{#if isWorkspace}} paths: - - {{pkgRelPath}}/** + - {{ pkgGlob }} {{/if}} - {{#if workspaceGlobs}} + {{#if isRootMono}} paths-ignore: - {{#each workspaceGlobs}} - - {{.}}/** + {{#each workspaceGlob}} + - {{ . }} {{/each}} {{/if}} push: branches: {{#each branches}} - - {{.}} + - {{ . }} {{/each}} - {{#if pkgRelPath}} + {{#if isWorkspace}} paths: - - {{pkgRelPath}}/** + - {{ pkgGlob }} {{/if}} - {{#if workspaceGlobs}} + {{#if isRootMono}} paths-ignore: - {{#each workspaceGlobs}} - - {{.}}/** + {{#each workspaceGlob}} + - {{ . }} {{/each}} {{/if}} schedule: diff --git a/lib/content/_setup-deps.yml b/lib/content/_setup-deps.yml deleted file mode 100644 index 4e521a13..00000000 --- a/lib/content/_setup-deps.yml +++ /dev/null @@ -1 +0,0 @@ -- run: {{rootNpmPath}} i --ignore-scripts --no-audit --no-fund {{~#if flags}} {{flags}}{{/if}} diff --git a/lib/content/_setup-git.yml b/lib/content/_setup-git.yml deleted file mode 100644 index 3a874352..00000000 --- a/lib/content/_setup-git.yml +++ /dev/null @@ -1,11 +0,0 @@ -- uses: actions/checkout@v3 -{{#if checkout}} - with: - {{#each checkout}} - {{@key}}: {{this}} - {{/each}} -{{/if}} -- name: Setup git user - run: | - git config --global user.email "npm-cli+bot@github.com" - git config --global user.name "npm CLI robot" diff --git a/lib/content/_setup-job.yml b/lib/content/_setup-job.yml deleted file mode 100644 index a9cec5b8..00000000 --- a/lib/content/_setup-job.yml +++ /dev/null @@ -1,6 +0,0 @@ -if: github.repository_owner == 'npm' {{~#if jobIf}} && {{{jobIf}}}{{/if}} -runs-on: ubuntu-latest -steps: - {{> setupGit}} - {{> setupNode}} - {{> setupDeps}} diff --git a/lib/content/_step-checks.yml b/lib/content/_step-checks.yml new file mode 100644 index 00000000..f38e78e8 --- /dev/null +++ b/lib/content/_step-checks.yml @@ -0,0 +1,24 @@ +- name: {{#if jobCheck.sha}}Create{{else}}Conclude{{/if}} Check + uses: LouisBrunner/checks-action@v1.3.1 + {{#if jobCheck.sha}} + id: check + {{#if jobCheck.if}}if: {{ jobCheck.if }}{{/if}} + {{else}} + if: always() + {{/if}} + with: + token: $\{{ secrets.GITHUB_TOKEN }} + {{#if jobCheck.sha}} + status: {{#if jobCheck.status}}{{ jobCheck.status }}{{else}}in_progress{{/if}} + name: {{#if jobCheck.name}}{{ jobCheck.name }}{{else}}{{ jobName }}{{/if}} + sha: {{ jobCheck.sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + {{else}} + conclusion: {{#if jobCheck.status}}{{ jobCheck.status }}{{else}}$\{{ job.status }}{{/if}} + check_id: {{#if jobCheck.id}}{{ jobCheck.id }}{{else}}$\{{ steps.check.outputs.check_id }}{{/if}} + {{/if}} diff --git a/lib/content/_step-deps.yml b/lib/content/_step-deps.yml new file mode 100644 index 00000000..de65db92 --- /dev/null +++ b/lib/content/_step-deps.yml @@ -0,0 +1,2 @@ +- name: Install Dependencies + run: {{ rootNpmPath }} i --ignore-scripts --no-audit --no-fund {{~#if jobDepFlags}} {{ jobDepFlags }}{{/if}} diff --git a/lib/content/_step-git.yml b/lib/content/_step-git.yml new file mode 100644 index 00000000..2211d118 --- /dev/null +++ b/lib/content/_step-git.yml @@ -0,0 +1,12 @@ +- name: Checkout + uses: actions/checkout@v3 + {{#if jobCheckout}} + with: + {{#each jobCheckout}} + {{ @key }}: {{ this }} + {{/each}} + {{/if}} +- name: Setup Git User + run: | + git config --global user.email "npm-cli+bot@github.com" + git config --global user.name "npm CLI robot" diff --git a/lib/content/_step-lint.yml b/lib/content/_step-lint.yml new file mode 100644 index 00000000..7070daee --- /dev/null +++ b/lib/content/_step-lint.yml @@ -0,0 +1,4 @@ +- name: Lint + run: {{ rootNpmPath }} run lint --ignore-scripts +- name: Post Lint + run: {{ rootNpmPath }} run postlint --ignore-scripts diff --git a/lib/content/_setup-node.yml b/lib/content/_step-node.yml similarity index 67% rename from lib/content/_setup-node.yml rename to lib/content/_step-node.yml index e1fc29db..bb27e6b9 100644 --- a/lib/content/_setup-node.yml +++ b/lib/content/_step-node.yml @@ -1,12 +1,13 @@ -- uses: actions/setup-node@v3 +- name: Setup Node + uses: actions/setup-node@v3 with: - node-version: {{#if useMatrix}}$\{{ matrix.node-version }}{{else}}{{#each ciVersions}}{{#if @last}}{{.}}{{/if}}{{/each}}{{/if}} + node-version: {{#if jobNodeMatrix}}$\{{ matrix.node-version }}{{else}}{{ last ciVersions }}{{/if}} {{#if lockfile}} cache: npm {{/if}} {{#if updateNpm}} -{{#if useMatrix}} -- name: Update to workable npm (windows) +{{#if jobNodeMatrix}} +- name: Update Windows npm # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) run: | @@ -16,15 +17,15 @@ node lib/npm.js install --no-fund --no-audit -g ..\npm-7.5.4.tgz cd .. rmdir /s /q package -- name: Update npm to 7 - # If we do test on npm 10 it needs npm7 +- name: Install npm@7 if: startsWith(matrix.node-version, '10.') run: npm i --prefer-online --no-fund --no-audit -g npm@7 -- name: Update npm to latest +- name: Install npm@latest if: $\{{ !startsWith(matrix.node-version, '10.') }} {{else}} -- name: Update npm to latest +- name: Install npm@latest {{/if}} run: npm i --prefer-online --no-fund --no-audit -g npm@latest -- run: npm -v +- name: npm Version + run: npm -v {{/if}} diff --git a/lib/content/_step-test.yml b/lib/content/_step-test.yml new file mode 100644 index 00000000..2a869cd9 --- /dev/null +++ b/lib/content/_step-test.yml @@ -0,0 +1,4 @@ +- name: Add Problem Matcher + run: echo "::add-matcher::.github/matchers/tap.json" +- name: Test + run: {{ rootNpmPath }} test --ignore-scripts {{~#if jobRunFlags}} {{ jobRunFlags }}{{/if}} diff --git a/lib/content/_steps-setup.yml b/lib/content/_steps-setup.yml new file mode 100644 index 00000000..e17d5f3d --- /dev/null +++ b/lib/content/_steps-setup.yml @@ -0,0 +1,6 @@ +{{~#if jobCheck}}{{> stepChecks }}{{/if}} +{{~#unless jobSkipSetup}} +{{> stepGit }} +{{> stepNode }} +{{> stepDeps }} +{{/unless}} diff --git a/lib/content/audit.yml b/lib/content/audit.yml index 6efc27aa..17a1343c 100644 --- a/lib/content/audit.yml +++ b/lib/content/audit.yml @@ -8,5 +8,6 @@ on: jobs: audit: - {{> setupJob flags="--package-lock"}} - - run: {{rootNpmPath}} audit + {{> job jobName="Audit Dependencies" jobDepFlags="--package-lock" }} + - name: Run Audit + run: {{ rootNpmPath }} audit diff --git a/lib/content/ci-release.yml b/lib/content/ci-release.yml new file mode 100644 index 00000000..7b83bd11 --- /dev/null +++ b/lib/content/ci-release.yml @@ -0,0 +1,31 @@ + +name: CI - Release + +on: + workflow_call: + inputs: + ref: + required: true + type: string + check-sha: + required: true + type: string + +jobs: + lint-all: + {{> job + jobName="Lint All" + jobCheck=(obj sha="${{ inputs.check-sha }}") + jobCheckout=(obj ref="${{ inputs.ref }}") + }} + {{> stepLint jobRunFlags=allFlags }} + {{> stepChecks jobCheck=true }} + + test-all: + {{> jobMatrix + jobName="Test All - ${{ matrix.platform.name }} - Node ${{ matrix.node-version }}" + jobCheck=(obj sha="${{ inputs.check-sha }}") + jobCheckout=(obj ref="${{ inputs.ref }}") + }} + {{> stepTest jobRunFlags=allFlags }} + {{> stepChecks jobCheck=true }} diff --git a/lib/content/ci.yml b/lib/content/ci.yml index 225bd5b4..feff29f1 100644 --- a/lib/content/ci.yml +++ b/lib/content/ci.yml @@ -1,15 +1,13 @@ -name: CI {{~#if isWorkspace}} - {{pkgName}}{{/if}} +name: CI {{~#if isWorkspace}} - {{ pkgName }}{{/if}} on: - {{> setupCiOn}} + {{> onCi }} jobs: lint: - {{> setupJob }} - - run: {{rootNpmPath}} run lint {{~#if isWorkspace}} -w {{pkgName}}{{/if}} + {{> job jobName="Lint" }} + {{> stepLint jobRunFlags=pkgFlags }} test: - {{> setupJobMatrix }} - - name: add tap problem matcher - run: echo "::add-matcher::.github/matchers/tap.json" - - run: {{rootNpmPath}} test --ignore-scripts {{~#if isWorkspace}} -w {{pkgName}}{{/if}} + {{> jobMatrix jobName="Test All - ${{ matrix.platform.name }} - Node ${{ matrix.node-version }}" }} + {{> stepTest jobRunFlags=pkgFlags }} diff --git a/lib/content/codeql-analysis.yml b/lib/content/codeql-analysis.yml index 382214ff..4e4c18f4 100644 --- a/lib/content/codeql-analysis.yml +++ b/lib/content/codeql-analysis.yml @@ -1,16 +1,15 @@ -name: "CodeQL" +name: CodeQL on: push: branches: {{#each branches}} - - {{.}} + - {{ . }} {{/each}} pull_request: - # The branches below must be a subset of the branches above branches: {{#each branches}} - - {{.}} + - {{ . }} {{/each}} schedule: # "At 10:00 UTC (03:00 PT) on Monday" https://crontab.guru/#0_10_*_*_1 @@ -24,17 +23,11 @@ jobs: actions: read contents: read security-events: write - - strategy: - fail-fast: false - matrix: - language: [javascript] - steps: - {{> setupGit}} - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: $\{{ matrix.language }} - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + {{> stepGit }} + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: javascript + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/lib/content/commitlintrc.js b/lib/content/commitlintrc.js index 2a0b0cde..26966878 100644 --- a/lib/content/commitlintrc.js +++ b/lib/content/commitlintrc.js @@ -1,7 +1,7 @@ module.exports = { extends: ['@commitlint/config-conventional'], rules: { - 'type-enum': [2, 'always', [{{#each changelogTypes}}'{{type}}'{{#unless @last}}, {{/unless}}{{/each}}]], + 'type-enum': [2, 'always', [{{{ join (quote (pluck changelogTypes "type")) }}}]], 'header-max-length': [2, 'always', 80], 'subject-case': [0, 'always', ['lower-case', 'sentence-case', 'start-case']], }, diff --git a/lib/content/dependabot.yml b/lib/content/dependabot.yml index ca82a4a1..0f747f9d 100644 --- a/lib/content/dependabot.yml +++ b/lib/content/dependabot.yml @@ -2,32 +2,14 @@ version: 2 updates: - package-ecosystem: npm - directory: "/" + directory: {{ pkgDir }} schedule: interval: daily allow: - dependency-type: direct - versioning-strategy: increase-if-necessary + versioning-strategy: {{ dependabot }} commit-message: prefix: deps prefix-development: chore labels: - "Dependencies" - - {{#if workspaces}} - {{#each workspaces}} - - package-ecosystem: npm - directory: "{{.}}/" - schedule: - interval: daily - allow: - - dependency-type: direct - versioning-strategy: increase-if-necessary - commit-message: - prefix: deps - prefix-development: chore - labels: - - "Dependencies" - - {{/each}} - {{/if}} diff --git a/lib/content/eslintrc.js b/lib/content/eslintrc.js index 5bb7b067..8ee511c7 100644 --- a/lib/content/eslintrc.js +++ b/lib/content/eslintrc.js @@ -8,10 +8,10 @@ const localConfigs = readdir(__dirname) module.exports = { root: true, - {{#if isRootMono}} + {{#if workspaceGlobs}} ignorePatterns: [ - {{#each workspaces}} - '{{.}}', + {{#each workspaceGlobs}} + '{{ . }}', {{/each}} ], {{/if}} diff --git a/lib/content/gitignore b/lib/content/gitignore index d85a1774..d9d4b23e 100644 --- a/lib/content/gitignore +++ b/lib/content/gitignore @@ -3,5 +3,5 @@ # keep these {{#each ignorePaths}} -{{.}} +{{ . }} {{/each}} diff --git a/lib/content/index.js b/lib/content/index.js index 0df1d8b7..fa03a3c5 100644 --- a/lib/content/index.js +++ b/lib/content/index.js @@ -1,33 +1,49 @@ const { name: NAME, version: LATEST_VERSION } = require('../../package.json') -const releasePlease = () => ({ - '.github/workflows/release-please.yml': { - file: 'release-please.yml', - filter: (o) => !o.pkg.private, - }, +const isPublic = (p) => !p.pkg.private + +const sharedRoot = (name) => ({ + // release '.github/workflows/release.yml': { file: 'release.yml', - filter: (o) => !o.pkg.private, + filter: isPublic, + }, + '.github/workflows/ci-release.yml': { + file: 'ci-release.yml', + filter: isPublic, }, '.release-please-manifest.json': { file: 'release-please-manifest.json', - filter: (o) => !o.pkg.private, - parser: (p) => class NoCommentJson extends p.JsonMerge { + filter: isPublic, + parser: (p) => class extends p.JsonMerge { comment = null }, }, 'release-please-config.json': { file: 'release-please-config.json', - filter: (o) => !o.pkg.private, - parser: (p) => class NoCommentJson extends p.JsonMerge { + filter: isPublic, + parser: (p) => class extends p.JsonMerge { comment = null }, }, -}) - -const tap = (name) => ({ + // ci '.github/matchers/tap.json': 'tap.json', [`.github/workflows/ci${name ? `-${name}` : ''}.yml`]: 'ci.yml', + // dependabot + '.github/dependabot.yml': { + file: 'dependabot.yml', + clean: (p) => p.config.isRoot, + // dependabot takes a single top level config file. this parser + // will run for all configured packages and each one will have + // its item replaced in the updates array based on the directory + parser: (p) => class extends p.YmlMerge { + key = 'updates' + id = 'directory' + }, + }, + '.github/workflows/post-dependabot.yml': { + file: 'post-dependabot.yml', + }, }) // Changes applied to the root of the repo @@ -37,16 +53,14 @@ const rootRepo = { '.github/ISSUE_TEMPLATE/bug.yml': 'bug.yml', '.github/ISSUE_TEMPLATE/config.yml': 'config.yml', '.github/CODEOWNERS': 'CODEOWNERS', - '.github/dependabot.yml': 'dependabot.yml', '.github/workflows/audit.yml': 'audit.yml', '.github/workflows/codeql-analysis.yml': 'codeql-analysis.yml', - '.github/workflows/post-dependabot.yml': 'post-dependabot.yml', '.github/workflows/pull-request.yml': 'pull-request.yml', - ...releasePlease(), - ...tap(), + ...sharedRoot(), }, rm: [ '.github/workflows/release-test.yml', + '.github/workflows/release-please.yml', ], } @@ -72,12 +86,11 @@ const rootModule = { // Changes for each workspace but applied to the root of the repo const workspaceRepo = { add: { - ...releasePlease(), - ...tap('{{pkgNameFs}}'), + ...sharedRoot('{{ pkgNameFs }}'), }, rm: [ // These are the old release please files that should be removed now - '.github/workflows/release-please-{{pkgNameFs}}.yml', + '.github/workflows/release-please-{{ pkgNameFs }}.yml', ], } @@ -104,7 +117,6 @@ module.exports = { macCI: true, branches: ['main', 'latest'], releaseBranches: [], - defaultBranch: 'main', distPaths: [ 'bin/', 'lib/', @@ -128,6 +140,7 @@ module.exports = { lockfile: false, npm: 'npm', npx: 'npx', + dependabot: 'increase-if-necessary', unwantedPackages: [ 'eslint', 'eslint-plugin-node', diff --git a/lib/content/npmrc b/lib/content/npmrc index 239fb98e..ebcd656b 100644 --- a/lib/content/npmrc +++ b/lib/content/npmrc @@ -1 +1 @@ -package-lock={{lockfile}} +package-lock={{ lockfile }} diff --git a/lib/content/pkg.json b/lib/content/pkg.json index 4335fdbd..d93cbd80 100644 --- a/lib/content/pkg.json +++ b/lib/content/pkg.json @@ -1,45 +1,47 @@ { "author": "GitHub Inc.", - "files": {{{json distPaths}}}, + "files": {{{ json distPaths }}}, "scripts": { "lint": "eslint \"**/*.js\"", "postlint": "template-oss-check", "template-oss-apply": "template-oss-apply --force", - "lintfix": "{{localNpmPath}} run lint -- --fix", - "preversion": {{{del}}}, - "postversion": {{{del}}}, - "prepublishOnly": {{{del}}}, - "postpublish": {{{del}}}, + "lintfix": "{{ localNpmPath }} run lint -- --fix", "snap": "tap", "test": "tap", - "posttest": "{{localNpmPath}} run lint", + "posttest": "{{ localNpmPath }} run lint", {{#if isRootMono}} - "test-all": "{{localNpmPath}} run test -ws -iwr --if-present", - "lint-all": "{{localNpmPath}} run lint -ws -iwr --if-present", + "test-all": "{{ localNpmPath }} run test {{ allFlags }}", + "lint-all": "{{ localNpmPath }} run lint {{ allFlags }}", {{/if}} - "template-copy": {{{del}}}, - "lint:fix": {{{del}}} + "template-copy": {{{ del }}}, + "lint:fix": {{{ del }}}, + "preversion": {{{ del }}}, + "postversion": {{{ del }}}, + "prepublishOnly": {{{ del }}}, + "postpublish": {{{ del }}} }, - "repository": {{#if repository}}{{{json repository}}}{{else}}{{{del}}}{{/if}}, + "repository": {{#if repository}}{{{ json repository }}}{{else}}{{{ del }}}{{/if}}, "engines": { {{#if engines}} - "node": {{{json engines}}} + "node": {{{ json engines }}} {{/if}} }, - {{{json __CONFIG_KEY__}}}: { - "version": {{#if isDogFood}}{{{del}}}{{else}}{{{json __VERSION__}}}{{/if}} + {{{ json __CONFIG_KEY__ }}}: { + "version": {{#if isDogFood}}{{{ del }}}{{else}}{{{ json __VERSION__ }}}{{/if}} }, - "templateVersion": {{{del}}}, - "standard": {{{del}}}, + "templateVersion": {{{ del }}}, + "standard": {{{ del }}}, "tap": { - {{#if isRootMono}} - "test-ignore": "^({{#each workspaceGlobs}}{{this}}{{#unless @last}}|{{/unless}}{{/each}})/", + {{#if workspacePaths}} + "test-ignore": "^({{ join workspacePaths "|" }})/**", {{/if}} "nyc-arg": [ - {{#if isRootMono}} - {{#each workspaceGlobs}}"--exclude", "{{this}}/**",{{/each}} - {{/if}} - "--exclude", "tap-snapshots/**" + {{#each workspaceGlobs}} + "--exclude", + "{{ . }}", + {{/each}} + "--exclude", + "tap-snapshots/**" ] } } diff --git a/lib/content/post-dependabot.yml b/lib/content/post-dependabot.yml index d6562244..52506961 100644 --- a/lib/content/post-dependabot.yml +++ b/lib/content/post-dependabot.yml @@ -1,26 +1,70 @@ -name: Post Dependabot Actions +name: Post Dependabot on: pull_request -# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions permissions: contents: write jobs: - template-oss-apply: - {{> setupJob jobIf="github.actor == 'dependabot[bot]'" checkout=(obj ref="${{ github.event.pull_request.head_ref }}")}} - - name: Dependabot metadata + template-oss: + {{> job + jobName="template-oss" + jobIf="github.actor == 'dependabot[bot]'" + jobCheckout=(obj ref="${{ github.event.pull_request.head_ref }}") + }} + - name: Fetch Dependabot Metadata id: metadata - uses: dependabot/fetch-metadata@v1.1.1 + uses: dependabot/fetch-metadata@v1 with: - github-token: "$\{{ secrets.GITHUB_TOKEN }}" - - name: Apply {{__NAME__}} changes and lint - if: contains(steps.metadata.outputs.dependency-names, '{{__NAME__}}') + github-token: $\{{ secrets.GITHUB_TOKEN }} + + # Dependabot can update multiple directories so we output which directory + # it is acting on so we can run the command for the correct root or workspace + - name: Get Dependabot Directory + if: contains(steps.metadata.outputs.dependency-names, '{{ __NAME__ }}') + id: flags + run: | + if [[ "$\{{ steps.metadata.outputs.directory }}" == "/" ]]; then + echo "::set-output name=workspace::-iwr" + else + echo "::set-output name=workspace::-w $\{{ steps.metadata.outputs.directory }}" + fi + + - name: Apply Changes + if: steps.flags.outputs.workspace + id: apply + run: | + {{ rootNpmPath }} run template-oss-apply $\{{ steps.flags.outputs.workspace }} + if [[ `git status --porcelain` ]]; then + echo "::set-output name=changes::true" + fi + + # This step will fail if template-oss has made any workflow updates. It is impossible + # for a workflow to update other workflows. In the case it does fail, we continue + # and then try to apply only a portion of the changes in the next step + - name: Push All Changes + if: steps.apply.outputs.changes + id: push + continue-on-error: true env: GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }} run: | - {{rootNpmPath}} run template-oss-apply git commit -am "chore: postinstall for dependabot template-oss PR" git push - {{rootNpmPath}} run lint + + - name: Push All Changes Except Workflows + if: steps.push.outcome == 'failure' + env: + GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }} + run: | + git reset HEAD~ + git checkout HEAD -- .github/workflows/ + git clean -fd .github/workflows/ + git commit -am "chore: postinstall for dependabot template-oss PR" + git push + + - name: Check Changes + if: steps.apply.outputs.changes + run: | + {{ rootNpmPath }} exec --offline $\{{ steps.flags.outputs.workspace }} -- template-oss-check diff --git a/lib/content/pull-request.yml b/lib/content/pull-request.yml index 3bbc04e9..c8c02a6a 100644 --- a/lib/content/pull-request.yml +++ b/lib/content/pull-request.yml @@ -1,4 +1,4 @@ -name: Pull Request Linting +name: Pull Request on: pull_request: @@ -9,12 +9,14 @@ on: - synchronize jobs: - check: - name: Check PR Title or Commits - {{> setupJob checkout=(obj fetch-depth=0)}} - - name: Check commits or PR title - env: - PR_TITLE: $\{{ github.event.pull_request.title }} + commitlint: + {{> job jobName="Lint Commits" jobCheckout=(obj fetch-depth=0) }} + - name: Run Commitlint on Commits + id: commit + continue-on-error: true run: | - {{rootNpxPath}} --offline commitlint -V --from origin/{{defaultBranch}} --to $\{{ github.event.pull_request.head.sha }} \ - || echo $PR_TITLE | npx --offline commitlint -V + {{ rootNpxPath }} --offline commitlint -V --from origin/$\{{ github.base_ref }} --to $\{{ github.event.pull_request.head.sha }} + - name: Run Commitlint on PR Title + if: steps.commit.outcome == 'failure' + run: | + echo $\{{ github.event.pull_request.title }} | {{ rootNpxPath }} --offline commitlint -V diff --git a/lib/content/release-please-config.json b/lib/content/release-please-config.json index 6976fd32..3562cee3 100644 --- a/lib/content/release-please-config.json +++ b/lib/content/release-please-config.json @@ -1,13 +1,13 @@ { - "separate-pull-requests": {{{del}}}, - "plugins": {{#if isMono}}["node-workspace"]{{else}}{{{del}}}{{/if}}, + "separate-pull-requests": {{{ del }}}, + "plugins": {{#if isMono }}["node-workspace"]{{ else }}{{{ del }}}{{/if}}, "exclude-packages-from-root": true, "group-pull-request-title-pattern": "chore: release ${version}", "pull-request-title-pattern": "chore: release${component} ${version}", - "changelog-sections": {{{json changelogTypes}}}, + "changelog-sections": {{{ json changelogTypes }}}, "packages": { - "{{#unless pkgRelPath}}.{{/unless}}{{pkgRelPath}}": { - {{#unless pkgRelPath}}"package-name": ""{{/unless}} + "{{ pkgPath }}": { + {{#if isRoot}}"package-name": ""{{/if}} } } } diff --git a/lib/content/release-please-manifest.json b/lib/content/release-please-manifest.json index 1ddfcacb..a67fb196 100644 --- a/lib/content/release-please-manifest.json +++ b/lib/content/release-please-manifest.json @@ -1,3 +1,3 @@ { - "{{#unless pkgRelPath}}.{{/unless}}{{pkgRelPath}}": "{{pkg.version}}" + "{{ pkgPath }}": "{{ pkg.version }}" } diff --git a/lib/content/release-please.yml b/lib/content/release-please.yml deleted file mode 100644 index 908531cc..00000000 --- a/lib/content/release-please.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Release Please - -on: - push: - branches: - {{#each branches}} - - {{.}} - {{/each}} - {{#each releaseBranches}} - - {{.}} - {{/each}} - -permissions: - contents: write - pull-requests: write - -jobs: - release-please: - outputs: - pr: $\{{ steps.release.outputs.pr }} - release: $\{{ steps.release.outputs.release }} - {{> setupJob }} - - name: Release Please - id: release - run: npx --offline template-oss-release-please $\{{ github.ref_name }} - env: - GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }} - - post-pr: - needs: release-please - if: needs.release-please.outputs.pr - runs-on: ubuntu-latest - outputs: - ref: $\{{ steps.ref.outputs.branch }} - steps: - - name: Output ref - id: ref - run: echo "::set-output name=branch::$\{{ fromJSON(needs.release-please.outputs.pr).headBranchName }}" - {{> setupGit checkout=(obj ref="${{ steps.ref.outputs.branch }}" fetch-depth=0)}} - {{> setupNode}} - {{> setupDeps}} - - name: Post pull request actions - env: - GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }} - run: | - {{rootNpmPath}} run rp-pull-request --ignore-scripts --if-present -ws -iwr - git commit -am "chore: post pull request" || true - git push - - release-test: - needs: post-pr - if: needs.post-pr.outputs.ref - uses: ./.github/workflows/release.yml - with: - ref: $\{{ needs.post-pr.outputs.ref }} - - post-release: - needs: release-please - {{> setupJob jobIf="needs.release-please.outputs.release" }} - - name: Post release actions - env: - GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }} - run: | - {{rootNpmPath}} run rp-release --ignore-scripts --if-present -ws -iwr diff --git a/lib/content/release.yml b/lib/content/release.yml index e336fb4f..a37b8e1e 100644 --- a/lib/content/release.yml +++ b/lib/content/release.yml @@ -1,20 +1,125 @@ - name: Release on: - workflow_call: - inputs: - ref: - required: true - type: string + push: + branches: + {{#each branches}} + - {{ . }} + {{/each}} + {{#each releaseBranches }} + - {{ . }} + {{/each}} + +permissions: + contents: write + pull-requests: write + checks: write jobs: - lint-all: - {{> setupJob checkout=(obj ref="${{ inputs.ref }}")}} - - run: {{rootNpmPath}} run lint -ws -iwr --if-present - - test-all: - {{> setupJobMatrix checkout=(obj ref="${{ inputs.ref }}")}} - - name: add tap problem matcher - run: echo "::add-matcher::.github/matchers/tap.json" - - run: {{rootNpmPath}} run test -ws -iwr --if-present + release: + outputs: + pr: $\{{ steps.release.outputs.pr }} + releases: $\{{ steps.release.outputs.releases }} + release-flags: $\{{ steps.release.outputs.release-flags }} + branch: $\{{ steps.release.outputs.pr-branch }} + pr-number: $\{{ steps.release.outputs.pr-number }} + comment-id: $\{{ steps.pr-comment.outputs.result }} + check-id: $\{{ steps.check.outputs.check_id }} + {{> job jobName="Release" }} + - name: Release Please + id: release + env: + GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }} + run: | + {{ rootNpxPath }} --offline template-oss-release-please $\{{ github.ref_name }} + - name: Post Pull Request Comment + if: steps.release.outputs.pr-number + uses: actions/github-script@v6 + id: pr-comment + env: + PR_NUMBER: $\{{ steps.release.outputs.pr-number }} + with: + script: | + const repo = { owner: context.repo.owner, repo: context.repo.repo } + const issue = { ...repo, issue_number: process.env.PR_NUMBER } + + const { data: workflow } = await github.rest.actions.getWorkflowRun({ ...repo, run_id: context.runId }) + + let body = '## Release Manager\n\n' + + const comments = await github.paginate(github.rest.issues.listComments, issue) + let commentId = comments?.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id + + body += `- Release workflow run: ${workflow.html_url}` + if (commentId) { + await github.rest.issues.updateComment({ ...repo, comment_id: commentId, body }) + } else { + const { data: comment } = await github.rest.issues.createComment({ ...issue, body }) + commentId = comment?.id + } + + return commentId + {{> stepChecks jobCheck=(obj name="Release" sha="${{ steps.release.outputs.pr-sha }}" if="steps.release.outputs.pr-number") }} + + update: + needs: release + outputs: + sha: $\{{ steps.commit.outputs.sha }} + check-id: $\{{ steps.check.outputs.check_id }} + {{> job + jobName="Update - Release" + jobIf="needs.release.outputs.pr" + jobCheckout=(obj ref="${{ needs.release.outputs.branch }}" fetch-depth=0) + }} + - name: Run Post Pull Request Actions + env: + RELEASE_PR_NUMBER: $\{{ needs.release.outputs.pr-number }} + RELEASE_COMMENT_ID: $\{{ needs.release.outputs.comment-id }} + GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }} + run: | + {{ rootNpmPath }} run rp-pull-request --ignore-scripts {{ allFlags }} + - name: Commit + id: commit + env: + GITHUB_TOKEN: $\{{ secrets.GITHUB_TOKEN }} + run: | + git commit --all --amend --no-edit || true + git push --force-with-lease + echo "::set-output name=sha::$(git rev-parse HEAD)" + {{> stepChecks jobCheck=(obj sha="${{ steps.commit.outputs.sha }}" name="Release" )}} + {{> stepChecks jobCheck=(obj id="${{ needs.release.outputs.check-id }}" )}} + + ci: + name: CI - Release + needs: [release, update] + if: needs.release.outputs.pr + uses: ./.github/workflows/ci-release.yml + with: + ref: $\{{ needs.release.outputs.branch }} + check-sha: $\{{ needs.update.outputs.sha }} + + post-ci: + needs: [release, update, ci] + {{> job jobName="Post CI - Release" jobIf="needs.release.outputs.pr && always()" jobSkipSetup=true }} + - name: Get Needs Result + id: needs-result + run: | + result="" + if [[ "$\{{ contains(needs.*.result, 'failure') }}" == "true" ]]; then + result="failure" + elif [[ "$\{{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + result="cancelled" + else + result="success" + fi + echo "::set-output name=result::$result" + {{> stepChecks jobCheck=(obj id="${{ needs.update.outputs.check-id }}" status="${{ steps.needs-result.outputs.result }}") }} + + post-release: + needs: release + {{> job jobName="Post Release - Release" jobIf="needs.release.outputs.releases" }} + - name: Run Post Release Actions + env: + RELEASES: $\{{ needs.release.outputs.releases }} + run: | + {{ rootNpmPath }} run rp-release --ignore-scripts --if-present $\{{ join(fromJSON(needs.release.outputs.release-flags), ' ') }} diff --git a/lib/index.js b/lib/index.js index 0a6f055d..e0a229a1 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,8 +7,8 @@ const mapWorkspaces = require('@npmcli/map-workspaces') const getPkg = async (path) => { log.verbose('get-pkg', path) - const pkg = (await PackageJson.load(path)).content - const pkgConfig = getConfig.getPkgConfig(pkg) + const pkgJson = (await PackageJson.load(path)).content + const pkgConfig = getConfig.getPkgConfig(pkgJson) log.verbose('get-pkg', pkgConfig) if (pkgConfig.content) { @@ -16,7 +16,7 @@ const getPkg = async (path) => { } return { - pkg, + pkgJson, path, config: pkgConfig, } @@ -25,21 +25,18 @@ const getPkg = async (path) => { const getWsPkgs = async (root, rootPkg) => { const wsPkgs = [] - // workspaces are only used to filter paths and control changes to workspaces - // so dont pass it along with the rest of the config - const { workspaces, ...baseConfig } = rootPkg.config - // Include all by default + const { workspaces } = rootPkg.config const include = (name) => Array.isArray(workspaces) ? workspaces.includes(name) : true // Look through all workspaces on the root pkg - const rootWorkspaces = await mapWorkspaces({ pkg: rootPkg.pkg, cwd: root }) + const rootWorkspaces = await mapWorkspaces({ pkg: rootPkg.pkgJson, cwd: root }) for (const [wsName, wsPath] of rootWorkspaces.entries()) { if (include(wsName)) { // A workspace can control its own workspaceRepo and workspaceModule settings // which are true by default on the root config - wsPkgs.push(await getPkg(wsPath, baseConfig)) + wsPkgs.push(await getPkg(wsPath)) } } @@ -67,19 +64,19 @@ const runAll = async (root, checks) => { const results = [] const { pkgs, workspaces, rootPkg: { config: rootConfig } } = await getPkgs(root) - for (const { pkg, path, config: pkgConfig } of pkgs) { + for (const { pkgJson, path, config: pkgConfig } of pkgs) { // full config includes original config values const fullConfig = await getConfig({ root, path, - pkg, + pkgJson, pkgs, workspaces, rootConfig, pkgConfig, }) - const options = { root, pkg, path, config: fullConfig } + const options = { root, path, pkg: pkgJson, config: fullConfig } log.verbose('run-all', options) // files can export multiple checks so flatten first diff --git a/lib/release-please/index.js b/lib/release-please/index.js index 2464143c..ffc9f908 100644 --- a/lib/release-please/index.js +++ b/lib/release-please/index.js @@ -26,13 +26,34 @@ const main = async ({ repo: fullRepo, token, dryRun, branch }) => { ) const pullRequests = await (dryRun ? manifest.buildPullRequests() : manifest.createPullRequests()) - const releases = await (dryRun ? manifest.buildReleases() : manifest.createReleases()) + const allReleases = await (dryRun ? manifest.buildReleases() : manifest.createReleases()) + + // We only ever get a single pull request with our current release-please settings + const rootPr = pullRequests.filter(Boolean)[0] + if (rootPr?.number) { + const commits = await github.octokit.paginate(github.octokit.rest.pulls.listCommits, { + owner: github.repository.owner, + repo: github.repository.repo, + pull_number: rootPr.number, + }) + rootPr.sha = commits?.[commits.length - 1]?.sha + } + + const releases = allReleases.filter(Boolean) + const [rootRelease, workspaceReleases] = releases.reduce((acc, r) => { + if (r.path === '.') { + acc[0] = r + } else { + acc[1].push(r) + } + return acc + }, [null, []]) return { - // We only ever get a single pull request with our current release-please settings - pr: pullRequests.filter(Boolean)[0], - releases: releases.filter(Boolean), - release: releases.find(r => r.path === '.'), + pr: rootPr, + release: rootRelease, + releases: releases.length ? releases : null, + workspaceReleases: workspaceReleases.length ? workspaceReleases : null, } } diff --git a/lib/util/files.js b/lib/util/files.js index 4e38290d..2e190064 100644 --- a/lib/util/files.js +++ b/lib/util/files.js @@ -23,18 +23,20 @@ const fileEntries = (dir, files, options) => Object.entries(files) // given an obj of files, return the full target/source paths and associated parser const getParsers = (dir, files, options) => { const parsers = fileEntries(dir, files, options).map(([target, source]) => { - const { file, parser, filter } = source + const { file, parser, filter, clean: shouldClean } = source if (typeof filter === 'function' && !filter(options)) { return null } + const clean = typeof shouldClean === 'function' ? shouldClean(options) : false + if (parser) { // allow files to extend base parsers or create new ones - return new (parser(Parser.Parsers))(target, file, options) + return new (parser(Parser.Parsers))(target, file, options, { clean }) } - return new (Parser(file))(target, file, options) + return new (Parser(file))(target, file, options, { clean }) }) return parsers.filter(Boolean) diff --git a/lib/util/parser.js b/lib/util/parser.js index 59f650cc..861da7b5 100644 --- a/lib/util/parser.js +++ b/lib/util/parser.js @@ -8,7 +8,9 @@ const { unset } = require('lodash') const template = require('./template.js') const jsonDiff = require('./json-diff') const merge = require('./merge.js') + const setFirst = (first, rest) => ({ ...first, ...rest }) + const traverse = (value, visit, keys = []) => { if (keys.length) { const res = visit(keys, value) @@ -23,17 +25,25 @@ const traverse = (value, visit, keys = []) => { } } +const fsOk = (code) => (error) => { + if (error.code === 'ENOENT') { + return null + } + return Object.assign(error, { code }) +} + class Base { static types = [] - static header = 'This file is automatically added by {{__NAME__}}. Do not edit.' + static header = 'This file is automatically added by {{ __NAME__ }}. Do not edit.' comment = (v) => v merge = false // supply a merge function which runs on prepare for certain types DELETE = template.DELETE - constructor (target, source, options) { + constructor (target, source, options, fileOptions) { this.target = target this.source = source this.options = options + this.fileOptions = fileOptions } header () { @@ -42,6 +52,13 @@ class Base { } } + clean () { + if (this.fileOptions.clean) { + return fs.rm(this.target).catch(fsOk()) + } + return null + } + read (s) { return fs.readFile(s, { encoding: 'utf-8' }) } @@ -88,13 +105,17 @@ class Base { // XXX: everything is allowed to be overridden in base classes but we could // find a different solution than making everything public applyWrite () { - return Promise.resolve(this.read(this.source)) + return Promise.resolve(this.clean()) + .then(() => this.read(this.source)) // replace template vars first, this will throw for nonexistant vars // because it must be parseable after this step .then((s) => this.template(s)) // parse into whatever data structure is necessary for maniuplating // diffing, merging, etc. by default its a string - .then((s) => this.parse(s)) + .then((s) => { + this.sourcePreParse = s + return this.parse(s) + }) // prepare the source for writing and diffing, pass in current // target for merging. errors parsing or preparing targets are ok here .then((s) => this.applyTarget().catch(() => null).then((t) => this.prepare(s, t))) @@ -109,14 +130,9 @@ class Base { } async applyDiff () { - const target = await this.applyTarget().catch((e) => { - // handle if old does not exist - if (e.code === 'ENOENT') { - return null - } else { - return { code: 'ETARGETERROR', error: e } - } - }) + // handle if old does not exist + const targetError = 'ETARGETERROR' + const target = await this.applyTarget().catch(fsOk(targetError)) // no need to diff if current file does not exist if (target === null) { @@ -131,11 +147,11 @@ class Base { // if there was a target error then there is no need to diff // so we just show the source with an error message - if (target.code === 'ETARGETERROR') { + if (target.code === targetError) { const msg = `[${this.options.config.__NAME__} ERROR]` return [ `${msg} There was an erroring getting the target file`, - `${msg} ${target.error}`, + `${msg} ${target}`, `${msg} It will be overwritten with the following source:`, '-'.repeat(40), this.toString(source), @@ -175,7 +191,12 @@ class Yml extends Base { comment = (c) => ` ${c}` toString (s) { - return s.toString({ lineWidth: 0, indent: 2 }) + try { + return s.toString({ lineWidth: 0, indent: 2 }) + } catch (err) { + err.message = [this.target, this.sourcePreParse, ...s.errors, err.message].join('\n') + throw err + } } parse (s) { @@ -192,6 +213,41 @@ class Yml extends Base { } } +class YmlMerge extends Yml { + prepare (source, t) { + if (t === null) { + // If target does not exist or is in an + // error state, we cant do anything but write + // the whole document + return super.prepare(source) + } + + const key = [].concat(this.key) + + const getId = (node) => { + const index = node.items.findIndex(p => p.key?.value === this.id) + return index !== -1 ? node.items[index].value?.value : node.toJSON() + } + + const target = this.parse(t) + const targetNodes = target.getIn(key).items.reduce((acc, node, index) => { + acc[getId(node)] = { node, index } + return acc + }, {}) + + for (const node of source.getIn(key).items) { + const index = targetNodes[getId(node)]?.index + if (typeof index === 'number' && index !== -1) { + target.setIn([...key, index], node) + } else { + target.addIn(key, node) + } + } + + return super.prepare(target) + } +} + class Json extends Base { static types = ['json'] // its a json comment! not really but we do add a special key @@ -220,7 +276,7 @@ class Json extends Base { } class JsonMerge extends Json { - static header = 'This file is partially managed by {{__NAME__}}. Edits may be overwritten.' + static header = 'This file is partially managed by {{ __NAME__ }}. Edits may be overwritten.' merge = (t, s) => merge(t, s) } @@ -260,6 +316,7 @@ const Parsers = { Ini, Markdown, Yml, + YmlMerge, Json, JsonMerge, PackageJson, diff --git a/lib/util/template.js b/lib/util/template.js index d6deab9c..20910b79 100644 --- a/lib/util/template.js +++ b/lib/util/template.js @@ -3,12 +3,19 @@ const { basename, extname, join } = require('path') const fs = require('fs') const DELETE = '__DELETE__' +const safeValues = (obj) => Object.entries(obj).map(([key, value]) => + [key, new Handlebars.SafeString(value)]) + const partialName = (s) => basename(s, extname(s)) // remove extension .replace(/^_/, '') // remove leading underscore .replace(/-([a-z])/g, (_, g) => g.toUpperCase()) // camelcase const setupHandlebars = (...partialDirs) => { - Handlebars.registerHelper('obj', ({ hash }) => hash) + Handlebars.registerHelper('obj', ({ hash }) => Object.fromEntries(safeValues(hash))) + Handlebars.registerHelper('join', (arr, sep) => arr.join(typeof sep === 'string' ? sep : ', ')) + Handlebars.registerHelper('pluck', (arr, key) => arr.map(a => a[key])) + Handlebars.registerHelper('quote', (arr) => arr.map(a => `'${a}'`)) + Handlebars.registerHelper('last', (arr) => arr[arr.length - 1]) Handlebars.registerHelper('json', (c) => JSON.stringify(c)) Handlebars.registerHelper('del', () => JSON.stringify(DELETE)) diff --git a/package.json b/package.json index 572517f0..cc313a8f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/template-oss", - "version": "4.2.0", + "version": "4.3.0", "description": "templated files used in npm CLI team oss projects", "main": "lib/content/index.js", "bin": { diff --git a/tap-snapshots/test/apply/files-snapshots.js.test.cjs b/tap-snapshots/test/apply/files-snapshots.js.test.cjs index b6aa6493..b59a4af3 100644 --- a/tap-snapshots/test/apply/files-snapshots.js.test.cjs +++ b/tap-snapshots/test/apply/files-snapshots.js.test.cjs @@ -16,11 +16,11 @@ exports[`test/apply/files-snapshots.js TAP private workspace > expect resolving .github/workflows/audit.yml .github/workflows/ci-a.yml .github/workflows/ci-b.yml +.github/workflows/ci-release.yml .github/workflows/ci.yml .github/workflows/codeql-analysis.yml .github/workflows/post-dependabot.yml .github/workflows/pull-request.yml -.github/workflows/release-please.yml .github/workflows/release.yml .gitignore .npmrc @@ -45,11 +45,11 @@ exports[`test/apply/files-snapshots.js TAP turn off add/rm types > expect resolv .github/ISSUE_TEMPLATE/config.yml .github/matchers/tap.json .github/workflows/audit.yml +.github/workflows/ci-release.yml .github/workflows/ci.yml .github/workflows/codeql-analysis.yml .github/workflows/post-dependabot.yml .github/workflows/pull-request.yml -.github/workflows/release-please.yml .github/workflows/release.yml .release-please-manifest.json package.json @@ -64,11 +64,11 @@ exports[`test/apply/files-snapshots.js TAP turn off module > expect resolving Pr .github/ISSUE_TEMPLATE/config.yml .github/matchers/tap.json .github/workflows/audit.yml +.github/workflows/ci-release.yml .github/workflows/ci.yml .github/workflows/codeql-analysis.yml .github/workflows/post-dependabot.yml .github/workflows/pull-request.yml -.github/workflows/release-please.yml .github/workflows/release.yml .release-please-manifest.json package.json @@ -96,11 +96,11 @@ exports[`test/apply/files-snapshots.js TAP turn off specific files > expect reso .github/ISSUE_TEMPLATE/config.yml .github/matchers/tap.json .github/workflows/audit.yml +.github/workflows/ci-release.yml .github/workflows/ci.yml .github/workflows/codeql-analysis.yml .github/workflows/post-dependabot.yml .github/workflows/pull-request.yml -.github/workflows/release-please.yml .github/workflows/release-test.yml .github/workflows/release.yml .gitignore @@ -113,9 +113,11 @@ SECURITY.md ` exports[`test/apply/files-snapshots.js TAP workspaces > expect resolving Promise 1`] = ` +.github/dependabot.yml .github/matchers/tap.json .github/workflows/ci-d.yml -.github/workflows/release-please.yml +.github/workflows/ci-release.yml +.github/workflows/post-dependabot.yml .github/workflows/release.yml .release-please-manifest.json package.json @@ -133,10 +135,12 @@ workspaces/d/package.json ` exports[`test/apply/files-snapshots.js TAP workspaces only (like npm/cli) > expect resolving Promise 1`] = ` +.github/dependabot.yml .github/matchers/tap.json .github/workflows/ci-a.yml .github/workflows/ci-b.yml -.github/workflows/release-please.yml +.github/workflows/ci-release.yml +.github/workflows/post-dependabot.yml .github/workflows/release.yml .release-please-manifest.json package.json diff --git a/tap-snapshots/test/apply/source-snapshots.js.test.cjs b/tap-snapshots/test/apply/source-snapshots.js.test.cjs index 6a316f09..d44a27cb 100644 --- a/tap-snapshots/test/apply/source-snapshots.js.test.cjs +++ b/tap-snapshots/test/apply/source-snapshots.js.test.cjs @@ -53,7 +53,7 @@ version: 2 updates: - package-ecosystem: npm - directory: "/" + directory: / schedule: interval: daily allow: @@ -177,22 +177,188 @@ on: jobs: audit: + name: Audit Dependencies 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 + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund --package-lock + - name: Run Audit + run: npm audit + +.github/workflows/ci-release.yml +======================================== +# This file is automatically added by @npmcli/template-oss. Do not edit. + +name: CI - Release + +on: + workflow_call: + inputs: + ref: + required: true + type: string + check-sha: + required: true + type: string + +jobs: + lint-all: + name: Lint All + if: github.repository_owner == 'npm' + runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + + with: + token: \${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Lint All + sha: \${{ inputs.check-sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: \${{ inputs.ref }} + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Lint + run: npm run lint --ignore-scripts + - name: Post Lint + run: npm run postlint --ignore-scripts + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() + with: + token: \${{ secrets.GITHUB_TOKEN }} + conclusion: \${{ job.status }} + check_id: \${{ steps.check.outputs.check_id }} + + test-all: + name: Test All - \${{ matrix.platform.name }} - Node \${{ matrix.node-version }} + if: github.repository_owner == 'npm' + strategy: + fail-fast: false + matrix: + platform: + - name: Linux + os: ubuntu-latest + shell: bash + - name: macOS + os: macos-latest + shell: bash + - name: Windows + os: windows-latest + shell: cmd + node-version: + - 14.17.0 + - 14.x + - 16.13.0 + - 16.x + - 18.0.0 + - 18.x + runs-on: \${{ matrix.platform.os }} + defaults: + run: + shell: \${{ matrix.platform.shell }} + steps: + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + + with: + token: \${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Test All - \${{ matrix.platform.name }} - Node \${{ matrix.node-version }} + sha: \${{ inputs.check-sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: \${{ inputs.ref }} + - 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: \${{ matrix.node-version }} + - name: Update Windows npm + # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows + if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) + run: | + curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz + tar xf npm-7.5.4.tgz + cd package + node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz + cd .. + rmdir /s /q package + - name: Install npm@7 + if: startsWith(matrix.node-version, '10.') + run: npm i --prefer-online --no-fund --no-audit -g npm@7 + - name: Install npm@latest + if: \${{ !startsWith(matrix.node-version, '10.') }} run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund --package-lock - - run: npm audit + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Add Problem Matcher + run: echo "::add-matcher::.github/matchers/tap.json" + - name: Test + run: npm test --ignore-scripts -ws -iwr --if-present + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() + with: + token: \${{ secrets.GITHUB_TOKEN }} + conclusion: \${{ job.status }} + check_id: \${{ steps.check.outputs.check_id }} .github/workflows/ci.yml ======================================== @@ -203,8 +369,6 @@ name: CI on: workflow_dispatch: pull_request: - branches: - - '*' push: branches: - main @@ -215,28 +379,50 @@ on: jobs: lint: + name: Lint if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Lint + run: npm run lint --ignore-scripts + - name: Post Lint + run: npm run postlint --ignore-scripts test: + name: Test All - \${{ matrix.platform.name }} - Node \${{ matrix.node-version }} if: github.repository_owner == 'npm' strategy: fail-fast: false matrix: + platform: + - name: Linux + os: ubuntu-latest + shell: bash + - name: macOS + os: macos-latest + shell: bash + - name: Windows + os: windows-latest + shell: cmd node-version: - 14.17.0 - 14.x @@ -244,27 +430,22 @@ jobs: - 16.x - 18.0.0 - 18.x - platform: - - os: ubuntu-latest - shell: bash - - os: macos-latest - shell: bash - - os: windows-latest - shell: cmd runs-on: \${{ matrix.platform.os }} defaults: run: shell: \${{ matrix.platform.shell }} steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: \${{ matrix.node-version }} - - name: Update to workable npm (windows) + - name: Update Windows npm # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) run: | @@ -274,24 +455,26 @@ jobs: node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz cd .. rmdir /s /q package - - name: Update npm to 7 - # If we do test on npm 10 it needs npm7 + - name: Install npm@7 if: startsWith(matrix.node-version, '10.') run: npm i --prefer-online --no-fund --no-audit -g npm@7 - - name: Update npm to latest + - name: Install npm@latest if: \${{ !startsWith(matrix.node-version, '10.') }} run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: add tap problem matcher + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm test --ignore-scripts + - name: Test + run: npm test --ignore-scripts -iwr .github/workflows/codeql-analysis.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. -name: "CodeQL" +name: CodeQL on: push: @@ -299,7 +482,6 @@ on: - main - latest pull_request: - # The branches below must be a subset of the branches above branches: - main - latest @@ -315,76 +497,119 @@ jobs: actions: read contents: read security-events: write - - strategy: - fail-fast: false - matrix: - language: [ javascript ] - steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: - languages: \${{ matrix.language }} + languages: javascript - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 .github/workflows/post-dependabot.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. -name: Post Dependabot Actions +name: Post Dependabot on: pull_request -# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions permissions: contents: write jobs: - template-oss-apply: + template-oss: + name: template-oss if: github.repository_owner == 'npm' && github.actor == 'dependabot[bot]' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 with: ref: \${{ github.event.pull_request.head_ref }} - - name: Setup git user + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Dependabot metadata + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Fetch Dependabot Metadata id: metadata - uses: dependabot/fetch-metadata@v1.1.1 + uses: dependabot/fetch-metadata@v1 with: - github-token: "\${{ secrets.GITHUB_TOKEN }}" - - name: Apply @npmcli/template-oss changes and lint + github-token: \${{ secrets.GITHUB_TOKEN }} + + # Dependabot can update multiple directories so we output which directory + # it is acting on so we can run the command for the correct root or workspace + - name: Get Dependabot Directory if: contains(steps.metadata.outputs.dependency-names, '@npmcli/template-oss') + id: flags + run: | + if [[ "\${{ steps.metadata.outputs.directory }}" == "/" ]]; then + echo "::set-output name=workspace::-iwr" + else + echo "::set-output name=workspace::-w \${{ steps.metadata.outputs.directory }}" + fi + + - name: Apply Changes + if: steps.flags.outputs.workspace + id: apply + run: | + npm run template-oss-apply \${{ steps.flags.outputs.workspace }} + if [[ \`git status --porcelain\` ]]; then + echo "::set-output name=changes::true" + fi + + # This step will fail if template-oss has made any workflow updates. It is impossible + # for a workflow to update other workflows. In the case it does fail, we continue + # and then try to apply only a portion of the changes in the next step + - name: Push All Changes + if: steps.apply.outputs.changes + id: push + continue-on-error: true + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + run: | + git commit -am "chore: postinstall for dependabot template-oss PR" + git push + + - name: Push All Changes Except Workflows + if: steps.push.outcome == 'failure' env: GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} run: | - npm run template-oss-apply + git reset HEAD~ + git checkout HEAD -- .github/workflows/ + git clean -fd .github/workflows/ git commit -am "chore: postinstall for dependabot template-oss PR" git push - npm run lint + + - name: Check Changes + if: steps.apply.outputs.changes + run: | + npm exec --offline \${{ steps.flags.outputs.workspace }} -- template-oss-check .github/workflows/pull-request.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. -name: Pull Request Linting +name: Pull Request on: pull_request: @@ -395,37 +620,47 @@ on: - synchronize jobs: - check: - name: Check PR Title or Commits + commitlint: + name: Lint Commits if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 with: fetch-depth: 0 - - name: Setup git user + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Check commits or PR title - env: - PR_TITLE: \${{ github.event.pull_request.title }} + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Commitlint on Commits + id: commit + continue-on-error: true run: | - npx --offline commitlint -V --from origin/main --to \${{ github.event.pull_request.head.sha }} / - || echo $PR_TITLE | npx --offline commitlint -V + npx --offline commitlint -V --from origin/\${{ github.base_ref }} --to \${{ github.event.pull_request.head.sha }} + - name: Run Commitlint on PR Title + if: steps.commit.outcome == 'failure' + run: | + echo \${{ github.event.pull_request.title }} | npx --offline commitlint -V -.github/workflows/release-please.yml +.github/workflows/release.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. -name: Release Please +name: Release on: push: @@ -436,186 +671,227 @@ on: permissions: contents: write pull-requests: write + checks: write jobs: - release-please: + release: outputs: pr: \${{ steps.release.outputs.pr }} - release: \${{ steps.release.outputs.release }} + releases: \${{ steps.release.outputs.releases }} + release-flags: \${{ steps.release.outputs.release-flags }} + branch: \${{ steps.release.outputs.pr-branch }} + pr-number: \${{ steps.release.outputs.pr-number }} + comment-id: \${{ steps.pr-comment.outputs.result }} + check-id: \${{ steps.check.outputs.check_id }} + name: Release if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund - name: Release Please id: release - run: npx --offline template-oss-release-please \${{ github.ref_name }} env: GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} - - post-pr: - needs: release-please - if: needs.release-please.outputs.pr - runs-on: ubuntu-latest + run: | + npx --offline template-oss-release-please \${{ github.ref_name }} + - name: Post Pull Request Comment + if: steps.release.outputs.pr-number + uses: actions/github-script@v6 + id: pr-comment + env: + PR_NUMBER: \${{ steps.release.outputs.pr-number }} + with: + script: | + const repo = { owner: context.repo.owner, repo: context.repo.repo } + const issue = { ...repo, issue_number: process.env.PR_NUMBER } + + const { data: workflow } = await github.rest.actions.getWorkflowRun({ ...repo, run_id: context.runId }) + + let body = '## Release Manager/n/n' + + const comments = await github.paginate(github.rest.issues.listComments, issue) + let commentId = comments?.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id + + body += \`- Release workflow run: \${workflow.html_url}\` + if (commentId) { + await github.rest.issues.updateComment({ ...repo, comment_id: commentId, body }) + } else { + const { data: comment } = await github.rest.issues.createComment({ ...issue, body }) + commentId = comment?.id + } + + return commentId + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + if: steps.release.outputs.pr-number + with: + token: \${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Release + sha: \${{ steps.release.outputs.pr-sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + + update: + needs: release outputs: - ref: \${{ steps.ref.outputs.branch }} + sha: \${{ steps.commit.outputs.sha }} + check-id: \${{ steps.check.outputs.check_id }} + name: Update - Release + if: github.repository_owner == 'npm' && needs.release.outputs.pr + runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - name: Output ref - id: ref - run: echo "::set-output name=branch::\${{ fromJSON(needs.release-please.outputs.pr).headBranchName }}" - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 with: fetch-depth: 0 - ref: \${{ steps.ref.outputs.branch }} - - name: Setup git user + ref: \${{ needs.release.outputs.branch }} + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Post pull request actions + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Post Pull Request Actions env: + RELEASE_PR_NUMBER: \${{ needs.release.outputs.pr-number }} + RELEASE_COMMENT_ID: \${{ needs.release.outputs.comment-id }} GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} run: | - npm run rp-pull-request --ignore-scripts --if-present -ws -iwr - git commit -am "chore: post pull request" || true - git push - - release-test: - needs: post-pr - if: needs.post-pr.outputs.ref - uses: ./.github/workflows/release.yml - with: - ref: \${{ needs.post-pr.outputs.ref }} - - post-release: - needs: release-please - if: github.repository_owner == 'npm' && needs.release-please.outputs.release - runs-on: ubuntu-latest - steps: - - 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" - - uses: actions/setup-node@v3 - with: - node-version: 18.x - - name: Update npm to latest - run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Post release actions + npm run rp-pull-request --ignore-scripts -ws -iwr --if-present + - name: Commit + id: commit env: GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} run: | - npm run rp-release --ignore-scripts --if-present -ws -iwr - -.github/workflows/release.yml -======================================== -# This file is automatically added by @npmcli/template-oss. Do not edit. - -name: Release + git commit --all --amend --no-edit || true + git push --force-with-lease + echo "::set-output name=sha::$(git rev-parse HEAD)" + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check -on: - workflow_call: - inputs: - ref: - required: true - type: string + with: + token: \${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Release + sha: \${{ steps.commit.outputs.sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() + with: + token: \${{ secrets.GITHUB_TOKEN }} + conclusion: \${{ job.status }} + check_id: \${{ needs.release.outputs.check-id }} + + ci: + name: CI - Release + needs: [ release, update ] + if: needs.release.outputs.pr + uses: ./.github/workflows/ci-release.yml + with: + ref: \${{ needs.release.outputs.branch }} + check-sha: \${{ needs.update.outputs.sha }} -jobs: - lint-all: - if: github.repository_owner == 'npm' + post-ci: + needs: [ release, update, ci ] + name: Post CI - Release + if: github.repository_owner == 'npm' && needs.release.outputs.pr && always() runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - with: - ref: \${{ inputs.ref }} - - name: Setup git user + - name: Get Needs Result + id: needs-result run: | - git config --global user.email "npm-cli+bot@github.com" - git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + result="" + if [[ "\${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then + result="failure" + elif [[ "\${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + result="cancelled" + else + result="success" + fi + echo "::set-output name=result::$result" + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() with: - node-version: 18.x - - name: Update npm to latest - run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint -ws -iwr --if-present + token: \${{ secrets.GITHUB_TOKEN }} + conclusion: \${{ steps.needs-result.outputs.result }} + check_id: \${{ needs.update.outputs.check-id }} - test-all: - if: github.repository_owner == 'npm' - strategy: - fail-fast: false - matrix: - node-version: - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - - 18.0.0 - - 18.x - platform: - - os: ubuntu-latest - shell: bash - - os: macos-latest - shell: bash - - os: windows-latest - shell: cmd - runs-on: \${{ matrix.platform.os }} + post-release: + needs: release + name: Post Release - Release + if: github.repository_owner == 'npm' && needs.release.outputs.releases + runs-on: ubuntu-latest defaults: run: - shell: \${{ matrix.platform.shell }} + shell: bash steps: - - uses: actions/checkout@v3 - with: - ref: \${{ inputs.ref }} - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: - node-version: \${{ matrix.node-version }} - - name: Update to workable npm (windows) - # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows - if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) - run: | - curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz - tar xf npm-7.5.4.tgz - cd package - node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz - cd .. - rmdir /s /q package - - name: Update npm to 7 - # If we do test on npm 10 it needs npm7 - if: startsWith(matrix.node-version, '10.') - run: npm i --prefer-online --no-fund --no-audit -g npm@7 - - name: Update npm to latest - if: \${{ !startsWith(matrix.node-version, '10.') }} + node-version: 18.x + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: add tap problem matcher - run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm run test -ws -iwr --if-present + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Post Release Actions + env: + RELEASES: \${{ needs.release.outputs.releases }} + run: | + npm run rp-release --ignore-scripts --if-present \${{ join(fromJSON(needs.release.outputs.release-flags), ' ') }} .gitignore ======================================== @@ -797,8 +1073,8 @@ const localConfigs = readdir(__dirname) module.exports = { root: true, ignorePatterns: [ - 'workspaces/a', - 'workspaces/b', + 'workspaces/a/**', + 'workspaces/b/**', ], extends: [ '@npmcli', @@ -820,7 +1096,7 @@ version: 2 updates: - package-ecosystem: npm - directory: "/" + directory: / schedule: interval: daily allow: @@ -831,9 +1107,8 @@ updates: prefix-development: chore labels: - "Dependencies" - - package-ecosystem: npm - directory: "workspaces/a/" + directory: workspaces/a/ schedule: interval: daily allow: @@ -844,9 +1119,8 @@ updates: prefix-development: chore labels: - "Dependencies" - - package-ecosystem: npm - directory: "workspaces/b/" + directory: workspaces/b/ schedule: interval: daily allow: @@ -970,22 +1244,31 @@ on: jobs: audit: + name: Audit Dependencies if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund --package-lock - - run: npm audit + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund --package-lock + - name: Run Audit + run: npm audit .github/workflows/ci-a.yml ======================================== @@ -996,8 +1279,6 @@ name: CI - a on: workflow_dispatch: pull_request: - branches: - - '*' paths: - workspaces/a/** push: @@ -1012,28 +1293,50 @@ on: jobs: lint: + name: Lint if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint -w a + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Lint + run: npm run lint --ignore-scripts + - name: Post Lint + run: npm run postlint --ignore-scripts test: + name: Test All - \${{ matrix.platform.name }} - Node \${{ matrix.node-version }} if: github.repository_owner == 'npm' strategy: fail-fast: false matrix: + platform: + - name: Linux + os: ubuntu-latest + shell: bash + - name: macOS + os: macos-latest + shell: bash + - name: Windows + os: windows-latest + shell: cmd node-version: - 14.17.0 - 14.x @@ -1041,27 +1344,22 @@ jobs: - 16.x - 18.0.0 - 18.x - platform: - - os: ubuntu-latest - shell: bash - - os: macos-latest - shell: bash - - os: windows-latest - shell: cmd runs-on: \${{ matrix.platform.os }} defaults: run: shell: \${{ matrix.platform.shell }} steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: \${{ matrix.node-version }} - - name: Update to workable npm (windows) + - name: Update Windows npm # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) run: | @@ -1071,18 +1369,20 @@ jobs: node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz cd .. rmdir /s /q package - - name: Update npm to 7 - # If we do test on npm 10 it needs npm7 + - name: Install npm@7 if: startsWith(matrix.node-version, '10.') run: npm i --prefer-online --no-fund --no-audit -g npm@7 - - name: Update npm to latest + - name: Install npm@latest if: \${{ !startsWith(matrix.node-version, '10.') }} run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: add tap problem matcher + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm test --ignore-scripts -w a + - name: Test + run: npm test --ignore-scripts -w a .github/workflows/ci-b.yml ======================================== @@ -1093,8 +1393,6 @@ name: CI - b on: workflow_dispatch: pull_request: - branches: - - '*' paths: - workspaces/b/** push: @@ -1109,28 +1407,50 @@ on: jobs: lint: + name: Lint if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint -w b + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Lint + run: npm run lint --ignore-scripts + - name: Post Lint + run: npm run postlint --ignore-scripts test: + name: Test All - \${{ matrix.platform.name }} - Node \${{ matrix.node-version }} if: github.repository_owner == 'npm' strategy: fail-fast: false matrix: + platform: + - name: Linux + os: ubuntu-latest + shell: bash + - name: macOS + os: macos-latest + shell: bash + - name: Windows + os: windows-latest + shell: cmd node-version: - 14.17.0 - 14.x @@ -1138,27 +1458,22 @@ jobs: - 16.x - 18.0.0 - 18.x - platform: - - os: ubuntu-latest - shell: bash - - os: macos-latest - shell: bash - - os: windows-latest - shell: cmd runs-on: \${{ matrix.platform.os }} defaults: run: shell: \${{ matrix.platform.shell }} steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: \${{ matrix.node-version }} - - name: Update to workable npm (windows) + - name: Update Windows npm # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) run: | @@ -1168,68 +1483,107 @@ jobs: node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz cd .. rmdir /s /q package - - name: Update npm to 7 - # If we do test on npm 10 it needs npm7 + - name: Install npm@7 if: startsWith(matrix.node-version, '10.') run: npm i --prefer-online --no-fund --no-audit -g npm@7 - - name: Update npm to latest + - name: Install npm@latest if: \${{ !startsWith(matrix.node-version, '10.') }} run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: add tap problem matcher + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm test --ignore-scripts -w b + - name: Test + run: npm test --ignore-scripts -w b -.github/workflows/ci.yml +.github/workflows/ci-release.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. -name: CI +name: CI - Release on: - workflow_dispatch: - pull_request: - branches: - - '*' - paths-ignore: - - workspaces/a/** - - workspaces/b/** - push: - branches: - - main - - latest - paths-ignore: - - workspaces/a/** - - workspaces/b/** - schedule: - # "At 09:00 UTC (02:00 PT) on Monday" https://crontab.guru/#0_9_*_*_1 - - cron: "0 9 * * 1" + workflow_call: + inputs: + ref: + required: true + type: string + check-sha: + required: true + type: string jobs: - lint: + lint-all: + name: Lint All if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + + with: + token: \${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Lint All + sha: \${{ inputs.check-sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: \${{ inputs.ref }} + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Lint + run: npm run lint --ignore-scripts + - name: Post Lint + run: npm run postlint --ignore-scripts + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() + with: + token: \${{ secrets.GITHUB_TOKEN }} + conclusion: \${{ job.status }} + check_id: \${{ steps.check.outputs.check_id }} - test: + test-all: + name: Test All - \${{ matrix.platform.name }} - Node \${{ matrix.node-version }} if: github.repository_owner == 'npm' strategy: fail-fast: false matrix: + platform: + - name: Linux + os: ubuntu-latest + shell: bash + - name: macOS + os: macos-latest + shell: bash + - name: Windows + os: windows-latest + shell: cmd node-version: - 14.17.0 - 14.x @@ -1237,27 +1591,158 @@ jobs: - 16.x - 18.0.0 - 18.x + runs-on: \${{ matrix.platform.os }} + defaults: + run: + shell: \${{ matrix.platform.shell }} + steps: + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + + with: + token: \${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Test All - \${{ matrix.platform.name }} - Node \${{ matrix.node-version }} + sha: \${{ inputs.check-sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: \${{ inputs.ref }} + - 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: \${{ matrix.node-version }} + - name: Update Windows npm + # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows + if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) + run: | + curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz + tar xf npm-7.5.4.tgz + cd package + node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz + cd .. + rmdir /s /q package + - name: Install npm@7 + if: startsWith(matrix.node-version, '10.') + run: npm i --prefer-online --no-fund --no-audit -g npm@7 + - name: Install npm@latest + if: \${{ !startsWith(matrix.node-version, '10.') }} + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Add Problem Matcher + run: echo "::add-matcher::.github/matchers/tap.json" + - name: Test + run: npm test --ignore-scripts -ws -iwr --if-present + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() + with: + token: \${{ secrets.GITHUB_TOKEN }} + conclusion: \${{ job.status }} + check_id: \${{ steps.check.outputs.check_id }} + +.github/workflows/ci.yml +======================================== +# This file is automatically added by @npmcli/template-oss. Do not edit. + +name: CI + +on: + workflow_dispatch: + pull_request: + paths-ignore: + push: + branches: + - main + - latest + paths-ignore: + schedule: + # "At 09:00 UTC (02:00 PT) on Monday" https://crontab.guru/#0_9_*_*_1 + - cron: "0 9 * * 1" + +jobs: + lint: + name: Lint + 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 + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Lint + run: npm run lint --ignore-scripts + - name: Post Lint + run: npm run postlint --ignore-scripts + + test: + name: Test All - \${{ matrix.platform.name }} - Node \${{ matrix.node-version }} + if: github.repository_owner == 'npm' + strategy: + fail-fast: false + matrix: platform: - - os: ubuntu-latest + - name: Linux + os: ubuntu-latest shell: bash - - os: macos-latest + - name: macOS + os: macos-latest shell: bash - - os: windows-latest + - name: Windows + os: windows-latest shell: cmd + node-version: + - 14.17.0 + - 14.x + - 16.13.0 + - 16.x + - 18.0.0 + - 18.x runs-on: \${{ matrix.platform.os }} defaults: run: shell: \${{ matrix.platform.shell }} steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: \${{ matrix.node-version }} - - name: Update to workable npm (windows) + - name: Update Windows npm # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) run: | @@ -1267,24 +1752,26 @@ jobs: node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz cd .. rmdir /s /q package - - name: Update npm to 7 - # If we do test on npm 10 it needs npm7 + - name: Install npm@7 if: startsWith(matrix.node-version, '10.') run: npm i --prefer-online --no-fund --no-audit -g npm@7 - - name: Update npm to latest + - name: Install npm@latest if: \${{ !startsWith(matrix.node-version, '10.') }} run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: add tap problem matcher + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm test --ignore-scripts + - name: Test + run: npm test --ignore-scripts -iwr .github/workflows/codeql-analysis.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. -name: "CodeQL" +name: CodeQL on: push: @@ -1292,7 +1779,6 @@ on: - main - latest pull_request: - # The branches below must be a subset of the branches above branches: - main - latest @@ -1308,76 +1794,119 @@ jobs: actions: read contents: read security-events: write - - strategy: - fail-fast: false - matrix: - language: [ javascript ] - steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: - languages: \${{ matrix.language }} + languages: javascript - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 .github/workflows/post-dependabot.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. -name: Post Dependabot Actions +name: Post Dependabot on: pull_request -# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions permissions: contents: write jobs: - template-oss-apply: + template-oss: + name: template-oss if: github.repository_owner == 'npm' && github.actor == 'dependabot[bot]' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 with: ref: \${{ github.event.pull_request.head_ref }} - - name: Setup git user + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Dependabot metadata + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Fetch Dependabot Metadata id: metadata - uses: dependabot/fetch-metadata@v1.1.1 + uses: dependabot/fetch-metadata@v1 with: - github-token: "\${{ secrets.GITHUB_TOKEN }}" - - name: Apply @npmcli/template-oss changes and lint + github-token: \${{ secrets.GITHUB_TOKEN }} + + # Dependabot can update multiple directories so we output which directory + # it is acting on so we can run the command for the correct root or workspace + - name: Get Dependabot Directory if: contains(steps.metadata.outputs.dependency-names, '@npmcli/template-oss') + id: flags + run: | + if [[ "\${{ steps.metadata.outputs.directory }}" == "/" ]]; then + echo "::set-output name=workspace::-iwr" + else + echo "::set-output name=workspace::-w \${{ steps.metadata.outputs.directory }}" + fi + + - name: Apply Changes + if: steps.flags.outputs.workspace + id: apply + run: | + npm run template-oss-apply \${{ steps.flags.outputs.workspace }} + if [[ \`git status --porcelain\` ]]; then + echo "::set-output name=changes::true" + fi + + # This step will fail if template-oss has made any workflow updates. It is impossible + # for a workflow to update other workflows. In the case it does fail, we continue + # and then try to apply only a portion of the changes in the next step + - name: Push All Changes + if: steps.apply.outputs.changes + id: push + continue-on-error: true + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + run: | + git commit -am "chore: postinstall for dependabot template-oss PR" + git push + + - name: Push All Changes Except Workflows + if: steps.push.outcome == 'failure' env: GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} run: | - npm run template-oss-apply + git reset HEAD~ + git checkout HEAD -- .github/workflows/ + git clean -fd .github/workflows/ git commit -am "chore: postinstall for dependabot template-oss PR" git push - npm run lint + + - name: Check Changes + if: steps.apply.outputs.changes + run: | + npm exec --offline \${{ steps.flags.outputs.workspace }} -- template-oss-check .github/workflows/pull-request.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. -name: Pull Request Linting +name: Pull Request on: pull_request: @@ -1388,37 +1917,47 @@ on: - synchronize jobs: - check: - name: Check PR Title or Commits + commitlint: + name: Lint Commits if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 with: fetch-depth: 0 - - name: Setup git user + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Check commits or PR title - env: - PR_TITLE: \${{ github.event.pull_request.title }} + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Commitlint on Commits + id: commit + continue-on-error: true + run: | + npx --offline commitlint -V --from origin/\${{ github.base_ref }} --to \${{ github.event.pull_request.head.sha }} + - name: Run Commitlint on PR Title + if: steps.commit.outcome == 'failure' run: | - npx --offline commitlint -V --from origin/main --to \${{ github.event.pull_request.head.sha }} / - || echo $PR_TITLE | npx --offline commitlint -V + echo \${{ github.event.pull_request.title }} | npx --offline commitlint -V -.github/workflows/release-please.yml +.github/workflows/release.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. -name: Release Please +name: Release on: push: @@ -1429,186 +1968,227 @@ on: permissions: contents: write pull-requests: write + checks: write jobs: - release-please: + release: outputs: pr: \${{ steps.release.outputs.pr }} - release: \${{ steps.release.outputs.release }} + releases: \${{ steps.release.outputs.releases }} + release-flags: \${{ steps.release.outputs.release-flags }} + branch: \${{ steps.release.outputs.pr-branch }} + pr-number: \${{ steps.release.outputs.pr-number }} + comment-id: \${{ steps.pr-comment.outputs.result }} + check-id: \${{ steps.check.outputs.check_id }} + name: Release if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund - name: Release Please id: release - run: npx --offline template-oss-release-please \${{ github.ref_name }} env: GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} - - post-pr: - needs: release-please - if: needs.release-please.outputs.pr - runs-on: ubuntu-latest + run: | + npx --offline template-oss-release-please \${{ github.ref_name }} + - name: Post Pull Request Comment + if: steps.release.outputs.pr-number + uses: actions/github-script@v6 + id: pr-comment + env: + PR_NUMBER: \${{ steps.release.outputs.pr-number }} + with: + script: | + const repo = { owner: context.repo.owner, repo: context.repo.repo } + const issue = { ...repo, issue_number: process.env.PR_NUMBER } + + const { data: workflow } = await github.rest.actions.getWorkflowRun({ ...repo, run_id: context.runId }) + + let body = '## Release Manager/n/n' + + const comments = await github.paginate(github.rest.issues.listComments, issue) + let commentId = comments?.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id + + body += \`- Release workflow run: \${workflow.html_url}\` + if (commentId) { + await github.rest.issues.updateComment({ ...repo, comment_id: commentId, body }) + } else { + const { data: comment } = await github.rest.issues.createComment({ ...issue, body }) + commentId = comment?.id + } + + return commentId + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + if: steps.release.outputs.pr-number + with: + token: \${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Release + sha: \${{ steps.release.outputs.pr-sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + + update: + needs: release outputs: - ref: \${{ steps.ref.outputs.branch }} + sha: \${{ steps.commit.outputs.sha }} + check-id: \${{ steps.check.outputs.check_id }} + name: Update - Release + if: github.repository_owner == 'npm' && needs.release.outputs.pr + runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - name: Output ref - id: ref - run: echo "::set-output name=branch::\${{ fromJSON(needs.release-please.outputs.pr).headBranchName }}" - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 with: fetch-depth: 0 - ref: \${{ steps.ref.outputs.branch }} - - name: Setup git user + ref: \${{ needs.release.outputs.branch }} + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Post pull request actions + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Post Pull Request Actions env: + RELEASE_PR_NUMBER: \${{ needs.release.outputs.pr-number }} + RELEASE_COMMENT_ID: \${{ needs.release.outputs.comment-id }} GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} run: | - npm run rp-pull-request --ignore-scripts --if-present -ws -iwr - git commit -am "chore: post pull request" || true - git push - - release-test: - needs: post-pr - if: needs.post-pr.outputs.ref - uses: ./.github/workflows/release.yml - with: - ref: \${{ needs.post-pr.outputs.ref }} - - post-release: - needs: release-please - if: github.repository_owner == 'npm' && needs.release-please.outputs.release - runs-on: ubuntu-latest - steps: - - 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" - - uses: actions/setup-node@v3 - with: - node-version: 18.x - - name: Update npm to latest - run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Post release actions + npm run rp-pull-request --ignore-scripts -ws -iwr --if-present + - name: Commit + id: commit env: GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} run: | - npm run rp-release --ignore-scripts --if-present -ws -iwr - -.github/workflows/release.yml -======================================== -# This file is automatically added by @npmcli/template-oss. Do not edit. - -name: Release + git commit --all --amend --no-edit || true + git push --force-with-lease + echo "::set-output name=sha::$(git rev-parse HEAD)" + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check -on: - workflow_call: - inputs: - ref: - required: true - type: string + with: + token: \${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Release + sha: \${{ steps.commit.outputs.sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() + with: + token: \${{ secrets.GITHUB_TOKEN }} + conclusion: \${{ job.status }} + check_id: \${{ needs.release.outputs.check-id }} + + ci: + name: CI - Release + needs: [ release, update ] + if: needs.release.outputs.pr + uses: ./.github/workflows/ci-release.yml + with: + ref: \${{ needs.release.outputs.branch }} + check-sha: \${{ needs.update.outputs.sha }} -jobs: - lint-all: - if: github.repository_owner == 'npm' + post-ci: + needs: [ release, update, ci ] + name: Post CI - Release + if: github.repository_owner == 'npm' && needs.release.outputs.pr && always() runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - with: - ref: \${{ inputs.ref }} - - name: Setup git user + - name: Get Needs Result + id: needs-result run: | - git config --global user.email "npm-cli+bot@github.com" - git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + result="" + if [[ "\${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then + result="failure" + elif [[ "\${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + result="cancelled" + else + result="success" + fi + echo "::set-output name=result::$result" + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() with: - node-version: 18.x - - name: Update npm to latest - run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint -ws -iwr --if-present + token: \${{ secrets.GITHUB_TOKEN }} + conclusion: \${{ steps.needs-result.outputs.result }} + check_id: \${{ needs.update.outputs.check-id }} - test-all: - if: github.repository_owner == 'npm' - strategy: - fail-fast: false - matrix: - node-version: - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - - 18.0.0 - - 18.x - platform: - - os: ubuntu-latest - shell: bash - - os: macos-latest - shell: bash - - os: windows-latest - shell: cmd - runs-on: \${{ matrix.platform.os }} + post-release: + needs: release + name: Post Release - Release + if: github.repository_owner == 'npm' && needs.release.outputs.releases + runs-on: ubuntu-latest defaults: run: - shell: \${{ matrix.platform.shell }} + shell: bash steps: - - uses: actions/checkout@v3 - with: - ref: \${{ inputs.ref }} - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: - node-version: \${{ matrix.node-version }} - - name: Update to workable npm (windows) - # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows - if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) - run: | - curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz - tar xf npm-7.5.4.tgz - cd package - node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz - cd .. - rmdir /s /q package - - name: Update npm to 7 - # If we do test on npm 10 it needs npm7 - if: startsWith(matrix.node-version, '10.') - run: npm i --prefer-online --no-fund --no-audit -g npm@7 - - name: Update npm to latest - if: \${{ !startsWith(matrix.node-version, '10.') }} + node-version: 18.x + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: add tap problem matcher - run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm run test -ws -iwr --if-present + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Post Release Actions + env: + RELEASES: \${{ needs.release.outputs.releases }} + run: | + npm run rp-release --ignore-scripts --if-present \${{ join(fromJSON(needs.release.outputs.release-flags), ' ') }} .gitignore ======================================== @@ -1702,7 +2282,7 @@ package.json "version": "{{VERSION}}" }, "tap": { - "test-ignore": "^(workspaces/a|workspaces/b)/", + "test-ignore": "^(workspaces/a|workspaces/b)/**", "nyc-arg": [ "--exclude", "workspaces/a/**", @@ -1922,6 +2502,38 @@ workspaces/b/package.json ` exports[`test/apply/source-snapshots.js TAP workspaces only > expect resolving Promise 1`] = ` +.github/dependabot.yml +======================================== +# This file is automatically added by @npmcli/template-oss. Do not edit. + +version: 2 + +updates: + - package-ecosystem: npm + directory: workspaces/a/ + schedule: + interval: daily + allow: + - dependency-type: direct + versioning-strategy: increase-if-necessary + commit-message: + prefix: deps + prefix-development: chore + labels: + - "Dependencies" + - package-ecosystem: npm + directory: workspaces/b/ + schedule: + interval: daily + allow: + - dependency-type: direct + versioning-strategy: increase-if-necessary + commit-message: + prefix: deps + prefix-development: chore + labels: + - "Dependencies" + .github/matchers/tap.json ======================================== { @@ -1966,8 +2578,6 @@ name: CI - a on: workflow_dispatch: pull_request: - branches: - - '*' paths: - workspaces/a/** push: @@ -1982,28 +2592,50 @@ on: jobs: lint: + name: Lint if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint -w a + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Lint + run: npm run lint --ignore-scripts + - name: Post Lint + run: npm run postlint --ignore-scripts test: + name: Test All - \${{ matrix.platform.name }} - Node \${{ matrix.node-version }} if: github.repository_owner == 'npm' strategy: fail-fast: false matrix: + platform: + - name: Linux + os: ubuntu-latest + shell: bash + - name: macOS + os: macos-latest + shell: bash + - name: Windows + os: windows-latest + shell: cmd node-version: - 14.17.0 - 14.x @@ -2011,27 +2643,22 @@ jobs: - 16.x - 18.0.0 - 18.x - platform: - - os: ubuntu-latest - shell: bash - - os: macos-latest - shell: bash - - os: windows-latest - shell: cmd runs-on: \${{ matrix.platform.os }} defaults: run: shell: \${{ matrix.platform.shell }} steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: \${{ matrix.node-version }} - - name: Update to workable npm (windows) + - name: Update Windows npm # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) run: | @@ -2041,18 +2668,20 @@ jobs: node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz cd .. rmdir /s /q package - - name: Update npm to 7 - # If we do test on npm 10 it needs npm7 + - name: Install npm@7 if: startsWith(matrix.node-version, '10.') run: npm i --prefer-online --no-fund --no-audit -g npm@7 - - name: Update npm to latest + - name: Install npm@latest if: \${{ !startsWith(matrix.node-version, '10.') }} run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: add tap problem matcher + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm test --ignore-scripts -w a + - name: Test + run: npm test --ignore-scripts -w a .github/workflows/ci-b.yml ======================================== @@ -2063,8 +2692,6 @@ name: CI - b on: workflow_dispatch: pull_request: - branches: - - '*' paths: - workspaces/b/** push: @@ -2079,28 +2706,50 @@ on: jobs: lint: + name: Lint if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint -w b + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Lint + run: npm run lint --ignore-scripts + - name: Post Lint + run: npm run postlint --ignore-scripts test: + name: Test All - \${{ matrix.platform.name }} - Node \${{ matrix.node-version }} if: github.repository_owner == 'npm' strategy: fail-fast: false matrix: + platform: + - name: Linux + os: ubuntu-latest + shell: bash + - name: macOS + os: macos-latest + shell: bash + - name: Windows + os: windows-latest + shell: cmd node-version: - 14.17.0 - 14.x @@ -2108,27 +2757,22 @@ jobs: - 16.x - 18.0.0 - 18.x - platform: - - os: ubuntu-latest - shell: bash - - os: macos-latest - shell: bash - - os: windows-latest - shell: cmd runs-on: \${{ matrix.platform.os }} defaults: run: shell: \${{ matrix.platform.shell }} steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: \${{ matrix.node-version }} - - name: Update to workable npm (windows) + - name: Update Windows npm # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) run: | @@ -2138,123 +2782,271 @@ jobs: node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz cd .. rmdir /s /q package - - name: Update npm to 7 - # If we do test on npm 10 it needs npm7 + - name: Install npm@7 if: startsWith(matrix.node-version, '10.') run: npm i --prefer-online --no-fund --no-audit -g npm@7 - - name: Update npm to latest + - name: Install npm@latest if: \${{ !startsWith(matrix.node-version, '10.') }} run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: add tap problem matcher + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Add Problem Matcher run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm test --ignore-scripts -w b + - name: Test + run: npm test --ignore-scripts -w b -.github/workflows/release-please.yml +.github/workflows/ci-release.yml ======================================== # This file is automatically added by @npmcli/template-oss. Do not edit. -name: Release Please +name: CI - Release on: - push: - branches: - - main - - latest - -permissions: - contents: write - pull-requests: write + workflow_call: + inputs: + ref: + required: true + type: string + check-sha: + required: true + type: string jobs: - release-please: - outputs: - pr: \${{ steps.release.outputs.pr }} - release: \${{ steps.release.outputs.release }} + lint-all: + name: Lint All if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + + with: + token: \${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Lint All + sha: \${{ inputs.check-sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: \${{ inputs.ref }} + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Release Please - id: release - run: npx --offline template-oss-release-please \${{ github.ref_name }} - env: - GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Lint + run: npm run lint --ignore-scripts + - name: Post Lint + run: npm run postlint --ignore-scripts + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() + with: + token: \${{ secrets.GITHUB_TOKEN }} + conclusion: \${{ job.status }} + check_id: \${{ steps.check.outputs.check_id }} - post-pr: - needs: release-please - if: needs.release-please.outputs.pr - runs-on: ubuntu-latest - outputs: - ref: \${{ steps.ref.outputs.branch }} + test-all: + name: Test All - \${{ matrix.platform.name }} - Node \${{ matrix.node-version }} + if: github.repository_owner == 'npm' + strategy: + fail-fast: false + matrix: + platform: + - name: Linux + os: ubuntu-latest + shell: bash + - name: macOS + os: macos-latest + shell: bash + - name: Windows + os: windows-latest + shell: cmd + node-version: + - 14.17.0 + - 14.x + - 16.13.0 + - 16.x + - 18.0.0 + - 18.x + runs-on: \${{ matrix.platform.os }} + defaults: + run: + shell: \${{ matrix.platform.shell }} steps: - - name: Output ref - id: ref - run: echo "::set-output name=branch::\${{ fromJSON(needs.release-please.outputs.pr).headBranchName }}" - - uses: actions/checkout@v3 + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + with: - fetch-depth: 0 - ref: \${{ steps.ref.outputs.branch }} - - name: Setup git user + token: \${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Test All - \${{ matrix.platform.name }} - Node \${{ matrix.node-version }} + sha: \${{ inputs.check-sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: \${{ inputs.ref }} + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: - node-version: 18.x - - name: Update npm to latest - run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Post pull request actions - env: - GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + node-version: \${{ matrix.node-version }} + - name: Update Windows npm + # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows + if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) run: | - npm run rp-pull-request --ignore-scripts --if-present -ws -iwr - git commit -am "chore: post pull request" || true - git push + curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz + tar xf npm-7.5.4.tgz + cd package + node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz + cd .. + rmdir /s /q package + - name: Install npm@7 + if: startsWith(matrix.node-version, '10.') + run: npm i --prefer-online --no-fund --no-audit -g npm@7 + - name: Install npm@latest + if: \${{ !startsWith(matrix.node-version, '10.') }} + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Add Problem Matcher + run: echo "::add-matcher::.github/matchers/tap.json" + - name: Test + run: npm test --ignore-scripts -ws -iwr --if-present + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() + with: + token: \${{ secrets.GITHUB_TOKEN }} + conclusion: \${{ job.status }} + check_id: \${{ steps.check.outputs.check_id }} - release-test: - needs: post-pr - if: needs.post-pr.outputs.ref - uses: ./.github/workflows/release.yml - with: - ref: \${{ needs.post-pr.outputs.ref }} +.github/workflows/post-dependabot.yml +======================================== +# This file is automatically added by @npmcli/template-oss. Do not edit. - post-release: - needs: release-please - if: github.repository_owner == 'npm' && needs.release-please.outputs.release +name: Post Dependabot + +on: pull_request + +permissions: + contents: write + +jobs: + template-oss: + name: template-oss + if: github.repository_owner == 'npm' && github.actor == 'dependabot[bot]' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - name: Checkout + uses: actions/checkout@v3 + with: + ref: \${{ github.event.pull_request.head_ref }} + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: Post release actions + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Fetch Dependabot Metadata + id: metadata + uses: dependabot/fetch-metadata@v1 + with: + github-token: \${{ secrets.GITHUB_TOKEN }} + + # Dependabot can update multiple directories so we output which directory + # it is acting on so we can run the command for the correct root or workspace + - name: Get Dependabot Directory + if: contains(steps.metadata.outputs.dependency-names, '@npmcli/template-oss') + id: flags + run: | + if [[ "\${{ steps.metadata.outputs.directory }}" == "/" ]]; then + echo "::set-output name=workspace::-iwr" + else + echo "::set-output name=workspace::-w \${{ steps.metadata.outputs.directory }}" + fi + + - name: Apply Changes + if: steps.flags.outputs.workspace + id: apply + run: | + npm run template-oss-apply \${{ steps.flags.outputs.workspace }} + if [[ \`git status --porcelain\` ]]; then + echo "::set-output name=changes::true" + fi + + # This step will fail if template-oss has made any workflow updates. It is impossible + # for a workflow to update other workflows. In the case it does fail, we continue + # and then try to apply only a portion of the changes in the next step + - name: Push All Changes + if: steps.apply.outputs.changes + id: push + continue-on-error: true env: GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} run: | - npm run rp-release --ignore-scripts --if-present -ws -iwr + git commit -am "chore: postinstall for dependabot template-oss PR" + git push + + - name: Push All Changes Except Workflows + if: steps.push.outcome == 'failure' + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + run: | + git reset HEAD~ + git checkout HEAD -- .github/workflows/ + git clean -fd .github/workflows/ + git commit -am "chore: postinstall for dependabot template-oss PR" + git push + + - name: Check Changes + if: steps.apply.outputs.changes + run: | + npm exec --offline \${{ steps.flags.outputs.workspace }} -- template-oss-check .github/workflows/release.yml ======================================== @@ -2263,89 +3055,235 @@ jobs: name: Release on: - workflow_call: - inputs: - ref: - required: true - type: string + push: + branches: + - main + - latest + +permissions: + contents: write + pull-requests: write + checks: write jobs: - lint-all: + release: + outputs: + pr: \${{ steps.release.outputs.pr }} + releases: \${{ steps.release.outputs.releases }} + release-flags: \${{ steps.release.outputs.release-flags }} + branch: \${{ steps.release.outputs.pr-branch }} + pr-number: \${{ steps.release.outputs.pr-number }} + comment-id: \${{ steps.pr-comment.outputs.result }} + check-id: \${{ steps.check.outputs.check_id }} + name: Release if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 + - 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: - ref: \${{ inputs.ref }} - - name: Setup git user + node-version: 18.x + - name: Install npm@latest + run: npm i --prefer-online --no-fund --no-audit -g npm@latest + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Release Please + id: release + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + run: | + npx --offline template-oss-release-please \${{ github.ref_name }} + - name: Post Pull Request Comment + if: steps.release.outputs.pr-number + uses: actions/github-script@v6 + id: pr-comment + env: + PR_NUMBER: \${{ steps.release.outputs.pr-number }} + with: + script: | + const repo = { owner: context.repo.owner, repo: context.repo.repo } + const issue = { ...repo, issue_number: process.env.PR_NUMBER } + + const { data: workflow } = await github.rest.actions.getWorkflowRun({ ...repo, run_id: context.runId }) + + let body = '## Release Manager/n/n' + + const comments = await github.paginate(github.rest.issues.listComments, issue) + let commentId = comments?.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id + + body += \`- Release workflow run: \${workflow.html_url}\` + if (commentId) { + await github.rest.issues.updateComment({ ...repo, comment_id: commentId, body }) + } else { + const { data: comment } = await github.rest.issues.createComment({ ...issue, body }) + commentId = comment?.id + } + + return commentId + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check + if: steps.release.outputs.pr-number + with: + token: \${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Release + sha: \${{ steps.release.outputs.pr-sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + + update: + needs: release + outputs: + sha: \${{ steps.commit.outputs.sha }} + check-id: \${{ steps.check.outputs.check_id }} + name: Update - Release + if: github.repository_owner == 'npm' && needs.release.outputs.pr + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: \${{ needs.release.outputs.branch }} + - name: Setup Git User run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - run: npm run lint -ws -iwr --if-present + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Post Pull Request Actions + env: + RELEASE_PR_NUMBER: \${{ needs.release.outputs.pr-number }} + RELEASE_COMMENT_ID: \${{ needs.release.outputs.comment-id }} + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + run: | + npm run rp-pull-request --ignore-scripts -ws -iwr --if-present + - name: Commit + id: commit + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + run: | + git commit --all --amend --no-edit || true + git push --force-with-lease + echo "::set-output name=sha::$(git rev-parse HEAD)" + - name: Create Check + uses: LouisBrunner/checks-action@v1.3.1 + id: check - test-all: - if: github.repository_owner == 'npm' - strategy: - fail-fast: false - matrix: - node-version: - - 14.17.0 - - 14.x - - 16.13.0 - - 16.x - - 18.0.0 - - 18.x - platform: - - os: ubuntu-latest - shell: bash - - os: macos-latest - shell: bash - - os: windows-latest - shell: cmd - runs-on: \${{ matrix.platform.os }} + with: + token: \${{ secrets.GITHUB_TOKEN }} + status: in_progress + name: Release + sha: \${{ steps.commit.outputs.sha }} + # XXX: this does not work when using the default GITHUB_TOKEN. + # Instead we post the main job url to the PR as a comment which + # will link to all the other checks. To work around this we would + # need to create a GitHub that would create on-demand tokens. + # https://github.com/LouisBrunner/checks-action/issues/18 + # details_url: + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() + with: + token: \${{ secrets.GITHUB_TOKEN }} + conclusion: \${{ job.status }} + check_id: \${{ needs.release.outputs.check-id }} + + ci: + name: CI - Release + needs: [ release, update ] + if: needs.release.outputs.pr + uses: ./.github/workflows/ci-release.yml + with: + ref: \${{ needs.release.outputs.branch }} + check-sha: \${{ needs.update.outputs.sha }} + + post-ci: + needs: [ release, update, ci ] + name: Post CI - Release + if: github.repository_owner == 'npm' && needs.release.outputs.pr && always() + runs-on: ubuntu-latest defaults: run: - shell: \${{ matrix.platform.shell }} + shell: bash steps: - - uses: actions/checkout@v3 + - name: Get Needs Result + id: needs-result + run: | + result="" + if [[ "\${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then + result="failure" + elif [[ "\${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + result="cancelled" + else + result="success" + fi + echo "::set-output name=result::$result" + - name: Conclude Check + uses: LouisBrunner/checks-action@v1.3.1 + if: always() with: - ref: \${{ inputs.ref }} - - name: Setup git user + token: \${{ secrets.GITHUB_TOKEN }} + conclusion: \${{ steps.needs-result.outputs.result }} + check_id: \${{ needs.update.outputs.check-id }} + + post-release: + needs: release + name: Post Release - Release + if: github.repository_owner == 'npm' && needs.release.outputs.releases + 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: - node-version: \${{ matrix.node-version }} - - name: Update to workable npm (windows) - # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows - if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) - run: | - curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz - tar xf npm-7.5.4.tgz - cd package - node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz - cd .. - rmdir /s /q package - - name: Update npm to 7 - # If we do test on npm 10 it needs npm7 - if: startsWith(matrix.node-version, '10.') - run: npm i --prefer-online --no-fund --no-audit -g npm@7 - - name: Update npm to latest - if: \${{ !startsWith(matrix.node-version, '10.') }} + node-version: 18.x + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund - - name: add tap problem matcher - run: echo "::add-matcher::.github/matchers/tap.json" - - run: npm run test -ws -iwr --if-present + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund + - name: Run Post Release Actions + env: + RELEASES: \${{ needs.release.outputs.releases }} + run: | + npm run rp-release --ignore-scripts --if-present \${{ join(fromJSON(needs.release.outputs.release-flags), ' ') }} .release-please-manifest.json ======================================== diff --git a/tap-snapshots/test/check/diff-snapshots.js.test.cjs b/tap-snapshots/test/check/diff-snapshots.js.test.cjs index ee05b1bb..64499514 100644 --- a/tap-snapshots/test/check/diff-snapshots.js.test.cjs +++ b/tap-snapshots/test/check/diff-snapshots.js.test.cjs @@ -97,7 +97,31 @@ The repo file audit.yml needs to be updated: .github/workflows/audit.yml ======================================== [@npmcli/template-oss ERROR] There was an erroring getting the target file - [@npmcli/template-oss ERROR] Error: Document with errors cannot be stringified + [@npmcli/template-oss ERROR] Error: {{ROOT}}/test/check/tap-testdir-diff-snapshots-update-and-remove-errors/.github/workflows/audit.yml + + YAMLParseError: Implicit keys need to be on a single line at line 38, column 1: + + run: npm audit + >>>>I HOPE THIS IS NOT VALID YAML<<<<<<<<<<< + ^ + + YAMLParseError: Block scalar header includes extra characters: >>>>I at line 38, column 2: + + >>>>I HOPE THIS IS NOT VALID YAML<<<<<<<<<<< + ^ + + YAMLParseError: Not a YAML token: HOPE THIS IS NOT VALID YAML<<<<<<<<<<< at line 38, column 7: + + >>>>I HOPE THIS IS NOT VALID YAML<<<<<<<<<<< + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + YAMLParseError: Implicit map keys need to be followed by map values at line 38, column 1: + + run: npm audit + >>>>I HOPE THIS IS NOT VALID YAML<<<<<<<<<<< + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Document with errors cannot be stringified [@npmcli/template-oss ERROR] It will be overwritten with the following source: ---------------------------------------- # This file is automatically added by @npmcli/template-oss. Do not edit. @@ -112,22 +136,31 @@ The repo file audit.yml needs to be updated: jobs: audit: + name: Audit Dependencies if: github.repository_owner == 'npm' runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v3 - - name: Setup git user + - 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" - - uses: actions/setup-node@v3 + - name: Setup Node + uses: actions/setup-node@v3 with: node-version: 18.x - - name: Update npm to latest + - name: Install npm@latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - run: npm -v - - run: npm i --ignore-scripts --no-audit --no-fund --package-lock - - run: npm audit + - name: npm Version + run: npm -v + - name: Install Dependencies + run: npm i --ignore-scripts --no-audit --no-fund --package-lock + - name: Run Audit + run: npm audit To correct it: npx template-oss-apply --force @@ -138,12 +171,12 @@ The repo file ci.yml needs to be updated: .github/workflows/ci.yml ======================================== - @@ -67,4 +67,24 @@ - with: + @@ -83,5 +83,25 @@ node-version: \${{ matrix.node-version }} - - name: Update to workable npm (windows) + - name: Update Windows npm # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows - + if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) + if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) + - run: "" + run: | + curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz + tar xf npm-7.5.4.tgz @@ -151,18 +184,20 @@ The repo file ci.yml needs to be updated: + node lib/npm.js install --no-fund --no-audit -g ../npm-7.5.4.tgz + cd .. + rmdir /s /q package - + - name: Update npm to 7 - + # If we do test on npm 10 it needs npm7 + + - name: Install npm@7 + if: startsWith(matrix.node-version, '10.') + run: npm i --prefer-online --no-fund --no-audit -g npm@7 - + - name: Update npm to latest + + - name: Install npm@latest + if: \${{ !startsWith(matrix.node-version, '10.') }} + run: npm i --prefer-online --no-fund --no-audit -g npm@latest - + - run: npm -v - + - run: npm i --ignore-scripts --no-audit --no-fund - + - name: add tap problem matcher + + - name: npm Version + + run: npm -v + + - name: Install Dependencies + + run: npm i --ignore-scripts --no-audit --no-fund + + - name: Add Problem Matcher + run: echo "::add-matcher::.github/matchers/tap.json" - + - run: npm test --ignore-scripts + + - name: Test + + run: npm test --ignore-scripts -iwr To correct it: npx template-oss-apply --force diff --git a/tap-snapshots/test/check/snapshots.js.test.cjs b/tap-snapshots/test/check/snapshots.js.test.cjs index 415ef453..961d7173 100644 --- a/tap-snapshots/test/check/snapshots.js.test.cjs +++ b/tap-snapshots/test/check/snapshots.js.test.cjs @@ -36,11 +36,11 @@ The following repo files need to be added: .github/ISSUE_TEMPLATE/config.yml .github/matchers/tap.json .github/workflows/audit.yml + .github/workflows/ci-release.yml .github/workflows/ci.yml .github/workflows/codeql-analysis.yml .github/workflows/post-dependabot.yml .github/workflows/pull-request.yml - .github/workflows/release-please.yml .github/workflows/release.yml .release-please-manifest.json release-please-config.json @@ -283,11 +283,11 @@ The following repo files need to be added: .github/ISSUE_TEMPLATE/config.yml .github/matchers/tap.json .github/workflows/audit.yml + .github/workflows/ci-release.yml .github/workflows/ci.yml .github/workflows/codeql-analysis.yml .github/workflows/post-dependabot.yml .github/workflows/pull-request.yml - .github/workflows/release-please.yml .github/workflows/release.yml .release-please-manifest.json release-please-config.json @@ -332,7 +332,7 @@ The module file package.json needs to be updated: "lint-all": "npm run lint -ws -iwr --if-present" } "tap" is missing, expected { - "test-ignore": "^(workspaces/a|workspaces/b)/", + "test-ignore": "^(workspaces/a|workspaces/b)/**", "nyc-arg": [ "--exclude", "workspaces/a/**", @@ -363,9 +363,11 @@ To correct it: npm rm @npmcli/template-oss @npmcli/eslint-config tap && npm i @n The following repo files need to be added: + .github/dependabot.yml .github/matchers/tap.json .github/workflows/ci-name-aaaa.yml - .github/workflows/release-please.yml + .github/workflows/ci-release.yml + .github/workflows/post-dependabot.yml .github/workflows/release.yml .release-please-manifest.json release-please-config.json @@ -431,9 +433,11 @@ To correct it: npm rm @npmcli/template-oss @npmcli/eslint-config tap && npm i @n The following repo files need to be added: + .github/dependabot.yml .github/matchers/tap.json .github/workflows/ci-bbb.yml - .github/workflows/release-please.yml + .github/workflows/ci-release.yml + .github/workflows/post-dependabot.yml .github/workflows/release.yml .release-please-manifest.json release-please-config.json diff --git a/test/apply/index.js b/test/apply/index.js index 77cbf2f1..e7c24ec0 100644 --- a/test/apply/index.js +++ b/test/apply/index.js @@ -219,7 +219,7 @@ t.test('content can override partials', async (t) => { }, testdir: { content_dir: { - '_setup-deps.yml': '- run: INSTALL\n', + '_step-deps.yml': '- run: INSTALL\n', }, }, }) @@ -240,7 +240,7 @@ t.test('content can extend files', async (t) => { content_dir: { // eslint-disable-next-line max-len 'index.js': 'module.exports={rootRepo:{add:{".github/workflows/release.yml": "release.yml"}}}', - 'release.yml': '{{> release}}\n smoke-publish:\n runs-on: ubuntu-latest', + 'release.yml': '{{> ciRelease}}\n smoke-publish:\n runs-on: ubuntu-latest', }, }, }) @@ -269,13 +269,13 @@ t.test('config via multiple locations', async (t) => { }, testdir: { 'root-content': { - root: '{{defaultBranch}}-{{a}}-{{b}}-{{c}}', + root: '{{rootNpmPath}}-{{a}}-{{b}}-{{c}}', 'index.js': 'module.exports={rootRepo:{add:{"root.txt":"root"}},c:"root-c"}', }, workspaces: { a: { 'ws-content': { - ws: '{{defaultBranch}}-{{a}}-{{b}}-{{c}}', + ws: '{{rootNpmPath}}-{{a}}-{{b}}-{{c}}', 'index.js': 'module.exports={workspaceRepo:{add:{"ws.txt":"ws"}},c:"ws-c"}', }, }, @@ -287,8 +287,8 @@ t.test('config via multiple locations', async (t) => { const root = await s.readFile('root.txt') const ws = await s.readFile(join('ws.txt')) - t.equal(root.split('\n').slice(-1)[0], 'main-root-a-root-b-root-c') - t.equal(ws.split('\n').slice(-1)[0], 'main-ws-a-ws-b-ws-c') + t.equal(root.split('\n').slice(-1)[0], 'npm-root-a-root-b-root-c') + t.equal(ws.split('\n').slice(-1)[0], 'npm-ws-a-ws-b-ws-c') }) t.test('private workspace', async (t) => { @@ -329,7 +329,7 @@ t.test('private workspace', async (t) => { t.notOk(rpConfig.packages['workspaces/a']) const rp = s.join('.github', 'workflows') - t.ok(fs.existsSync(join(rp, 'release-please.yml'))) + t.ok(fs.existsSync(join(rp, 'release.yml'))) t.notOk(fs.existsSync(join(rp, 'release-please-b.yml'))) t.notOk(fs.existsSync(join(rp, 'release-please-a.yml'))) }) diff --git a/test/apply/merge-yml.js b/test/apply/merge-yml.js new file mode 100644 index 00000000..1303353f --- /dev/null +++ b/test/apply/merge-yml.js @@ -0,0 +1,117 @@ +const t = require('tap') +const { join } = require('path') +const yaml = require('yaml') +const setup = require('../setup.js') + +const toYml = (data) => new yaml.Document(data).toString() + +t.test('json merge', async (t) => { + const s = await setup(t, { + package: { + templateOSS: { + content: 'content', + defaultContent: false, + }, + }, + testdir: { + 'target.yml': toYml({ + existing: 'header', + key: [ + { id: 1, a: 1 }, + { id: 2, a: 2 }, + { noid: 1 }, + ], + }), + content: { + 'index.js': await setup.fixture('yml-merge.js'), + 'source.yml': toYml({ + new: 'header', + key: [ + { id: 1, b: 1 }, + { id: 2, b: 2 }, + { id: 3, b: 3 }, + ], + }), + }, + }, + }) + + await s.apply() + + t.strictSame(yaml.parse(await s.readFile('target.yml')), { + existing: 'header', + key: [ + { id: 1, b: 1 }, + { id: 2, b: 2 }, + { noid: 1 }, + { id: 3, b: 3 }, + ], + }) +}) + +t.test('dependabot', async t => { + t.test('root', async (t) => { + const s = await setup(t, { + ok: true, + }) + await s.apply() + + const dependabot = await s.readFile(join('.github', 'dependabot.yml')) + + t.match(dependabot, 'directory: /') + t.notMatch(dependabot, /directory: workspaces/) + + t.same(await s.check(), []) + await s.apply() + await s.apply() + await s.apply() + t.same(await s.check(), []) + }) + + t.test('root + workspaces', async (t) => { + const s = await setup(t, { + ok: true, + workspaces: { a: 'a', b: 'b', c: 'c' }, + }) + await s.apply() + + const dependabot = await s.readFile(join('.github', 'dependabot.yml')) + + t.match(dependabot, 'directory: /') + t.match(dependabot, 'directory: workspaces/a/') + t.match(dependabot, 'directory: workspaces/b/') + t.match(dependabot, 'directory: workspaces/c/') + + t.same(await s.check(), []) + await s.apply() + await s.apply() + await s.apply() + t.same(await s.check(), []) + }) + + t.test('workspaces only', async (t) => { + const s = await setup(t, { + ok: true, + package: { + templateOSS: { + rootRepo: false, + }, + }, + workspaces: { a: 'a', b: 'b', c: 'c' }, + }) + await s.apply() + + const dependabot = await s.readFile(join('.github', 'dependabot.yml')) + + t.notMatch(dependabot, /directory: \//) + t.match(dependabot, 'directory: workspaces/a/') + t.match(dependabot, 'directory: workspaces/b/') + t.match(dependabot, 'directory: workspaces/c/') + + t.same(await s.check(), []) + await s.apply() + await s.apply() + await s.apply() + t.same(await s.check(), []) + }) +}) diff --git a/test/fixtures/yml-merge.js b/test/fixtures/yml-merge.js new file mode 100644 index 00000000..3b9edd9d --- /dev/null +++ b/test/fixtures/yml-merge.js @@ -0,0 +1,13 @@ +module.exports = { + rootRepo: { + add: { + 'target.yml': { + file: 'source.yml', + parser: (p) => class extends p.YmlMerge { + key = 'key' + id = 'id' + }, + }, + }, + }, +} diff --git a/test/index.js b/test/index.js index f7cec74a..5563ad00 100644 --- a/test/index.js +++ b/test/index.js @@ -12,6 +12,15 @@ t.test('apply and check multiple is ok', async (t) => { t.same(await s.runAll(), []) }) +t.test('apply and check workspaces are ok', async (t) => { + const s = await setup(t, { + ok: true, + workspaces: { a: 'a', b: 'b', c: 'c' }, + }) + t.same(await s.runAll(), []) + t.same(await s.runAll(), []) +}) + t.test('empty content is ok', async (t) => { const s = await setup(t, { package: { diff --git a/test/setup.js b/test/setup.js index c36ebb5b..88938dbb 100644 --- a/test/setup.js +++ b/test/setup.js @@ -165,6 +165,7 @@ const setupGit = async (...args) => { } const cleanSnapshot = (str) => str + .replace(resolve(), '{{ROOT}}') .replace(/\\+/g, '/') .replace(/\r\n/g, '\n') .replace(new RegExp(`("version": "|${esc(NAME)}@)${esc(VERSION)}`, 'g'), '$1{{VERSION}}')