Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0cb4b4f
build: remove manual tag.sh scripts in favor of semantic-release
rathpc May 3, 2026
111e3d3
build: bump action runtime to node24
rathpc May 3, 2026
0be2a96
docs: add CLAUDE.md for AI-assisted development
rathpc May 3, 2026
0cb165e
refactor: convert index.js to runAction({core,exec,github,tc}) DI pat…
rathpc May 3, 2026
9d8ee39
build: migrate to ESLint 10 flat config (drop .eslintrc.json/.eslinti…
rathpc May 3, 2026
0743dbb
build: bump deps to latest stable; add engines.node >=24; drop ncc an…
rathpc May 3, 2026
f54b982
build: add esbuild driver with license plugin (replaces ncc)
rathpc May 3, 2026
48b77db
build: regenerate package-lock.json against new dep versions
rathpc May 3, 2026
c66a2fe
fix: address eslint issues
rathpc May 3, 2026
0122b3d
test: add unit tests for runAction and helpers with mocked deps
rathpc May 3, 2026
e0a8921
fix: mock fs in tests instead of swallowing errors in downloadAndSetup
rathpc May 3, 2026
9f76d04
build: rebuild dist/ with esbuild + license plugin
rathpc May 3, 2026
134fa9f
fix: correct esbuild-plugin-license output config and regenerate dist…
rathpc May 3, 2026
6b9c428
docs: update README build instructions for esbuild
rathpc May 3, 2026
3df3d20
docs: seed CHANGELOG.md for semantic-release
rathpc May 3, 2026
1d1bdaa
build: add semantic-release config; patch-on-every-commit-type
rathpc May 3, 2026
782eb62
ci: replace minimal build.yml with standardized ci.yml (Node 24, floa…
rathpc May 3, 2026
ad2ac6f
fix: use namespace imports for pure-ESM @actions/* packages
rathpc May 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .eslintignore

This file was deleted.

18 changes: 0 additions & 18 deletions .eslintrc.json

This file was deleted.

66 changes: 0 additions & 66 deletions .github/tag.sh

This file was deleted.

25 changes: 0 additions & 25 deletions .github/workflows/build.yml

This file was deleted.

67 changes: 67 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: ci

on:
pull_request:
push:
branches: [main]

concurrency: "ci-${{ github.ref }}"

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: true

- uses: actions/setup-node@v6
with:
node-version: 24.x

- run: npm ci

- run: npm run lint

- run: npm run build

- run: npm test

- name: Self-test (CLI setup)
if: github.repository_owner == 'nemerosa'
uses: ./
with:
url: ${{ vars.ONTRACK_URL }}
token: ${{ secrets.ONTRACK_TOKEN }}
config: github.com
indexation: 120
github-token: ${{ github.token }}
auto-validation-stamps: true
auto-promotion-levels: true
promotions: ./.github/ontrack/promotions.yml

- name: Semantic release
id: release
if: ${{ github.ref == 'refs/heads/main' }}
run: |
npx semantic-release
VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
echo "version=$VERSION" >> $GITHUB_OUTPUT
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Update floating major/minor tags
if: ${{ github.ref == 'refs/heads/main' && steps.release.outputs.version != '' }}
run: |
VERSION="${{ steps.release.outputs.version }}"
[[ "$VERSION" =~ ^v([0-9]+)\.([0-9]+)\.[0-9]+$ ]] || exit 0
MAJOR="v${BASH_REMATCH[1]}"
MINOR="v${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"
git tag -f "$MAJOR" "$VERSION"
git tag -f "$MINOR" "$VERSION"
git push origin -f "$MAJOR" "$MINOR"
30 changes: 30 additions & 0 deletions .releaserc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"branches": ["main"],
"plugins": [
["@semantic-release/commit-analyzer", {
"preset": "angular",
"releaseRules": [
{ "breaking": true, "release": "major" },
{ "type": "feat", "release": "minor" },
{ "type": "refactor", "release": "major" },
{ "type": "fix", "release": "patch" },
{ "type": "perf", "release": "patch" },
{ "type": "chore", "release": "patch" },
{ "type": "docs", "release": "patch" },
{ "type": "style", "release": "patch" },
{ "type": "test", "release": "patch" },
{ "type": "build", "release": "patch" },
{ "type": "ci", "release": "patch" },
{ "type": "revert", "release": "patch" },
{ "type": "release", "release": "patch" }
]
}],
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
["@semantic-release/git", {
"assets": ["dist/**/*", "CHANGELOG.md", "package.json", "package-lock.json", "action.yml"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}],
"@semantic-release/github"
]
}
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Changelog

(This file is populated by semantic-release.)
49 changes: 49 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
npm run lint # ESLint (flat config, v10)
npm run build # esbuild bundle to dist/index.js + esbuild-plugin-license writes dist/licenses.txt
npm test # Jest unit tests (mocked @actions/* deps)
npm run all # lint + build + test
```

`npm run build` runs `node build.js`, which invokes esbuild with `esbuild-plugin-license` to produce `dist/index.js` and license attribution. ncc is no longer used.

## Architecture

This is a JavaScript GitHub Action that installs the [Yontrack CLI](https://github.com/nemerosa/ontrack-cli) on the runner, optionally configures it against a Yontrack instance, and optionally sets up the project + branch + auto-promotions in Yontrack. It exposes `installed` / `project` / `branch` outputs.

**Key files:**
- `index.js` — source. A top-level IIFE loads pure-ESM `@actions/*` packages via dynamic import and calls `runAction({core, exec, github, tc})`. The action's logic lives in `runAction` and four exported helpers: `configureCLI`, `configureProject`, `configureAutoPromotion`, `downloadAndSetup`. Pure helpers `mapArch`, `mapOS`, `argsWithConfig` are also exported.
- `dist/index.js` — bundled output (esbuild, CJS) committed alongside source changes. The runner executes this, not `index.js` directly.
- `dist/licenses.txt` — license attributions, generated by `esbuild-plugin-license`.
- `action.yml` — action metadata: many inputs (version, url, token, config, promotions, etc.), three outputs (installed, project, branch), `runs.using: node24`, `runs.main: dist/index.js`.
- `index.test.js` — Jest unit tests with hand-rolled mock objects.
- `build.js` — esbuild driver. Note the `.default` import for `esbuild-plugin-license` (CJS interop quirk).
- `.github/ontrack/promotions.yml` — config used by the action's self-test in CI.

**Why dependency injection:** All `@actions/*` packages at the latest majors are pure ESM. Top-level CJS imports no longer work. The IIFE loads ESM deps once at startup and passes them into `runAction`.

**Why `@actions/io` is gone:** It was only used for `io.mv` which `fs.promises.rename` covers natively for the in-tmpdir use case. Dropping it reduces the dep surface.

**Why `@actions/tool-cache` is kept:** `tc.downloadTool` handles auth, retries, and platform-specific download semantics that we'd have to reimplement otherwise.

## Release process

Releases are automated via `semantic-release` (`.releaserc`). Commit messages follow Angular convention. Every conventional-commit type triggers at least a patch release; `feat` is minor; `refactor` and any commit with a `BREAKING CHANGE:` footer is major.

The CI workflow on `main`:
1. Runs `npm run lint && npm run build && npm test`.
2. Runs the action against itself (`uses: ./`) as a self-test (gated to `nemerosa` repo owner so fork PRs don't try to authenticate).
3. Runs `npx semantic-release` — produces `vX.Y.Z` GitHub release; `@semantic-release/git` commits the regenerated `dist/`, `CHANGELOG.md`, `package.json`, `package-lock.json`, and `action.yml`.
4. Force-updates floating `vX` and `vX.Y` tags to point at the new release SHA.

The previous manual `tag.sh` flow has been removed. Existing floating `@v2` tags created by `tag.sh` are left alone (the breaking release lands as `@v3.0.0`, with new `@v3` and `@v3.0` floating tags).

## NPM registry

This repo's runtime deps come from npmjs.com (the public registry). No GH Packages auth needed.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,11 @@ npm install
To build the distribution:

```bash
ncc build
npm run build
```

This runs `node build.js`, which invokes esbuild with `esbuild-plugin-license` to produce `dist/index.js` (the bundle the action runner executes) and `dist/licenses.txt` (license attributions). To run lint, build, and tests in one go:

```bash
npm run all
```
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,5 @@ outputs:
branch:
description: Ontrack branch name
runs:
using: 'node16'
using: 'node24'
main: 'dist/index.js'
31 changes: 31 additions & 0 deletions build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const esbuild = require('esbuild');
const license = require('esbuild-plugin-license').default;

esbuild.build({
entryPoints: ['index.js'],
bundle: true,
platform: 'node',
format: 'cjs',
target: 'node24',
outfile: 'dist/index.js',
sourcemap: true,
plugins: [license({
thirdParty: {
output: {
file: 'dist/licenses.txt',
template(dependencies) {
return dependencies
.map(dep => [
dep.packageJson.name,
dep.packageJson.license,
dep.licenseText,
].filter(Boolean).join('\n'))
.join('\n\n');
},
},
},
})],
}).catch((err) => {
console.error(err);
process.exit(1);
});
Loading