From 2c2984451b97949cbc1be9d739f106b4d1d5c461 Mon Sep 17 00:00:00 2001 From: Vladimir Jimenez Date: Tue, 23 Jun 2026 20:58:08 -0700 Subject: [PATCH 1/5] (fix) release workflow to use Trusted Publishers --- .github/workflows/release.yml | 164 +++++++++++++++++----------------- 1 file changed, 80 insertions(+), 84 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 689d33c7e1..b2d79ccff2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,87 +7,106 @@ on: - "*pre*" - "*beta*" - "1[0-9]+.[0-9]+.[0-9]+" - -permissions: - contents: read + workflow_dispatch: + inputs: + tag_name: + description: 'Tag name to release' + required: true + type: string + dry_run: + description: 'Dry run (skip git push and npm publish)' + required: false + type: boolean + default: false jobs: - prerelease: - permissions: - contents: write # for git push - + release: name: Release runs-on: ubuntu-latest + permissions: + contents: read + id-token: write # for OIDC token steps: - - name: Checkout highlight.js - uses: actions/checkout@v5 + - name: Set tag environment variables + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + TAG_REF="refs/tags/${{ github.event.inputs.tag_name }}" + else + TAG_REF="${{ github.ref }}" + fi + + TAG_NAME="${TAG_REF/refs\/tags\//}" + + echo "TAG_REF=$TAG_REF" >> "$GITHUB_ENV" + echo "TAG_NAME=$TAG_NAME" >> "$GITHUB_ENV" + echo "MAJOR_VERSION=$(echo "$TAG_NAME" | cut -d'.' -f1)" >> "$GITHUB_ENV" - - name: Tag is ${{ github.ref }}. - # we have to repeat ourselves here since the environment is not actually updated - # until the next step executes, so we can't access $TAG_NAME yet + - name: Set dry-run flag run: | - echo "TAG_NAME=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV - echo "MAJOR_VERSION=$(echo ${GITHUB_REF/refs\/tags\//} | cut -d'.' -f1)" >> $GITHUB_ENV + if [ "${{ inputs.dry_run }}" = "true" ]; then + echo "DRY_RUN_FLAG=--dry-run" >> "$GITHUB_ENV" + else + echo "DRY_RUN_FLAG=" >> "$GITHUB_ENV" + fi + - name: Make sure we are pushing a tag... - if: ${{ !contains(github.ref,'refs/tags/') }} - run: false - # run: echo "TAG_NAME=0.0.0-test0" >> $GITHUB_ENV + if: ${{ !contains(env.TAG_REF, 'refs/tags/') }} + run: exit 1 - - if: contains(github.ref, 'beta') || contains(github.ref, 'pre') || contains(github.ref, 'alpha') - run: | - echo "NPM_TAG=beta" >> $GITHUB_ENV - echo "RELEASING=beta" >> $GITHUB_ENV + - name: Checkout highlight.js + uses: actions/checkout@v7 + with: + persist-credentials: false + ref: ${{ env.TAG_NAME }} - - if: ${{ !(contains(github.ref, 'beta') || contains(github.ref, 'pre') || contains(github.ref, 'alpha')) }} + - name: Set release channel and branch + run: | + if [[ "${TAG_NAME}" == *beta* || "${TAG_NAME}" == *pre* || "${TAG_NAME}" == *alpha* ]]; then + echo "NPM_TAG=beta" >> "$GITHUB_ENV" + echo "RELEASING=beta" >> "$GITHUB_ENV" + echo "BRANCH_NAME=main" >> "$GITHUB_ENV" + else + echo "NPM_TAG=latest" >> "$GITHUB_ENV" + echo "RELEASING=stable" >> "$GITHUB_ENV" + echo "BRANCH_NAME=${MAJOR_VERSION}-stable" >> "$GITHUB_ENV" + fi + + - name: Validate release channel run: | - echo "NPM_TAG=latest" >> $GITHUB_ENV - echo "RELEASING=stable" >> $GITHUB_ENV + if [[ "${{ env.RELEASING }}" != "stable" && "${{ env.RELEASING }}" != "beta" ]]; then + echo "We seem to be releasing ${{ env.RELEASING }}." + exit 1 + fi - - name: match-tag-to-package-version - uses: geritol/match-tag-to-package-version@0.2.0 - env: - TAG_PREFIX: refs/tags/ # Optional, default prefix refs/tags/ + - name: Match tag to package version + run: | + PACKAGE_VERSION=$(jq -r '.version' package.json) + if [ "$PACKAGE_VERSION" != "$TAG_NAME" ]; then + echo "Tag ($TAG_NAME) does not match package.json version ($PACKAGE_VERSION)" + exit 1 + fi - - name: Use Node.js 20.x + - name: Use Node.js 24.x uses: actions/setup-node@v6 with: - node-version: 20.x + node-version: 24.x + registry-url: 'https://registry.npmjs.org' + package-manager-cache: false # never use caching in release builds + - name: Build Node.js package run: | - npm install + npm ci node ./tools/build.js -t node npm test - name: Publish highlight.js to NPM - id: publish - uses: JS-DevTools/npm-publish@v4 - with: - check-version: true - token: ${{ secrets.NPM_TOKEN }} - package: ./build/package.json - tag: ${{ env.NPM_TAG }} - - - if: steps.publish.outputs.type != 'none' - run: | - echo "Version changed: ${{ steps.publish.outputs.old-version }} => ${{ steps.publish.outputs.version }}" - - # if stable release - - name: Stable Release - if: env.RELEASING == 'stable' - run: echo "BRANCH_NAME=${MAJOR_VERSION}-stable" >> $GITHUB_ENV - # else (beta) - - name: Beta Release - if: env.RELEASING == 'beta' - run: echo "BRANCH_NAME=main" >> $GITHUB_ENV - - name: Confirm release is either stable or beta - if: ${{ !(env.RELEASING == 'stable' || env.RELEASING == 'beta') }} run: | - echo We seem to be releasing `${RELEASING}`. - false + npm publish --tag ${{ env.NPM_TAG }} ${{ env.DRY_RUN_FLAG }} - name: Checkout cdn-release - uses: actions/checkout@v5 + uses: actions/checkout@v7 with: + persist-credentials: true repository: 'highlightjs/cdn-release' path: 'cdn-release' token: ${{ secrets.CDN_REPO_TOKEN }} @@ -96,7 +115,8 @@ jobs: - name: Build CDN package run: node ./tools/build.js -t cdn :common - - name: Commmit & Push cdn-release ${{ env.TAG_NAME }} + - name: Commit & Push cdn-release ${{ env.TAG_NAME }} + if: ${{ github.event_name != 'workflow_dispatch' || inputs.dry_run != true }} working-directory: ./cdn-release run: | rm -r ./build @@ -111,30 +131,6 @@ jobs: git push -f --atomic origin ${BRANCH_NAME} ${TAG_NAME} - name: Publish cdn-assets to NPM - id: publish_cdn - uses: JS-DevTools/npm-publish@v4 - with: - check-version: true - token: ${{ secrets.NPM_TOKEN }} - package: ./cdn-release/build/package.json - tag: ${{ env.NPM_TAG }} - - # log.info('Updating CDN repo at %s' % settings.HLJS_CDN_SOURCE) - # run(['nodejs', 'tools/build.js', '--target', 'cdn', ':common']) - # os.chdir(settings.HLJS_CDN_SOURCE) - # run(['git', 'pull', '-f']) - # lines = run(['git', '--git-dir', os.path.join(settings.HLJS_CDN_SOURCE, '.git'), 'tag']) - # build_dir = os.path.join(settings.HLJS_CDN_SOURCE, 'build') - # if version in lines: - # log.info('Tag %s already exists in the local CDN repo' % version) - # else: - # if os.path.exists(build_dir): - # shutil.rmtree(build_dir) - # shutil.move(os.path.join(settings.HLJS_SOURCE, 'build'), build_dir) - # run(['git', 'add', '.']) - # run(['git', 'commit', '-m', 'Update to version %s' % version]) - # run(['git', 'tag', version]) - # run(['git', 'push']) - # run(['git', 'push', '--tags']) - # npm_publish(build_dir) - # os.chdir(settings.HLJS_SOURCE) + run: | + cd ./cdn-release/build + npm publish --tag ${{ env.NPM_TAG }} ${{ env.DRY_RUN_FLAG }} From fcbaaaf7b5bc68ee20d38900ddf6f997800239b4 Mon Sep 17 00:00:00 2001 From: Vladimir Jimenez Date: Tue, 4 Aug 2026 23:27:57 -0700 Subject: [PATCH 2/5] fix(ci/cd): publish correct build folder Co-authored-by: Artur <145776561+arturict@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b2d79ccff2..a1c6a30568 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -101,7 +101,7 @@ jobs: - name: Publish highlight.js to NPM run: | - npm publish --tag ${{ env.NPM_TAG }} ${{ env.DRY_RUN_FLAG }} + npm publish ./build --tag ${{ env.NPM_TAG }} ${{ env.DRY_RUN_FLAG }} - name: Checkout cdn-release uses: actions/checkout@v7 From 48595d10fa4515bdc4768c7ae60b62869fc1dcd5 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 9 Aug 2026 00:35:16 -0400 Subject: [PATCH 3/5] (chore) clarify unexpected release channel error message --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a1c6a30568..4d02909ba9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -74,7 +74,7 @@ jobs: - name: Validate release channel run: | if [[ "${{ env.RELEASING }}" != "stable" && "${{ env.RELEASING }}" != "beta" ]]; then - echo "We seem to be releasing ${{ env.RELEASING }}." + echo "Unexpected RELEASING='${{ env.RELEASING }}' (expected stable or beta). Aborting." exit 1 fi From 9268004ec6a190a726705894bc1add5afed36052 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 9 Aug 2026 00:40:00 -0400 Subject: [PATCH 4/5] (chore) skip npm publish when version already exists Check the registry first so a re-run can finish CDN steps after a partial release without failing on "version already published". Also bump setup-node to v7. --- .github/workflows/release.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4d02909ba9..b3886163ef 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -87,7 +87,7 @@ jobs: fi - name: Use Node.js 24.x - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version: 24.x registry-url: 'https://registry.npmjs.org' @@ -101,7 +101,11 @@ jobs: - name: Publish highlight.js to NPM run: | - npm publish ./build --tag ${{ env.NPM_TAG }} ${{ env.DRY_RUN_FLAG }} + if npm view "highlight.js@${TAG_NAME}" version >/dev/null 2>&1; then + echo "highlight.js@${TAG_NAME} already on npm; skipping publish" + else + npm publish ./build --tag "${NPM_TAG}" ${DRY_RUN_FLAG} + fi - name: Checkout cdn-release uses: actions/checkout@v7 @@ -132,5 +136,8 @@ jobs: - name: Publish cdn-assets to NPM run: | - cd ./cdn-release/build - npm publish --tag ${{ env.NPM_TAG }} ${{ env.DRY_RUN_FLAG }} + if npm view "@highlightjs/cdn-assets@${TAG_NAME}" version >/dev/null 2>&1; then + echo "@highlightjs/cdn-assets@${TAG_NAME} already on npm; skipping publish" + else + npm publish ./cdn-release/build --tag "${NPM_TAG}" ${DRY_RUN_FLAG} + fi From fdfe4b820e96e3746912fef4d7e4e9473ae142b1 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 9 Aug 2026 00:47:00 -0400 Subject: [PATCH 5/5] (chore) reject unrecognized release tags instead of assuming stable workflow_dispatch can pass any tag name; only allow the same shapes as on.push.tags so typos never publish under npm latest. --- .github/workflows/release.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b3886163ef..2deb2a56f4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,10 +65,13 @@ jobs: echo "NPM_TAG=beta" >> "$GITHUB_ENV" echo "RELEASING=beta" >> "$GITHUB_ENV" echo "BRANCH_NAME=main" >> "$GITHUB_ENV" - else + elif [[ "${TAG_NAME}" =~ ^1[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "NPM_TAG=latest" >> "$GITHUB_ENV" echo "RELEASING=stable" >> "$GITHUB_ENV" echo "BRANCH_NAME=${MAJOR_VERSION}-stable" >> "$GITHUB_ENV" + else + echo "Unrecognized tag '${TAG_NAME}' (expected 1x.y.z stable or *alpha*/*pre*/*beta* prerelease)." + exit 1 fi - name: Validate release channel