feat(ci): release pipeline + stop tracking dist on main#16
Conversation
| # workflow for how dist/index.js + dist/index.js.map land on the dist branch. | ||
| # Local `pnpm build` still produces dist/ for typecheck/tests; just don't | ||
| # commit it. | ||
| */dist/ |
There was a problem hiding this comment.
🔴 CONTRIBUTING.md rules violated: new */dist/ gitignore contradicts "commit dist/" instructions
The PR adds */dist/ to .gitignore (line 14), which makes it impossible to follow the CONTRIBUTING.md instructions that explicitly say to "Run pnpm build and commit the generated dist/" (Node.js action step 6) and "Commit dist/ like a normal Node.js action" (Hybrid action step 5). The CONTRIBUTING.md also describes a release process (manual git tag + gh release create triggering the release workflow) that no longer applies now that release.yml is workflow_dispatch. Since CONTRIBUTING.md is a mandatory rule file, the PR should update it to reflect the new architecture (orphan dist branches, no committed bundles on main, new workflow_dispatch release process).
Prompt for agents
The PR adds `*/dist/` to .gitignore, which means dist/ folders can no longer be committed to main. This directly contradicts the CONTRIBUTING.md instructions in multiple places:
1. Node.js action step 6: Run pnpm build and commit the generated dist/
2. Hybrid action step 5: Commit dist/ like a normal Node.js action
3. The Releasing section describes manually creating tags and GitHub releases, but the workflow is now workflow_dispatch
4. The repository structure comment says release.yml Runs on release publish but it now runs on workflow_dispatch
5. The Required secrets section mentions ACTIONS_RELEASE_TOKEN but the new workflow uses GITHUB_TOKEN
CONTRIBUTING.md needs to be updated to describe the new architecture: dist bundles are published to orphan branches dist/<action> via the release workflow, not committed to main. The release process is now triggered via workflow_dispatch in the GitHub Actions UI. Contributors should NOT commit dist/ to main.
Was this helpful? React with 👍 or 👎 to provide feedback.
| # See /Users/fedezara/.claude/plans/help-me-setting-up-velvety-sutherland.md | ||
| # for design rationale (orphan branches, env-var injection, ledger format). |
There was a problem hiding this comment.
🟡 Local developer filesystem path leaked in release.yml comment
Line 11 of the release workflow contains # See /Users/fedezara/.claude/plans/help-me-setting-up-velvety-sutherland.md — a hardcoded reference to a developer's local filesystem path. This is meaningless to anyone else reading the file and leaks the developer's username and local tooling details into the repository.
| # See /Users/fedezara/.claude/plans/help-me-setting-up-velvety-sutherland.md | |
| # for design rationale (orphan branches, env-var injection, ledger format). | |
| # See the PR description for design rationale (orphan branches, env-var | |
| # injection, ledger format). |
Was this helpful? React with 👍 or 👎 to provide feedback.
b11b5c0 to
7cb687e
Compare
…ry sourcemap upload
Replaces release.yml (which only moved alias tags on GitHub Release publish)
with a workflow_dispatch pipeline that:
- Bakes RELEASE_TAG / POSTHOG_API_KEY / SENTRY_DSN_AUTOMATIONS /
AUTOMATION_EVENT_API_URL into the bundle via tsup's `env` option
- Builds the action with sourcemaps enabled
- Uploads sourcemaps to Sentry, tied to a release named <action>@<version>
- Publishes the artifact to an orphan branch `dist/<action>` with a
commit linking back to the source SHA on main
- Tags <action>@<version> on the dist branch commit
- Moves floating <action>@v<major> / <action>@v<major>.<minor> aliases
(skipped for prereleases)
- Appends an entry to <action>/RELEASES.md on main and pushes a
`chore(release): <action>@<version>` marker commit (best-effort; logs
a warning if branch protection rejects the push)
- Creates a GitHub Release for the new tag
Telemetry constants now read from process.env with no-op fallbacks so local
dev / tests / non-release CI keep working. RELEASE_TAG is wired through to
Sentry.init({ release }), PostHog $lib_version, and the actions_version
field on log payloads + Lightweight API events.
Sourcemaps (*/dist/*.map) are gitignored on main — they ride along on the
dist branches and are uploaded to Sentry per release.
See /Users/fedezara/.claude/plans/help-me-setting-up-velvety-sutherland.md
for design rationale (per-action orphan branches over single shared dist;
RELEASES.md ledger format; sibling-ref pinning roadmap).
Action bundles are now published to per-action orphan branches
`dist/<action>` by the release workflow added in the previous commit.
Consumers should pin to a tag (e.g. setup-cli@v1) — those tags point at
commits on the dist branches that contain the artifact. Direct
`uses: fern-api/actions/<action>@main` references won't resolve anymore.
Changes:
- `git rm` 9 tracked dist/index.js files (one per Node.js action,
including example-action). Pre-existing history of these files is
preserved on the rebase ancestor — `git log --follow` still works.
- .gitignore: replace the old "must commit dist" rule with `*/dist/`
(action dirs) + `packages/*/dist/` (shared package).
- ci.yml: drop the "Check dist is committed" step (no longer applies)
and expand the "Verify bundles exist" check to cover all 9 actions
(was hardcoded to 4 — `example-action preview upgrade verify` — and
silently skipping the other 5).
Backward compatibility: no existing tags in this repo, so nothing to
break. If tags had existed, they'd keep working because they're
immutable pointers to historical commits that still have dist files in
their trees.
7cb687e to
f47ab0f
Compare
The old doc was inconsistent with reality in several places:
- Repo structure: claimed setup-cli, resolve-cli, verify-token were
composite actions and described preview as hybrid. All 8 production
actions are Node.js (`runs.using: node20`, `runs.main: dist/index.js`)
since the conversion in PR #12.
- "Adding a new action" Node.js step 6 + hybrid step 5 told contributors
to commit dist/. Dist is now gitignored on main; bundles are published
to per-action orphan branches by the release workflow.
- "Releasing" section described the manual `git tag` + `gh release create`
flow. Releases are now workflow_dispatch.
- Required secrets section listed ACTIONS_RELEASE_TOKEN. The new pipeline
uses Sentry/PostHog vars + secret instead.
- release.yml is no longer "Runs on release publish".
Also drops the composite + hybrid action sections — no current actions
use those patterns; they're cleaner to add back if/when needed.
With dist/ no longer tracked on main, two CI jobs broke:
- `Lint GitHub Actions`: actionlint statically resolves `runs.main:
dist/index.js` against the working tree. With dist gone, every local
`uses: ./<action>` reference (currently `uses: ./resolve-cli` in
test-resolve-cli.yml) fails the action-existence check.
- `Test resolve-cli`: the matrix uses `uses: ./resolve-cli`, which
crashes at runtime with "File not found: ./resolve-cli/dist/index.js".
Fix: add `pnpm install` + `pnpm build` steps before both. The actionlint
job builds all actions so any future `uses: ./<action>` reference also
resolves; the resolve-cli test only builds shared + resolve-cli.
Also rename the Sentry secret reference to `FERN_SENTRY_AUTH_TOKEN` so the
same secret value used by the fern repo's publish workflows covers this
repo too. The in-container env var stays `SENTRY_AUTH_TOKEN` (what
sentry-cli and getsentry/action-release@v1 look for).

Stacked on top of #15. Set the base to
FedeZara/fer-10253-obs-foundationsso the diff scopes to just the release-pipeline work; once #15 lands the base will auto-retarget tomain.Summary
Replaces the old
release.yml(which only moved alias tags on GitHub Release publish) with aworkflow_dispatchpipeline that builds an action with secrets baked in, uploads sourcemaps to Sentry, and publishes the artifact to a per-action orphan branchdist/<action>. Also removesdist/index.jsfrom main since artifacts now live on the dist branches.What's in this PR
Commit 1 —
feat(ci): add release workflow with build-time env injection and Sentry sourcemap uploadThe new pipeline (
workflow_dispatchonly, no automatic triggers):example-action, hard-fail if the tag already exists (tags are immutable)pnpm install→ build shared → build the selected action withRELEASE_TAG=<action>@<version>,POSTHOG_API_KEY,SENTRY_DSN_AUTOMATIONS,AUTOMATION_EVENT_API_URLexported as env vars. AssertsRELEASE_TAGwas substituted into the bundle (catches the case where injection silently no-ops)getsentry/action-release@v1creates the release and uploads sourcemaps withurl_prefix: '~/'. Gated onSENTRY_AUTH_TOKENbeing present so the pipeline runs end-to-end before Sentry is provisioneddist/<action>, copy in the freshly builtdist/+action.yml, commit withSource:/Built from:body, push, then tag<action>@<version>on that commit<action>@v<major>and<action>@v<major>.<minor>aliases (skipped on prerelease)gh release createagainst the new tagscripts/append-release-entry.mjsto prepend an entry to<action>/RELEASES.mdon main, then commitschore(release): <action>@<version>. Best-effort: a failed push (e.g. from branch protection) logs a warning, doesn't roll back the releaseConcurrency:
group: release-${{ inputs.action }}, cancel-in-progress: false. Two parallel releases of different actions are fine; two of the same action serialize.Build-time env injection
packages/shared/src/telemetry/build-constants.tsnow readsprocess.env.X ?? <fallback>instead of being hardcoded. tsup'senvoption substitutes those reads with string literals at bundle time (shared between all tsup configs viascripts/build-env.ts). Local dev / tests / non-release CI: env vars unset → fallbacks → telemetry no-ops. Release CI: env vars set → values baked into the bundle.New constant:
RELEASE_TAG(defaults to\"dev\"). Wired into:init({ release })for stack-trace deobfuscation$lib_version+actions_versionpropertiesactions_versionfieldactions_versionPer-action
RELEASES.mdledgerscripts/append-release-entry.mjsmaintains a human-readable per-action release log on main:Per-action files (not a single repo-root file) so concurrent releases of different actions never race on the same file. Idempotent: re-running with an already-recorded version is a no-op.
Commit 2 —
build: stop tracking dist/index.js on maingit rm9 trackeddist/index.jsfiles (one per Node.js action, including example-action).gitignore: replace the old "must commit dist" rule with*/dist/(action dirs) +packages/*/dist/(shared package)ci.yml: drop the now-obsolete "Check dist is committed" step and expand the bundle-existence check to cover all 9 actions (was hardcoded to 4 —example-action preview upgrade verify— silently skipping the other 5)Design decisions worth flagging
dist/setup-cli,dist/sync-openapi, …) instead of a single shareddistbranch. Cleaner per-actiongit log, safer under concurrent releases, and sets up sibling-ref pinning cleanly for composite actions. See the plan file for the full rationale (alternatives considered: ephemeral per-release branches, shareddistbranch).Source: <main-sha>) so the orphan-history boundary doesn't lose source traceability. Plus per-actionRELEASES.mdon main as a second, human-readable index.chore(cli): release X.Y.Z) for org-idiom consistency.Backward compatibility
Zero existing tags in this repo (verified with
git tag -landgit ls-remote --tags origin), so nothing to break. Hypothetically, if old tags existed, they'd keep working because they're immutable pointers to historical commits that still have dist files in their trees. The only thing that breaks: consumers usinguses: fern-api/actions/<action>@maindirectly — they should pin to a tag.Required repo configuration before first real release
SENTRY_AUTH_TOKENproject:releasesscope. Without it, the sentry-release job auto-skipsSENTRY_ORGbuildwithfernSENTRY_PROJECTautomations-actionsPOSTHOG_API_KEYSENTRY_DSN_AUTOMATIONSAUTOMATION_EVENT_API_URLTest plan
RELEASE_TAG=verify-token@v0.0.1-test pnpm buildproduces a bundle containing that literal; default build contains the\"dev\"fallbackscripts/append-release-entry.mjssmoke-tested for fresh write, prepend, and idempotent retryverify-tokenwithversion=v0.0.1-test1, prerelease=true(no Sentry secret) — verify orphan branch creation, tag push, ledger entry, GitHub Releasegit show verify-token@v0.0.1-test1:verify-token/dist/index.js | grep -F 'verify-token@v0.0.1-test1'uses: fern-api/actions/verify-token@v0.0.1-test1runs end-to-endv0.0.1-test2, verify sourcemap upload and stack-trace deobfuscation on a forced errorOut of scope (follow-ups)
generate/action.ymlreferencesverify-token@maintoday — the new workflow has the seam to sed-rewrite this at release time, but the auto-resolution to "latest stable sibling" isn't implemented)actions/attest-build-provenance) — worth adding for supply-chain, tracked separatelymainrejects direct pushes (the workflow logs a warning and continues today)