From d44a4b9f57249581300cf7ff2b237e282cac2e9e Mon Sep 17 00:00:00 2001 From: Rodolphe Stoclin Date: Wed, 2 Sep 2026 14:48:36 +0200 Subject: [PATCH 1/4] fix(release): surface breaking changes in notes, correct action tag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Release notes: publish.yml emitted only a package list plus 'See individual package CHANGELOGs', so v11's breaking changes — Node 22 baseline, 5 removed compressors, removed type aliases — were invisible on the releases page. Prepend the version's own core CHANGELOG section above the package list. Verified the awk extraction against the real 11.0.0 entry, and that a missing section degrades to the previous package-list-only output. Action tag: every doc said 'srod/node-minify@v1', but no v1 tag exists and release-action.yml has never run. That workflow derives its major tag from the release tag publish.yml creates, which is the monorepo version — so the action line is v11, never v1. The documented snippet could not have worked. Update all 31 references across the docs, both action READMEs and the composite action manifest. --- .github/actions/node-minify/README.md | 6 ++--- .github/actions/node-minify/action.yml | 6 ++--- .github/workflows/publish.yml | 22 ++++++++++++++--- AGENTS.md | 6 ++--- docs/src/content/docs/github-action.md | 34 +++++++++++++------------- packages/action/README.md | 10 ++++---- 6 files changed, 49 insertions(+), 35 deletions(-) diff --git a/.github/actions/node-minify/README.md b/.github/actions/node-minify/README.md index 2f74ef34e..7be0ac9cf 100644 --- a/.github/actions/node-minify/README.md +++ b/.github/actions/node-minify/README.md @@ -3,7 +3,7 @@ > **This action is deprecated.** Please use the new bundled action instead: > > ```yaml -> - uses: srod/node-minify@v1 +> - uses: srod/node-minify@v11 > ``` The new action includes: @@ -29,7 +29,7 @@ Replace: With: ```yaml -- uses: srod/node-minify@v1 +- uses: srod/node-minify@v11 with: input: "src/app.js" output: "dist/app.min.js" @@ -64,7 +64,7 @@ If you use the `gcc` compressor that requires Java: distribution: 'temurin' java-version: '17' -- uses: srod/node-minify@v1 +- uses: srod/node-minify@v11 with: compressor: gcc ``` diff --git a/.github/actions/node-minify/action.yml b/.github/actions/node-minify/action.yml index 9efd1160e..71e717a8a 100644 --- a/.github/actions/node-minify/action.yml +++ b/.github/actions/node-minify/action.yml @@ -1,5 +1,5 @@ name: "node-minify (deprecated)" -description: "DEPRECATED: Use srod/node-minify@v1 instead. This composite action will be removed in a future release." +description: "DEPRECATED: Use srod/node-minify@v11 instead. This composite action will be removed in a future release." author: "srod" branding: icon: "minimize-2" @@ -70,7 +70,7 @@ runs: - name: Deprecation warning shell: bash run: | - echo "::warning::This action (.github/actions/node-minify) is DEPRECATED. Please migrate to 'uses: srod/node-minify@v1' for the new bundled action with more features (PR comments, annotations, benchmarking)." + echo "::warning::This action (.github/actions/node-minify) is DEPRECATED. Please migrate to 'uses: srod/node-minify@v11' for the new bundled action with more features (PR comments, annotations, benchmarking)." - name: Setup Java (for gcc) if: contains(fromJSON('["gcc", "google-closure-compiler"]'), inputs.compressor) @@ -136,6 +136,6 @@ runs: | **Gzip Size** | ${{ steps.minify.outputs.gzip-size-formatted }} | | **Time** | ${{ steps.minify.outputs.time-ms }}ms | - > **Note:** This action is deprecated. Please migrate to \`uses: srod/node-minify@v1\` for enhanced features. + > **Note:** This action is deprecated. Please migrate to \`uses: srod/node-minify@v11\` for enhanced features. EOF diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index aa0c2c640..5d0ff6cc4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -61,11 +61,25 @@ jobs: # Format package list as markdown PACKAGE_LIST=$(echo "$PACKAGES" | jq -r '.[] | "- `\(.name)@\(.version)`"') - # Create release notes - cat > /tmp/release-notes.md << 'EOF' - ## Published Packages + # Lead with this version's own CHANGELOG entry so breaking changes are + # visible on the releases page instead of only in the package files. + # awk prints the body between this version's heading and the next one. + NOTES=$(awk -v ver="## ${VERSION}" ' + $0 == ver { capture = 1; next } + capture && /^## / { exit } + capture { print } + ' packages/core/CHANGELOG.md) - EOF + : > /tmp/release-notes.md + if [ -n "$(echo "$NOTES" | tr -d '[:space:]')" ]; then + echo "$NOTES" >> /tmp/release-notes.md + echo "" >> /tmp/release-notes.md + echo "---" >> /tmp/release-notes.md + echo "" >> /tmp/release-notes.md + fi + + echo "## Published Packages" >> /tmp/release-notes.md + echo "" >> /tmp/release-notes.md echo "$PACKAGE_LIST" >> /tmp/release-notes.md echo "" >> /tmp/release-notes.md echo "See individual package CHANGELOGs for details." >> /tmp/release-notes.md diff --git a/AGENTS.md b/AGENTS.md index e19356f65..97cece0ec 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -153,10 +153,10 @@ Custom `scripts/publish.ts` resolves `workspace:*` → concrete versions before ## GitHub Action -Published as `srod/node-minify@v1`. See `packages/action/AGENTS.md` for details. +Published as `srod/node-minify@v11`. See `packages/action/AGENTS.md` for details. ```yaml -- uses: srod/node-minify@v1 +- uses: srod/node-minify@v11 with: input: "src/app.js" output: "dist/app.min.js" @@ -165,7 +165,7 @@ Published as `srod/node-minify@v1`. See `packages/action/AGENTS.md` for details. Zero-config mode: ```yaml -- uses: srod/node-minify@v1 +- uses: srod/node-minify@v11 with: auto: true output-dir: dist diff --git a/docs/src/content/docs/github-action.md b/docs/src/content/docs/github-action.md index 15b1caa38..93a1e6e66 100644 --- a/docs/src/content/docs/github-action.md +++ b/docs/src/content/docs/github-action.md @@ -72,7 +72,7 @@ Enable automatic file discovery and compressor selection with `auto: true`. The npm install @node-minify/terser @node-minify/lightningcss - name: Minify all files - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: auto: 'true' ``` @@ -87,7 +87,7 @@ Override default patterns to target specific files: ```yaml - name: Minify custom locations - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: auto: 'true' patterns: 'public/**/*.js,assets/**/*.css' @@ -100,7 +100,7 @@ Add additional ignore patterns (merges with defaults): ```yaml - name: Minify with custom ignores - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: auto: 'true' ignore: '**/*.config.js,**/vendor/**' @@ -112,7 +112,7 @@ Preview which files would be processed without minifying: ```yaml - name: Preview auto mode - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: auto: 'true' dry-run: 'true' @@ -143,7 +143,7 @@ For a project with JavaScript, CSS, and HTML: @node-minify/html-minifier - name: Minify all assets - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: auto: 'true' output-dir: 'public/dist' @@ -161,7 +161,7 @@ For a project with JavaScript, CSS, and HTML: run: npm install @node-minify/terser - name: Minify JavaScript - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/app.js" output: "dist/app.min.js" @@ -193,7 +193,7 @@ jobs: run: npm install @node-minify/terser - name: Minify JS - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/app.js" output: "dist/app.min.js" @@ -211,7 +211,7 @@ When enabled, PR comments include a **"vs Base"** column showing size changes co ```yaml - name: Minify and Report - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/app.js" output: "dist/app.min.js" @@ -229,7 +229,7 @@ When enabled, PR comments include a **"vs Base"** column showing size changes co run: npm install @node-minify/lightningcss - name: Minify CSS - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/styles.css" output: "dist/styles.min.css" @@ -240,7 +240,7 @@ When enabled, PR comments include a **"vs Base"** column showing size changes co ```yaml - name: Minify with Thresholds - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/app.js" output: "dist/app.min.js" @@ -258,7 +258,7 @@ Compare multiple compressors to find the best one for your project: run: npm install @node-minify/terser @node-minify/esbuild @node-minify/swc @node-minify/oxc - name: Benchmark Compressors - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/app.js" output: "dist/app.min.js" @@ -344,7 +344,7 @@ The `type` parameter is **required** for: ```yaml - name: Minify id: minify - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/app.js" output: "dist/app.min.js" @@ -372,7 +372,7 @@ The `type` parameter is **required** for: run: npm install @node-minify/google-closure-compiler - name: Minify with GCC - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/app.js" output: "dist/app.min.js" @@ -387,7 +387,7 @@ The `type` parameter is **required** for: run: npm install @node-minify/html-minifier - name: Minify HTML - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/index.html" output: "dist/index.html" @@ -402,14 +402,14 @@ The `type` parameter is **required** for: run: npm install @node-minify/terser @node-minify/lightningcss - name: Minify JS bundle - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/**/*.js" output: "dist/bundle.min.js" compressor: "terser" - name: Minify CSS bundle - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/**/*.css" output: "dist/styles.min.css" @@ -446,7 +446,7 @@ jobs: - name: Minify Assets id: minify - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "dist/**/*.js" compressor: "esbuild" diff --git a/packages/action/README.md b/packages/action/README.md index 4ddc77906..25dc0388f 100644 --- a/packages/action/README.md +++ b/packages/action/README.md @@ -27,7 +27,7 @@ Compressor packages contain native dependencies that cannot be bundled into the run: npm install @node-minify/terser - name: Minify JavaScript - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/app.js" output: "dist/app.min.js" @@ -38,7 +38,7 @@ Compressor packages contain native dependencies that cannot be bundled into the ```yaml - name: Minify JavaScript - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/app.js" output: "dist/app.min.js" @@ -55,7 +55,7 @@ Automatically discover and minify files with `auto: true`: run: npm install @node-minify/terser @node-minify/lightningcss - name: Minify all files - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: auto: 'true' ``` @@ -68,7 +68,7 @@ See [full zero-config documentation](https://node-minify.2clics.net/github-actio ```yaml - name: Minify and Report - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/app.js" output: "dist/app.min.js" @@ -83,7 +83,7 @@ See [full zero-config documentation](https://node-minify.2clics.net/github-actio ```yaml - name: Minify with Quality Gates - uses: srod/node-minify@v1 + uses: srod/node-minify@v11 with: input: "src/app.js" output: "dist/app.min.js" From 3ad6ad17e93f1f4805bb407f0a0a6f1090786800 Mon Sep 17 00:00:00 2001 From: Rodolphe Stoclin Date: Wed, 2 Sep 2026 14:56:58 +0200 Subject: [PATCH 2/4] fix(release): dispatch action release, raise minify-html timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cubic P1: releases created with GITHUB_TOKEN do not emit 'release: published', so release-action.yml has never run — which is why no v1/v11 tag exists and every documented action snippet was broken. Documenting @v11 alone would not have fixed it. workflow_dispatch is exempt from that recursion guard, so publish.yml now dispatches release-action.yml with the release tag after creating the release, and takes the actions: write permission that requires. release-action.yml uploaded its artifact only when github.event_name was 'release', so a dispatched run would tag without publishing the zip; drop that condition and pass tag_name explicitly. Windows CI: the minify-html error test resets the module registry and re-imports the compressor, exceeding vitest's 5s default on Windows runners. Set testTimeout to 30s, matching the convention in the action and GCC packages. --- .github/workflows/publish.yml | 14 ++++++++++++++ .github/workflows/release-action.yml | 4 +++- packages/minify-html/vitest.config.ts | 3 +++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5d0ff6cc4..c31200355 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,6 +10,8 @@ permissions: id-token: write contents: write pull-requests: write + # Required to dispatch release-action.yml after the release is created. + actions: write concurrency: ${{ github.workflow }}-${{ github.ref }} @@ -89,3 +91,15 @@ jobs: --notes-file /tmp/release-notes.md env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Releases created with GITHUB_TOKEN do not emit `release: published`, + # so release-action.yml would never run on its own. workflow_dispatch is + # exempt from that recursion guard, so trigger it explicitly to publish + # the action's version and major tags. + - name: 🏷️ Trigger action tag release + if: steps.changesets.outputs.published == 'true' + run: | + VERSION=$(jq -r '.version' packages/core/package.json) + gh workflow run release-action.yml -f tag="v${VERSION}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-action.yml b/.github/workflows/release-action.yml index 1f51957c7..78d21bb14 100644 --- a/.github/workflows/release-action.yml +++ b/.github/workflows/release-action.yml @@ -124,8 +124,10 @@ jobs: cd release-action zip -r ../node-minify-action-${{ steps.version.outputs.version }}.zip . + # Also runs for workflow_dispatch: publish.yml dispatches this workflow + # because a GITHUB_TOKEN release never emits `release: published`. - name: Upload release artifact - if: github.event_name == 'release' uses: softprops/action-gh-release@v2 with: + tag_name: ${{ steps.version.outputs.version }} files: node-minify-action-${{ steps.version.outputs.version }}.zip diff --git a/packages/minify-html/vitest.config.ts b/packages/minify-html/vitest.config.ts index 4f772037c..3b7f6de4b 100644 --- a/packages/minify-html/vitest.config.ts +++ b/packages/minify-html/vitest.config.ts @@ -3,5 +3,8 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { globals: true, + // The error test resets the module registry and re-imports the + // compressor, which exceeds the 5s default on Windows runners. + testTimeout: 30000, }, }); From b24fb6209401ab23c4421a070753cc0281c2d1a6 Mon Sep 17 00:00:00 2001 From: Rodolphe Stoclin Date: Wed, 2 Sep 2026 15:08:36 +0200 Subject: [PATCH 3/4] fix(release): make artifact upload explicit, scope test timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cubic P2 claimed softprops/action-gh-release@v2 errors on an existing asset unless 'overwrite: true' is set. Checked the action source: the input is overwrite_files, not overwrite, and github.ts only skips the replace when it is exactly false — so re-runs already overwrite. Set it explicitly anyway to document the intent for repeated dispatches. cubic P3: the 30s timeout applied to every minify-html test, so an unrelated regression could hang for 30s instead of failing at 5s. Move it onto the one slow test and restore the package default. --- .github/workflows/release-action.yml | 3 +++ packages/minify-html/__tests__/minify-html-error.test.ts | 4 +++- packages/minify-html/vitest.config.ts | 3 --- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-action.yml b/.github/workflows/release-action.yml index 78d21bb14..d8d9fb25a 100644 --- a/.github/workflows/release-action.yml +++ b/.github/workflows/release-action.yml @@ -131,3 +131,6 @@ jobs: with: tag_name: ${{ steps.version.outputs.version }} files: node-minify-action-${{ steps.version.outputs.version }}.zip + # Re-runs and repeated dispatches target the same tag. This is the + # action's default, set explicitly so the intent survives upgrades. + overwrite_files: true diff --git a/packages/minify-html/__tests__/minify-html-error.test.ts b/packages/minify-html/__tests__/minify-html-error.test.ts index 81e2455f8..52c8cb6a3 100644 --- a/packages/minify-html/__tests__/minify-html-error.test.ts +++ b/packages/minify-html/__tests__/minify-html-error.test.ts @@ -15,7 +15,9 @@ describe("Package: minify-html error handling", () => { vi.doUnmock("@minify-html/node"); }); - test("should wrap minification errors", async () => { + // Resetting the module registry and re-importing the compressor exceeds + // vitest's 5s default on Windows runners, so widen this test only. + test("should wrap minification errors", { timeout: 30000 }, async () => { // Mirrors the real module shape: @minify-html/node is CommonJS, so its // exports are reached through the default export. vi.doMock("@minify-html/node", () => ({ diff --git a/packages/minify-html/vitest.config.ts b/packages/minify-html/vitest.config.ts index 3b7f6de4b..4f772037c 100644 --- a/packages/minify-html/vitest.config.ts +++ b/packages/minify-html/vitest.config.ts @@ -3,8 +3,5 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { globals: true, - // The error test resets the module registry and re-imports the - // compressor, which exceeds the 5s default on Windows runners. - testTimeout: 30000, }, }); From b401ceddd250fd26c0edab0fa6230930ae84f5dc Mon Sep 17 00:00:00 2001 From: Rodolphe Stoclin Date: Wed, 2 Sep 2026 15:20:20 +0200 Subject: [PATCH 4/4] fix(release): authorize manual dispatches of the action release CodeRabbit (Major, CWE-862) on the dispatch path this PR introduced: release-action.yml force-updates tags and now overwrites release assets on workflow_dispatch, but its tag input was only format-validated. Any account with write access could dispatch an arbitrary tag-shaped ref, have it checked out, and force-move tags over an existing release. Gate the workflow before checkout: on workflow_dispatch the tag must match the release-tag pattern and resolve to an existing, non-draft release of this repository. Verified v10.5.0 is accepted while an unpublished tag, a branch name, and a shell-injection string are all rejected before any ref is checked out. --- .github/workflows/release-action.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/release-action.yml b/.github/workflows/release-action.yml index d8d9fb25a..3154a7cb1 100644 --- a/.github/workflows/release-action.yml +++ b/.github/workflows/release-action.yml @@ -20,6 +20,32 @@ jobs: name: Build and Release Action runs-on: ubuntu-latest steps: + # Runs before checkout: a manual dispatch can name any tag-shaped ref, + # and this workflow force-updates tags and overwrites release assets. + # Require the tag to already exist as a published (non-draft) release. + - name: Verify tag is a published release + if: github.event_name == 'workflow_dispatch' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + INPUT_TAG: ${{ github.event.inputs.tag }} + run: | + if ! [[ "$INPUT_TAG" =~ ^v[0-9]+(\.[0-9]+)*$ ]]; then + echo "Error: '$INPUT_TAG' is not a valid release tag." + exit 1 + fi + + if ! gh release view "$INPUT_TAG" \ + --repo "$GITHUB_REPOSITORY" \ + --json isDraft > /tmp/release.json 2>/dev/null; then + echo "Error: '$INPUT_TAG' is not a published release of this repository." + exit 1 + fi + + if [ "$(jq -r '.isDraft' /tmp/release.json)" != "false" ]; then + echo "Error: release '$INPUT_TAG' is a draft; refusing to publish action tags." + exit 1 + fi + - name: Checkout uses: actions/checkout@v4 with: