You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This issue tracks the full design and implementation of an automated release pipeline for all frmscoe rule repos. The work is the frmscoe-org counterpart to tazama-lf/workflows#30 and extends the canonical improvements previously identified in #65.
It covers four interconnected areas:
publish.yml — full replacement of the old-generation file (Node 16, checkout@v3, auto-versioning, token leak)
version-check.yml — new gate workflow that blocks PRs to main if package.json still contains a prerelease version suffix
release-train.yml — new workflow_dispatch automation to prepare and open a release PR from dev → main
Reusable rule workflows — replace the 33 per-repo copies of package-rule-rc.yml / package-rule.yml with a single workflow_call reusable workflow + thin per-repo caller stubs
Background
Current state problems
publish.yml
The current publish.yml has several critical problems that go beyond the stale action versions noted in #65:
Problem
Detail
checkout@v3, setup-node@v3, Node 16
Actions and runtime EOL
push: dev trigger
Publishes every push to dev — not developer-controlled
Auto-versioning via commit message parsing
Fragile; parses [major]/[minor]/[patch] tokens from commit messages
Version-bump PR automation
Creates an additional PR for every publish — noise and merge conflicts
cat .npmrc step
Security bug — leaks npm auth token to workflow run logs
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_LIB }} at job level
Shadows the built-in GITHUB_TOKEN; token visible to all steps unnecessarily
No version gate on main
Nothing currently prevents a developer from merging a PR to main while package.json still contains -rc.N. Without a gate, the push: main trigger in the new publish.yml would publish a prerelease version as latest.
frmscoe has 33 rule repos that each carry their own full copy of these two workflow files. Combined with the 2 tazama-lf rule repos, there are 70 nearly-identical files. Any bug fix or improvement must be applied 70 times. Several known bugs remain unaddressed (see Bug Inventory below).
Library Dependency Tiers
All 33 frmscoe rule repos depend only on @tazama-lf/frms-coe-lib (plus @tazama-lf/frms-coe-startup-lib in rule-executer). They have no cross-rule dependencies. The full library tier ordering is:
TIER 1 — Foundations (release first, no internal deps):
@tazama-lf/frms-coe-lib — canonical source of all rule business logic types
@tazama-lf/auth-lib — authentication primitives
@tazama-lf/audit-lib — audit logging
TIER 2 — Rules and supplementary libraries:
@frmscoe/rule-001 .. rule-091 — all depend solely on frms-coe-lib@Tier1
@tazama-lf/frms-coe-startup-lib → frms-coe-lib
@tazama-lf/auth-lib-provider-keycloak → auth-lib ⚠️ RANGE (see note below)
TIER 3 — Services (consume Tier 1+2, no npm publish):
rule-executer, relay-service, event-director, typology-processor, etc.
Special case: auth-lib-provider-keycloak range dependency
The dep is "auth-lib": "^4.0.0-rc.4". npm behaviour: prerelease ranges only match prerelease versions on the same base. When auth-lib@4.0.0 is published stable, this range stops resolving. Release-train must convert ^4.0.0-rc.4 → ^4.0.0.
No event-name detection. The developer strips -rc.N manually in the release PR. version-check.yml enforces this gate.
Triggers (replaces push: dev)
on:
push:
branches: [main] # auto-publish stable on PR mergeworkflow_dispatch: # developer manually publishes RC from dev
Release-train: automated dep resolution
The release-train workflow_dispatch input is the target stable version (e.g. 4.0.0). The workflow:
Validates input does not contain -
For each @frmscoe/@tazama-lf dep in package.json: queries npm view @pkg dist-tags.latest — fails if stable not yet published
Converts X.Y.Z-rc.N pinned deps → X.Y.Z
Converts ^X.Y.Z-rc.N range deps → ^X.Y.Z
Commits via GitHub API (Verified commit, satisfies branch protection)
Opens PR → main with configured reviewers
Reusable workflow architecture
Each rule repo's stub is ~15 lines:
# .github/workflows/package-rule-rc.yml (stub in each frmscoe rule repo)on:
push:
branches: [dev]workflow_dispatch:
jobs:
build:
uses: frmscoe/workflows/.github/workflows/package-rule-rc.yml@devwith:
rule_number: "001"rule_org: "frmscoe"secrets: inherit
The canonical reusable file defined here holds the full job definition. rule_org input drives Dockerfile sed substitution (@tazama-lf → @frmscoe, rule-901 → rule-NNN).
Bug Inventory (current package-rule-*.yml copies)
#
Severity
Issue
Fix
1
🔴 Critical
Docker tag hardcoded as 3.0.0
Derive from package.json version
2
🔴 Critical
rule-002 only: single-quoted sed — shell variables never expand
Use double-quote pattern (already correct in all other rule repos)
3
🔴 Critical
Version fetched via GitHub API without ?ref= — reads wrong branch
Read from checked-out package.json directly
4
🟠 Major
cat .npmrc step leaks auth token to workflow logs
Remove cat steps
5
🟠 Major
GITHUB_TOKEN overridden at job level — shadows built-in
Remove; use NODE_AUTH_TOKEN/GH_TOKEN explicitly per step
6
🟡 Minor
npm install used after deleting package-lock.json — non-reproducible
Use npm ci with lock file intact
7
🟡 Minor
Action tags not SHA-pinned
Pin to verified SHAs
8
🟡 Minor
No build provenance attestation
Add actions/attest-build-provenance
9
Architecture
33 full copies in frmscoe alone — any bug fix requires 33 PRs
workflow_call reusable + caller stubs
Bugs 4 and 5 are also present in publish.yml and will be fixed as part of the publish.yml replacement.
frmscoe Rule Repo Inventory
All 33 repos currently have both package-rule-rc.yml and package-rule.yml. All will receive the thin caller stubs:
Version derived from checked-out package.json (no API call, no ?ref= needed)
npm ci, no lock file deletion, no cat .npmrc, no job-level GITHUB_TOKEN shadow
Docker tag: $VERSION-rc and :rc (moving pointer)
rule_org input drives sed: when frmscoe, substitute @tazama-lf → @frmscoe and rule-901 → rule-$rule_number in Dockerfile
Create canonical package-rule.yml as workflow_call reusable
Same fixes; Docker tags: $VERSION and :latest
Update sync-workflows.yml to stamp caller stubs (15 lines each) into all 33 frmscoe rule repos
Stub-stamping logic derives rule_number from repo name (e.g. rule-044 → "044")
Remove full-copy distribution of package-rule-rc.yml / package-rule.yml from sync
Phase 4 — Release-train workflow
Create release-train.yml in frmscoe/workflows
Mirror of tazama-lf version with @frmscoe scope
dep-resolution logic handles @tazama-lf tier-1/2 deps (rules depend on them)
Commit via GitHub API (Verified), PR → main, optional back-bump to dev
Phase 5 — Sync and distribution
Include version-check.yml and release-train.yml in sync targets
Run full sync to all 33 rule repos
Verify: caller stubs correct, no full copies left in rule repos
Security Notes
The existing publish.yml has a confirmed token-exposure bug:
- name: Print .npmrc # ← THIS LEAKS THE TOKENrun: cat .npmrc
This step prints the full contents of .npmrc which includes the //npm.pkg.github.com/:_authToken=<token> line. While GitHub Actions masks known secret values in logs, this is a bad practice and will be removed in the replacement. The same applies to any similar cat step found in package-rule-rc.yml copies.
Out of Scope
case-management-system, connection-studio, rule-studio — under active development; dev teams adopt the new pattern once established
Summary
This issue tracks the full design and implementation of an automated release pipeline for all
frmscoerule repos. The work is thefrmscoe-org counterpart to tazama-lf/workflows#30 and extends the canonical improvements previously identified in #65.It covers four interconnected areas:
publish.yml— full replacement of the old-generation file (Node 16,checkout@v3, auto-versioning, token leak)version-check.yml— new gate workflow that blocks PRs tomainifpackage.jsonstill contains a prerelease version suffixrelease-train.yml— newworkflow_dispatchautomation to prepare and open a release PR fromdev→mainpackage-rule-rc.yml/package-rule.ymlwith a singleworkflow_callreusable workflow + thin per-repo caller stubsBackground
Current state problems
publish.ymlThe current
publish.ymlhas several critical problems that go beyond the stale action versions noted in #65:checkout@v3,setup-node@v3, Node 16push: devtriggerdev— not developer-controlled[major]/[minor]/[patch]tokens from commit messagescat .npmrcstepGITHUB_TOKEN: ${{ secrets.GH_TOKEN_LIB }}at job levelGITHUB_TOKEN; token visible to all steps unnecessarilyNo version gate on
mainNothing currently prevents a developer from merging a PR to
mainwhilepackage.jsonstill contains-rc.N. Without a gate, thepush: maintrigger in the newpublish.ymlwould publish a prerelease version aslatest.package-rule-rc.yml/package-rule.yml— 35 per-repo copiesfrmscoehas 33 rule repos that each carry their own full copy of these two workflow files. Combined with the 2 tazama-lf rule repos, there are 70 nearly-identical files. Any bug fix or improvement must be applied 70 times. Several known bugs remain unaddressed (see Bug Inventory below).Library Dependency Tiers
All 33
frmscoerule repos depend only on@tazama-lf/frms-coe-lib(plus@tazama-lf/frms-coe-startup-libin rule-executer). They have no cross-rule dependencies. The full library tier ordering is:Special case:
auth-lib-provider-keycloakrange dependencyThe dep is
"auth-lib": "^4.0.0-rc.4". npm behaviour: prerelease ranges only match prerelease versions on the same base. Whenauth-lib@4.0.0is published stable, this range stops resolving. Release-train must convert^4.0.0-rc.4→^4.0.0.Design Decisions
Version string is the only publish signal
No event-name detection. The developer strips
-rc.Nmanually in the release PR.version-check.ymlenforces this gate.Triggers (replaces
push: dev)Release-train: automated dep resolution
The release-train
workflow_dispatchinput is the target stable version (e.g.4.0.0). The workflow:-@frmscoe/@tazama-lfdep inpackage.json: queriesnpm view @pkg dist-tags.latest— fails if stable not yet publishedX.Y.Z-rc.Npinned deps →X.Y.Z^X.Y.Z-rc.Nrange deps →^X.Y.Zmainwith configured reviewersReusable workflow architecture
Each rule repo's stub is ~15 lines:
The canonical reusable file defined here holds the full job definition.
rule_orginput drives Dockerfilesedsubstitution (@tazama-lf → @frmscoe,rule-901 → rule-NNN).Bug Inventory (current
package-rule-*.ymlcopies)3.0.0package.jsonversionrule-002only: single-quotedsed— shell variables never expand?ref=— reads wrong branchpackage.jsondirectlycat .npmrcstep leaks auth token to workflow logscatstepsGITHUB_TOKENoverridden at job level — shadows built-inNODE_AUTH_TOKEN/GH_TOKENexplicitly per stepnpm installused after deletingpackage-lock.json— non-reproduciblenpm ciwith lock file intactactions/attest-build-provenanceworkflow_callreusable + caller stubsBugs 4 and 5 are also present in
publish.ymland will be fixed as part of thepublish.ymlreplacement.frmscoe Rule Repo Inventory
All 33 repos currently have both
package-rule-rc.ymlandpackage-rule.yml. All will receive the thin caller stubs:rule-001,rule-002,rule-003,rule-004,rule-005,rule-006,rule-007,rule-008,rule-009,rule-010,rule-011,rule-012,rule-013,rule-014,rule-016,rule-017,rule-018,rule-019,rule-020,rule-021,rule-022,rule-024,rule-025,rule-026,rule-028,rule-030,rule-044,rule-045,rule-048,rule-054,rule-063,rule-074,rule-075,rule-076,rule-078,rule-083,rule-084,rule-090,rule-091(Numbers in the sequence that are absent — e.g. 015, 023 — have been confirmed non-existent in the org.)
Implementation Plan
Phase 1 —
publish.ymlreplacement (resolves #65 item 1)publish.ymlwith new canonical matching tazama-lf form:@frmscoescope, Node 20,actions/checkout@v4,actions/setup-node@v4push: branches: [main]+workflow_dispatch*-*→--tag rc, elselatest)cat .npmrc, job-levelGITHUB_TOKENoverrideNODE_AUTH_TOKENper stepPhase 2 — Version enforcement (standalone)
version-check.ymlpull_requesttargetingmainjq -r '.version' package.json→ fail with clear message if result contains-Phase 3 — Reusable rule workflows (resolves #65 item 2)
package-rule-rc.ymlasworkflow_callreusable infrmscoe/workflowsrule_number(string),rule_org(string, defaultfrmscoe)GH_TOKEN_LIB,DOCKER_USERNAME,DOCKER_PASSWORD,SLACK_WEBHOOK_URLpackage.json(no API call, no?ref=needed)npm ci, no lock file deletion, nocat .npmrc, no job-levelGITHUB_TOKENshadow$VERSION-rcand:rc(moving pointer)rule_orginput drives sed: whenfrmscoe, substitute@tazama-lf → @frmscoeandrule-901 → rule-$rule_numberin Dockerfilepackage-rule.ymlasworkflow_callreusable$VERSIONand:latestsync-workflows.ymlto stamp caller stubs (15 lines each) into all 33 frmscoe rule reposrule_numberfrom repo name (e.g.rule-044→"044")package-rule-rc.yml/package-rule.ymlfrom syncPhase 4 — Release-train workflow
release-train.ymlinfrmscoe/workflows@frmscoescope@tazama-lftier-1/2 deps (rules depend on them)main, optional back-bump todevPhase 5 — Sync and distribution
version-check.ymlandrelease-train.ymlin sync targetsSecurity Notes
The existing
publish.ymlhas a confirmed token-exposure bug:This step prints the full contents of
.npmrcwhich includes the//npm.pkg.github.com/:_authToken=<token>line. While GitHub Actions masks known secret values in logs, this is a bad practice and will be removed in the replacement. The same applies to any similarcatstep found inpackage-rule-rc.ymlcopies.Out of Scope
case-management-system,connection-studio,rule-studio— under active development; dev teams adopt the new pattern once establishedRelated Issues
publish.ymlandpackage-ruleitems there)node.js.yml(parallel, independent track)publish.ymlpush: maintrigger