From 3f023489c3be3e25ca0aa63547da81abcb2fc04c 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 releases 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. --- .github/workflows/publish.yml | 117 +++++++++++++++++++++++++++++++ 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 +- 6 files changed, 122 insertions(+), 18 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..e406b655d --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,117 @@ +name: Publish NPM packages + +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: + 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') + ) + }} + runs-on: ubuntu-24.04 + permissions: + contents: read + # required for npm package provenance + id-token: write + steps: + - name: Harden Runner + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 + with: + egress-policy: audit + - 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: ${{ github.event_name == 'pull_request' }} + run: | + PR_TITLE="${{ github.event.pull_request.title }}" + 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 + run: | + echo "Extracting version from commit message" + VERSION=$(echo ${{ github.event.pull_request.title || github.event.head_commit.message }} | grep -Po '(v\d+\.\d+\.\d+(\S)*)') + echo "VERSION: $VERSION" + echo "newPackageVersion=${VERSION}" >> $GITHUB_OUTPUT + - name: Check version matches package.json + 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)" + 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: ${{ 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 + - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + with: + node-version-file: .nvmrc + - name: Publish dry run + if: ${{ github.event_name == 'pull_request' }} + run: npm publish --workspaces --provenance --access public --dry-run + - name: Publish + if: ${{ 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 1f727f4ec..10757d430 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11950,19 +11950,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.52.0-alpha.1", diff --git a/packages/components/package.json b/packages/components/package.json index e7a8884c7..368806e5b 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 b132db162..28329b6b7 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": {