ci(release): add automated release workflow with nx release#924
Conversation
- workflow_dispatch trigger with optional bump override and dry-run - OIDC trusted publishing + npm provenance (id-token: write) - Test gate (lint/test/build) before version/publish - Concurrency lock + branch guard to master - Workspace changelog wired to root CHANGELOG.md with one GH release per version Supersedes the manual local-dev/publish-all-libs.sh flow.
|
Too much diff to scan? Review this PR in Change Stack to start with the highest-impact changes. Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a Release GitHub Actions workflow (manual dispatch) that computes an Nx version, optionally publishes packages, pushes commit/tags, generates changelog/GitHub release, and writes a run summary; adds an actionlint workflow for workflow files; updates nx.json to point the workspace changelog to CHANGELOG.md. ChangesRelease Automation Workflow
Actionlint Workflow
Fun fact: "transloco" is about translations—learn a word: in Japanese, "translation" is 翻訳 (honyaku). 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
@jsverse/transloco
@jsverse/transloco-locale
@jsverse/transloco-messageformat
@jsverse/transloco-optimize
@jsverse/transloco-persist-lang
@jsverse/transloco-persist-translations
@jsverse/transloco-preload-langs
@jsverse/transloco-schematics
@jsverse/transloco-scoped-libs
@jsverse/transloco-utils
@jsverse/transloco-validator
commit: |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
.github/workflows/release.yml (1)
38-38: ⚖️ Poor tradeoffConsider pinning action to commit SHA for supply-chain security.
Using tag references (e.g.,
@v4) rather than commit SHAs makes the workflow vulnerable to tag manipulation attacks. For a security-sensitive release workflow, pinning to immutable commit SHAs is recommended.🔐 Optional SHA pinning
- - name: Checkout - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2Apply the same pattern to line 45 (setup-node). You can use tools like
pin-github-actionor Dependabot to maintain pinned SHAs.Fun fact: The word "security" comes from Latin securus (se- "without" + cura "care"). In Japanese, it's セキュリティ (sekyuriti), a direct loanword! 🔐
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release.yml at line 38, Replace mutable tag refs with immutable commit SHAs: update the uses: entry "actions/checkout@v4" to the corresponding commit SHA (e.g., actions/checkout@<commit-sha>) and do the same for the "actions/setup-node@vX" reference on the other line; locate these by the literal strings "actions/checkout@v4" and "actions/setup-node" in the workflow, fetch the correct pinned commit SHAs from the actions' GitHub repos (or use a tool like pin-github-action/Dependabot), and update the workflow to use the full commit SHAs to prevent tag manipulation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Around line 37-42: Update the checkout action (uses: actions/checkout@v4) to
add persist-credentials: false so the GITHUB_TOKEN is not persisted to the
workspace; then adjust the later push step that performs git push to use an
explicit token (e.g., a PAT from secrets) or perform a separate checkout
configured with credentials so pushes still work without relying on the
persisted GITHUB_TOKEN.
- Around line 78-80: The GitHub Actions step "Read version" (id: version) uses a
run line with improperly escaped double quotes causing shell parsing errors;
replace the current node -p invocation so the JS string is quoted safely (e.g.,
use single quotes around the node expression or wrap the whole run value as a
YAML block) to avoid backslashes—update the run for the "Read version" step to
call node -p with proper single-quoting of
"require('./libs/transloco/package.json').version" or convert the run to a
multiline/| block so no shell-escaping is necessary.
- Around line 11-12: Replace the invalid empty choice option by changing the
input from a choice to a string: remove the "options:" block that contains "-
''" and switch the input declaration to "type: string" (locate the "options"
entry in the workflow diff), then update the Version step's validation logic
(the step named "Version") to explicitly validate allowed values (e.g., via a
conditional or regex check) so only valid release values are accepted.
---
Nitpick comments:
In @.github/workflows/release.yml:
- Line 38: Replace mutable tag refs with immutable commit SHAs: update the uses:
entry "actions/checkout@v4" to the corresponding commit SHA (e.g.,
actions/checkout@<commit-sha>) and do the same for the "actions/setup-node@vX"
reference on the other line; locate these by the literal strings
"actions/checkout@v4" and "actions/setup-node" in the workflow, fetch the
correct pinned commit SHAs from the actions' GitHub repos (or use a tool like
pin-github-action/Dependabot), and update the workflow to use the full commit
SHAs to prevent tag manipulation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 78646dfd-22b2-40c2-9d4b-f283c47f4a27
📒 Files selected for processing (2)
.github/workflows/release.ymlnx.json
There was a problem hiding this comment.
2 issues found across 2 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/release.yml">
<violation number="1" location=".github/workflows/release.yml:12">
P1: GitHub Actions `type: choice` inputs do not permit empty strings in the `options` list. This causes actionlint to fail with `string should not be empty [syntax-check]`. Switch to `type: string` with a `default: ''` instead, and validate the input value in the Version step to ensure only `patch|minor|major|prerelease` are accepted.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
- Replace empty-string choice option with explicit 'auto' sentinel
(empty option values are invalid in workflow_dispatch choice inputs)
- Replace fragile `\"`-escaped command substitution with a two-line
shell block that's unambiguous to shellcheck
- Move all `${{ ... }}` interpolations into env: blocks before they hit
shell, preventing GH Actions expression injection patterns
Lints workflow YAML on PR/push when files under .github/workflows or .github/actions change. Mirrors the cx-web-workspace pattern: pinned actionlint v1.7.12, cached at ~/bin/actionlint, installed if cache miss.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/actionlint.yml:
- Line 23: Update the actions/checkout@v4 step to disable credential persistence
by adding a with: block that sets persist-credentials: false; locate the
checkout usage (the line containing "uses: actions/checkout@v4") and add the
with: mapping under that step so the workflow only checks out repo contents
without persisting credentials.
- Line 23: Replace the floating action refs by pinning actions/checkout@v4 and
actions/cache@v4 to their immutable commit SHAs (e.g.,
actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac and the full commit
SHA for the v4 of actions/cache) and add the persist-credentials: false option
to the checkout step (ensure the checkout step containing actions/checkout is
updated to include the persist-credentials: false key).
In @.github/workflows/release.yml:
- Around line 81-85: In the "Read version" step, avoid reading the on-disk
package.json when this run is a dry-run: detect the dry-run flag (e.g., check
the workflow input or env like github.event.inputs.dry-run or a DRY_RUN variable
set earlier) and if it's a dry-run, set VERSION to a clear placeholder (for
example "preview" or "dry-run") and echo that to GITHUB_OUTPUT; otherwise
preserve the existing node -p require(...) behavior. Ensure you still write
"value=$VERSION" to GITHUB_OUTPUT in both branches so downstream steps consume
the placeholder during previews.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 89c529aa-ec7b-4748-8c53-4f1005a1a5ee
📒 Files selected for processing (2)
.github/workflows/actionlint.yml.github/workflows/release.yml
- actions/checkout v4 -> v6 - actions/setup-node v4 -> v6 - actions/cache v4 -> v5 Pre-existing workflows (ci.yml, deploy-demo-app.yml, stale-issues.yml, step-setup composite) intentionally left alone here to keep this PR focused on the release workflow. Tracked as a follow-up.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/release.yml (1)
97-102:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winFix dry-run guards to use
inputs['dry-run'](string) comparison
workflow_dispatchboolean inputs are exposed to expressions as strings ('true'/'false'), and the hyphenated keydry-runmust be accessed via bracket notation; update bothif:guards accordingly to reliably prevent push/release during dry-run.🔧 Proposed fix
- name: Push commit and tag - if: ${{ inputs.dry-run != 'true' }} + if: ${{ inputs['dry-run'] == 'false' }} run: git push --follow-tags origin HEAD:master - name: Changelog and GitHub release - if: ${{ inputs.dry-run != 'true' }} + if: ${{ inputs['dry-run'] == 'false' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: npx nx release changelog ${{ steps.version.outputs.value }} --verboseTransloco fun fact: when using lazy-loaded scopes, translations load asynchronously—calling
translate/selectTranslatebefore the scope is triggered can temporarily yield missing-key results.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release.yml around lines 97 - 102, The conditional guards using inputs.dry-run are invalid because the hyphenated input must be accessed with bracket notation and inputs are strings; update both if: expressions (the one guarding the git push step "git push --follow-tags origin HEAD:master" and the step titled "Changelog and GitHub release") to use inputs['dry-run'] and compare against the string 'true' (e.g., if: ${{ inputs['dry-run'] != 'true' }}) so the push/release steps are correctly skipped during a dry-run.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In @.github/workflows/release.yml:
- Around line 97-102: The conditional guards using inputs.dry-run are invalid
because the hyphenated input must be accessed with bracket notation and inputs
are strings; update both if: expressions (the one guarding the git push step
"git push --follow-tags origin HEAD:master" and the step titled "Changelog and
GitHub release") to use inputs['dry-run'] and compare against the string 'true'
(e.g., if: ${{ inputs['dry-run'] != 'true' }}) so the push/release steps are
correctly skipped during a dry-run.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 63da4080-5c66-4d90-bf02-737041fd5682
📒 Files selected for processing (2)
.github/workflows/actionlint.yml.github/workflows/release.yml
Summary
Replaces the manual
local-dev/publish-all-libs.shflow with aworkflow_dispatch-triggered GitHub Actions pipeline that drivesnx releaseend-to-end (version → publish → tag → changelog → GitHub release), plus a standaloneactionlintworkflow that lints workflow YAML on every relevant PR.Publishing uses npm OIDC trusted publishing — no
NPM_TOKENsecret, with provenance attestations generated automatically.Release workflow (
.github/workflows/release.yml)Two jobs: a parallel
gate(lint / test / e2e matrix) and areleasejob that runs only when the gate passes (needs: gate).Inputs
bumpautoautoinfers from conventional commits; orpatch/minor/major/prereleasedry-runfalseOrdering (and why each constraint is load-bearing)
gate → version → build → publish → push (commit + tag) → changelog → push (changelog commit)versionbeforebuild—nx release versionbumps the sourcepackage.jsonfiles; ng-packagr then copies them intodist/at build time. Building first would publish tarballs stamped with the previous version (instant npm registry rejection).publishbefore any push — a failed publish then leaves a clean rollback: the version commit and tag exist only on the runner, never onmaster.push(tag) beforechangelog— the changelog step creates the GitHub Release forreleases/{version}; if that tag isn't on the remote yet, the API invents it at the oldmasterHEAD.push(changelog commit) last — the changelog step commitsCHANGELOG.md+ per-project changelogs locally, so a final push lands them onmaster.Auth (OIDC trusted publishing)
npm install -g npm@11. (Node 24 isn't supported by Angular 18 yet.)registry-urlonsetup-node, deliberately — it writes an_authToken=${NODE_AUTH_TOKEN}line into.npmrcthat can shadow OIDC auth. npmjs is the default registry; trusted publishing needs no token config at all.--provenanceflag.Dry-run semantics
Gate, version preview, and a full build all run; publish / push / changelog are skipped (gated on
!inputs.dry-run) rather than passed--dry-run. Reasons: a version dry-run leaves files untouched, sonpm publish --dry-runwould always collide with the already-published version, and the run summary would otherwise misreport the on-disk (current) version as the preview.Prerelease dist-tag
bump: prereleasepublishes under thenextdist-tag, solatest(whatnpm i @jsverse/translocoresolves) never points at a prerelease. This makesprereleasethe low-stakes path for the first real OIDC publish.Changelog config (
nx.json)workspaceChangelogblock writes the rootCHANGELOG.mdand creates one GitHub release per version; per-project changelogs preserved.version.gitcommits + tags,changelog.gitcommits) — top-levelrelease.gitis rejected whennx releaseis driven via individual subcommands, and the subcommand git defaults are all-false (so without this, no tag is ever created and the changelog commit is orphaned on the runner).Publish target fix (
nx.json)nx release publishdefaultspackageRootto the project root, i.e. it would have packedlibs/*— the source dirs, whose manifests have nomain/module/typings/exports(ng-packagr generates those intodist/libs/*/package.json). All 11 packages would have shipped broken. Fixed viatargetDefaults:Actionlint workflow (
.github/workflows/actionlint.yml)Pinned
actionlint v1.7.12, cached at~/bin/actionlint, installed only on cache miss. Runs on PR + push tomasterwhen files under.github/workflows/**or.github/actions/**change. Replaces the previousreviewdog.yml(removed in this PR) — reviewdog's diff-filtering only reported findings on lines the PR touched, which had been hiding a deprecation in its own workflow file; the raw runner lints every workflow file every time.Bugs caught and fixed during development
Workflow YAML only fails at runtime, so each was caught by linting or local
--dry-runrehearsal before it could fail a real release:''choice option (actionlint) — invalidworkflow_dispatchchoice; switched to an explicitautosentinel.\"-escapednode -p(shellcheck via actionlint) — replaced with a clean shell block.inputs.dry-run != 'true'— boolean inputs are real booleans in expressions; comparing against the string'true'makes GitHub coerce both to numbers (true→1,'true'→NaN), so the guard was always-true and dry-runs would have pushed/released. Fixed to!inputs.dry-run.packageRootfix above.release.gitrejected + no tag configured — would fail at the Version step on first run.Security
github.refguard on both jobs); dry-runs may be dispatched from any branch — side-effect-free by construction (every mutating step gated on!inputs.dry-run), making the pipeline testable from a PR branchconcurrency: { group: release-${{ github.ref }}, cancel-in-progress: false }— real releases serialize; a branch dry-run never queue-blocks a real releaseregistry-urltoken line in.npmrc; gate checkouts usepersist-credentials: false${{ ... }}interpolations routed throughenv:, never inlined intorun:contents: write(commit/tag/release) +id-token: write(OIDC). Nopackages: write..github/Pre-flight before first non-dry run
@jsverse/*, pointed at this repo +release.ymlmasterrestricts who can dispatch workflows (or attach a GH Environment with required reviewers)git log releases/8.3.0..HEAD --onelinedry-run: trueas the first real testTest plan
masterwithdry-run: true,bump: auto— verify gate legs run in parallel, the inferred bump matches expectation (should bepatch), build succeeds, and publish/push/changelog are skipped with no side effectsdry-run: true,bump: patch— verify the override path previews a patch bumpbump: prerelease,dry-run: false— verify OIDC publish + provenance land on thenextdist-tag (e.g.8.3.1-0), GH release created, tagreleases/X.Y.Zonmaster, rootCHANGELOG.mdupdated — a low-stakes rehearsal that never toucheslatestFollow-ups (intentionally out of scope)
schematics-coresync —libs/schematics-core/is copied intolibs/transloco/schematics-core/andlibs/transloco-schematics/schematics-core/via a manual gitignored script (local-dev/sync-schematics-core.sh), not wired into any build target. With automated releases, drift could ship silently. Follow-up: move it to a trackedscripts/location, run before build inci.yml+release.yml, add a pre-commit drift guard.ci.yml,deploy-demo-app.yml,stale-issues.yml, and thestep-setupcomposite still use older majors (checkout@v4,setup-node@v4,stale@v9,configure-pages@v5,upload-pages-artifact@v3,deploy-pages@v4). Single chore PR. The workflows added here are already on latest majors.github-actions(none exists today — pins without automated bumps rot silently).local-dev/publish-all-libs.sh— kept as an emergency manual fallback for now.