Skip to content

feat: upgrade to Node 24 and modernize build toolchain#12

Merged
dcoraboeuf merged 18 commits into
nemerosa:mainfrom
rathpc:feat/upgrade-node-24-and-build-toolchain
May 5, 2026
Merged

feat: upgrade to Node 24 and modernize build toolchain#12
dcoraboeuf merged 18 commits into
nemerosa:mainfrom
rathpc:feat/upgrade-node-24-and-build-toolchain

Conversation

@rathpc
Copy link
Copy Markdown
Contributor

@rathpc rathpc commented May 3, 2026

Summary

Upgrade cli-setup to Node 24, esbuild bundling (replacing @vercel/ncc), ESLint 10, real unit tests, standardized semantic-release with floating major/minor tags, and a CLAUDE.md. Replaces the manual tag.sh release flow with semantic-release.

What changed

  • Runtime: engines.node set to >=24; action.yml runtime is node24; CI workflow installs Node 24.
  • Bundling: replaced @vercel/ncc with esbuild + esbuild-plugin-license (driven by build.js). dist/index.js produced by esbuild; dist/licenses.txt carries third-party attributions.
  • Source shape: index.js refactored to a dependency-injection pattern. A top-level IIFE loads pure-ESM @actions/* packages via dynamic ESM import and calls runAction({core, exec, github, tc}). Helpers (configureCLI, configureProject, configureAutoPromotion, downloadAndSetup) take deps as params for testability.
  • Tests: new index.test.js with 27 unit tests covering input handling, ref parsing (branches/tags/PR refs), version resolution, only-for gating, CLI configuration, project setup, and the pure helpers (mapArch, mapOS, argsWithConfig). Tests run in <500ms without secrets or network. fs.promises.chmod and fs.promises.rename are mocked in the test setup so downloadAndSetup can be exercised without real filesystem operations — this is the right boundary, the production code does not swallow errors.
  • Dependency cleanup: dropped @actions/io entirely (its single use, io.mv, replaced with native fs.promises.rename). Kept @actions/tool-cache for tc.downloadTool. Kept read-yaml-promise (still CJS-compatible).
  • ESLint: eslint 8.x → 10.3.0 with new flat config. .eslintrc.json and .eslintignore removed. Companion @eslint/js pinned to ^10.0.1.
  • Release process: removed tag.sh and .github/tag.sh. Replaced with semantic-release configured to trigger at least a patch on every conventional-commit type, plus a floating-tag step that updates vX and vX.Y after each release.
  • Docs: new CLAUDE.md; README.md "Building" section updated for esbuild.
  • Workflow: old minimal build.yml replaced by ci.yml with the standardized pipeline.

Why this is breaking

engines.node >= 24 and runs.using: node24 mean self-hosted runners on older Node will fail. GitHub-hosted runners support Node 24 natively. The release flow itself also changes: existing @v2 floating tags created by the manual tag.sh are left alone; the breaking release lands as v3.0.0 with new @v3 and @v3.0 floating tags. Consumers stay on @v2 until they explicitly opt into @v3.

The action's input/output surface is unchanged.

Test plan

  • CI passes on this PR (lint, build, tests)
  • After merge, semantic-release publishes v3.0.0
  • Floating tags v3 and v3.0 exist; existing v2 tags untouched
  • CHANGELOG.md is populated by the release commit
  • dist/licenses.txt is regenerated alongside the bundle (with full per-dependency license text)

Squash-merge commit message

When merging, please use this exact commit body:

feat: upgrade to Node 24 and modernize build toolchain

- Replace @vercel/ncc with esbuild + esbuild-plugin-license
- Convert index.js to runAction({core,exec,github,tc}) DI pattern
- Drop @actions/io (replaced with native fs.promises.rename)
- Bump @actions/* and semantic-release to latest stable
- Migrate to ESLint 10 flat config
- Add 27 unit tests with mocked GitHub Actions deps
- Replace manual tag.sh release flow with semantic-release
- Standardize release rules (patch on every commit type)
- Add floating major/minor tag step in CI
- Add CLAUDE.md for AI-assisted development

BREAKING CHANGE: Now requires Node 24 runtime. Self-hosted runners
must have Node 24 available; GitHub-hosted runners support this
natively. The release flow has changed: existing v2 floating tags
are unchanged; new releases publish as v3+ with floating v3 / v3.0
tags. Consumers using @v2 stay on the old code unless they bump.

rathpc and others added 18 commits May 2, 2026 22:53
Also fix downloadAndSetup to use fs.promises.chmod (async) and swallow
ENOENT on chmod/rename so the function is testable with a mock tc dep.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…/licenses.txt

The `output` option requires `{ file, template }` object form, not a bare
string. Passing a string caused the plugin to silently fall back to the
default filename (`dependencies.txt` in the project root) and the minimal
one-liner template, so `dist/licenses.txt` was never written.

Switch to the object form with a template that matches the ncc-era format
(package name, SPDX identifier, full license text). This also eliminates
the stray `dependencies.txt` that was being written to the project root.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rathpc
Copy link
Copy Markdown
Contributor Author

rathpc commented May 3, 2026

@dcoraboeuf Here is number 3 in the series being opened. More to come...

@dcoraboeuf dcoraboeuf merged commit ff03a61 into nemerosa:main May 5, 2026
1 check passed
@dcoraboeuf
Copy link
Copy Markdown
Contributor

Available in v3.0.0

Thanks for your contribution @rathpc !

@rathpc rathpc deleted the feat/upgrade-node-24-and-build-toolchain branch May 6, 2026 01:28
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.

2 participants