From 3944f5deaa3ad03b44ecebc50954d062e0feeac6 Mon Sep 17 00:00:00 2001 From: Alan Greene Date: Wed, 18 Dec 2024 15:01:36 +0000 Subject: [PATCH] Add workflow to publish npm packages Add workflow to handle automatically publishing packages to the npm registry when the commit message matches the expected format: `Publish of the @tektoncd/dashboard-* packages` For PRs it validates the PR is up-to-date with the base branch and that the PR title and commit message match. For both PRs and pushes it validates that the version in the commit message matches the version in the package.json files. Once all validation passes, it will publish the package (dry-run for PR). This simplifies the process of releasing new package versions as now it only requires running the `npm version --workspaces ` command and committing the result. The rest of the process, i.e. ensuring inter-workspace dependencies are updated to use the correct versions before publishing, is handled by the workflow. Also generate provenance statements for the packages. Skip all steps if commit message or PR title don't match expected format so the job passes and doesn't block unrelated PRs for dependency updates etc. --- .github/workflows/publish.yml | 134 +++++++++++++++++++++++++++++++ package-lock.json | 13 --- packages/components/package.json | 2 +- packages/e2e/package-lock.json | 4 +- packages/e2e/package.json | 2 +- packages/graph/package.json | 2 +- tekton/README.md | 14 ++-- 7 files changed, 147 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 000000000..1088d64ac --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,134 @@ +name: Publish NPM packages + +permissions: + contents: read + +on: + pull_request: + branches: ["main"] + paths-ignore: + - "**" + - "!**/package.json" + - "!**/package-lock.json" + types: + - opened + - reopened + - synchronize + push: + branches: ["main"] + paths-ignore: + - "**" + - "!**/package.json" + - "!**/package-lock.json" + +defaults: + run: + shell: bash + +jobs: + publish: + runs-on: ubuntu-24.04 + permissions: + contents: read + # required for npm package provenance + id-token: write + steps: + - name: Check for publish commit + id: checkPublishCommit + if: >- + ${{ + ( + github.event_name == 'pull_request' && + startsWith(github.event.pull_request.title, 'Publish v') && + endsWith(github.event.pull_request.title, 'of the @tektoncd/dashboard-* packages') + ) || + ( + github.event_name == 'push' && + startsWith(github.event.head_commit.message, 'Publish v') && + endsWith(github.event.head_commit.message, 'of the @tektoncd/dashboard-* packages') + ) + }} + run: | + echo "Confirmed it's a publish commit" + - name: Harden Runner + if: ${{ steps.checkPublishCommit.outcome == 'success' }} + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 + with: + egress-policy: audit + - name: Checkout + if: ${{ steps.checkPublishCommit.outcome == 'success' }} + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + # for PRs checkout the head rather than the merge commit so we can get the original commit message + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: Validate PR title and commit message match + if: ${{ steps.checkPublishCommit.outcome == 'success' && github.event_name == 'pull_request' }} + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + COMMIT_MESSAGE="$(git log --pretty=%s -n 1)" + if [ "$PR_TITLE" != "$COMMIT_MESSAGE" ]; then + echo "::error::PR title and commit message mismatch" + echo "Expected format: Publish of the @tektoncd/dashboard-* packages" + echo "PR_TITLE: $PR_TITLE" + echo "COMMIT_MESSAGE: $COMMIT_MESSAGE" + exit 1 + else + echo "PR title and commit message match, continuing…" + fi + - name: Get version + id: get-version + if: ${{ steps.checkPublishCommit.outcome == 'success' }} + env: + MESSAGE_WITH_VERSION: ${{ github.event.pull_request.title || github.event.head_commit.message }} + run: | + echo "Extracting version from commit message" + VERSION=$(echo "$MESSAGE_WITH_VERSION" | grep -Po '(v\d+\.\d+\.\d+(\S)*)') + echo "VERSION: $VERSION" + echo "newPackageVersion=${VERSION}" >> $GITHUB_OUTPUT + - name: Check version matches package.json + if: ${{ steps.checkPublishCommit.outcome == 'success' }} + run: | + EXPECTED_VERSION="${{ steps.get-version.outputs.newPackageVersion }}" + mismatch=false + for packageJson in ./packages/*/package.json; do + VERSION="v$(jq -r .version $packageJson)" + PRIVATE="$(jq -r .private $packageJson)" + if [ "$PRIVATE" == "false" ] && [ "$VERSION" != "$EXPECTED_VERSION" ]; then + echo "::error::Version mismatch found in $packageJson: ${VERSION}" + mismatch=true + fi + done + if [ "$mismatch" == "true" ]; then + exit 1 + fi + - name: Check PR is up-to-date + if: ${{ steps.checkPublishCommit.outcome == 'success' && github.event_name == 'pull_request' }} + env: + GH_TOKEN: ${{ github.token }} + run: | + BASE_REF="${{github.event.pull_request.base.repo.owner.login}}:${{github.event.pull_request.base.ref}}" + HEAD_REF="${{github.event.pull_request.head.repo.owner.login}}:${{github.event.pull_request.head.ref}}" + STATUS=$(gh api \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/${{ github.repository }}/compare/${BASE_REF}...${HEAD_REF} | jq -r .status) + if [ "$STATUS" != "ahead" ]; then + echo "::error::Pull request not up-to-date with base branch, please rebase" + exit 1 + else + echo "Pull request is up-to-date with base branch, continuing…" + fi + - name: Setup Node.js + if: ${{ steps.checkPublishCommit.outcome == 'success' }} + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + with: + node-version-file: .nvmrc + - name: Publish dry run + if: ${{ steps.checkPublishCommit.outcome == 'success' && github.event_name == 'pull_request' }} + run: npm publish --workspaces --provenance --access public --dry-run + - name: Publish + if: ${{ steps.checkPublishCommit.outcome == 'success' && github.event_name == 'push' }} + run: npm publish --workspaces --provenance --access public + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} diff --git a/package-lock.json b/package-lock.json index 19d398e4f..79a0f2e9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12145,19 +12145,6 @@ "react-router-dom": "^5.0.0 || ^6.0.0" } }, - "packages/e2e": { - "name": "@tektoncd/dashboard-e2e", - "version": "0.52.0-alpha.1", - "extraneous": true, - "license": "Apache-2.0", - "devDependencies": { - "cypress": "13.16.0" - }, - "engines": { - "node": "^20.18.0", - "npm": "^10.8.2" - } - }, "packages/graph": { "name": "@tektoncd/dashboard-graph", "version": "0.54.0-alpha.0", diff --git a/packages/components/package.json b/packages/components/package.json index fb1365979..acab8152f 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -15,7 +15,7 @@ "main": "./src/components/index.js", "type": "module", "scripts": { - "version": "npm pkg set \"dependencies.@tektoncd/dashboard-utils=$npm_new_version\"", + "prepublishOnly": "npm pkg set \"dependencies.@tektoncd/dashboard-utils=$npm_package_version\"", "postpublish": "npm pkg set \"dependencies.@tektoncd/dashboard-utils=file:../utils\"" }, "dependencies": { diff --git a/packages/e2e/package-lock.json b/packages/e2e/package-lock.json index 6d15a5063..334367203 100644 --- a/packages/e2e/package-lock.json +++ b/packages/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tektoncd/dashboard-e2e", - "version": "0.52.0-alpha.1", + "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tektoncd/dashboard-e2e", - "version": "0.52.0-alpha.1", + "version": "0.0.0", "license": "Apache-2.0", "devDependencies": { "cypress": "^13.16.0" diff --git a/packages/e2e/package.json b/packages/e2e/package.json index cb503548c..87c20241a 100644 --- a/packages/e2e/package.json +++ b/packages/e2e/package.json @@ -1,6 +1,6 @@ { "name": "@tektoncd/dashboard-e2e", - "version": "0.52.0-alpha.1", + "version": "0.0.0", "author": { "name": "The Tekton Authors" }, diff --git a/packages/graph/package.json b/packages/graph/package.json index 46fd09c78..d27fab1ae 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -17,7 +17,7 @@ "main": "./src/index.js", "type": "module", "scripts": { - "version": "npm pkg set \"dependencies.@tektoncd/dashboard-utils=$npm_new_version\"", + "prepublishOnly": "npm pkg set \"dependencies.@tektoncd/dashboard-utils=$npm_package_version\"", "postpublish": "npm pkg set \"dependencies.@tektoncd/dashboard-utils=file:../utils\"" }, "dependencies": { diff --git a/tekton/README.md b/tekton/README.md index c45f87fb0..223196480 100644 --- a/tekton/README.md +++ b/tekton/README.md @@ -162,13 +162,15 @@ To update the Dashboard release on the [`dogfooding` cluster](https://dashboard. To release a new version of the npm packages, e.g. `@tektoncd/dashboard-components`: -1. ensure you have the relevant commit checked out and that you're at the root of the project +1. ensure you have checked out the latest change and that you're at the root of the project 1. `npm --workspaces version ` where version is a valid semver string, e.g. `0.24.1-alpha.0` - Note: On Windows set the npm script-shell to git-bash, e.g.: `npm config set script-shell "C:\\Program Files\\Git\\bin\\bash.exe"` -1. `npm --workspaces publish --otp ` -1. once the packages are published run `npm install` -1. stage and commit the changes to the package.json and package-lock.json files and open a new PR to record the release -1. build and publish the Storybook: - 1. `npm run storybook:build` +1. commit the change and open a PR + - Note: both the PR title and commit message should use the following format: + > `Publish v of the @tektoncd/dashboard-* packages` +  + e.g. `Publish v0.24.1-alpha.0 of the @tektoncd/dashboard-* packages` +1. once the packages are published, build and publish the Storybook: + 1. `npm run storybook:build` - optional, deploys to your fork (origin) 1. `npm run storybook:deploy -- --remote upstream` 1. verify that the updated version is available at https://tektoncd.github.io/dashboard/