Skip to content

feat(ci): flat dist layout + slash-separated release tags#19

Merged
FedeZara merged 1 commit into
mainfrom
FedeZara/flat-dist-and-slash-tags
May 18, 2026
Merged

feat(ci): flat dist layout + slash-separated release tags#19
FedeZara merged 1 commit into
mainfrom
FedeZara/flat-dist-and-slash-tags

Conversation

@FedeZara
Copy link
Copy Markdown
Contributor

@FedeZara FedeZara commented May 18, 2026

Closes the consumer-syntax gap introduced by PR #16 — see the failure write-up at the bottom for the concrete error that motivated this PR.

Summary

Restructures the release pipeline so action releases are consumable via uses: fern-api/actions@<action>/<version> (no path component). Two coupled changes that share the same motivation:

  1. Flat dist layout. publish-dist now puts action.yml and dist/ at the root of dist/<action> (was: under a ${ACTION}/ subdir mirroring main's layout). The publish step wipes the working tree on every release, which also handles migration from the legacy subdir layout transparently — the next release of each action will commit the new flat layout on top of the existing dist branch history.

  2. Slash-separated tags. Tag format is now <action>/<version> (e.g. setup-cli/v4.1.0) instead of <action>@<version>. Applied uniformly: validate-step collision check, RELEASE_TAG build env, sentry-release.with.version, publish-dist git tag, alias-tags major/minor, github-release tag, marker commit messages, and the ledger entry rendered by scripts/append-release-entry.mjs.

Why this combination

GitHub Actions parses uses: owner/repo/path@ref by anchoring @ to the end of the string. With the old <action>@<version> tag format, consuming the action required uses: fern-api/actions/<action>@<action>@<version> (two @s), which the parser splits on the last @ — giving ref=<version>, which doesn't exist as a standalone tag. Every tag the old pipeline produced was unconsumable via the documented uses: syntax.

The two fixes are coupled because:

  • Slash tags alone aren't enough: with the subdir layout, the consumer string becomes fern-api/actions/<action>@<action>/<version> — parses cleanly but reads as the duplicated <action> because the subdir name matches the tag prefix.
  • Flat layout alone isn't enough: with @ tags, the consumer string becomes fern-api/actions@<action>@<version> — parses (the regex greedily backtracks to the first @ when there's no /path to bound it), but it's brittle and reads oddly.

Together they give the conventional shape: uses: fern-api/actions@setup-cli/v1, matching actions/checkout@v4, docker/build-push-action@v5, etc.

Layout Tag format Consumer string Verdict
${action}/ subdir <action>@<version> fern-api/actions/<action>@<action>@<version> broken
${action}/ subdir <action>/<version> fern-api/actions/<action>@<action>/<version> works, ugly
root (this PR) <action>@<version> fern-api/actions@<action>@<version> works, brittle
root (this PR) <action>/<version> (this PR) fern-api/actions@<action>/<version> cleanest

Backward compatibility

The two prerelease tags created with the old format (verify-token@v0.0.1-test, preview@v0.0.1-ci-test) remain in place but are unconsumable. They're prereleases, marked as such on GitHub — not worth deleting, not worth migrating. Flagged in CONTRIBUTING.md's backward-compat section.

@main examples in upgrade/README.md and resolve-cli/README.md are unchanged. Those use the source-tree layout (action at <action>/) and stay parseable because main has no @.

Test plan

  • pnpm typecheck && pnpm test — all 58 shared tests + per-action tests pass (no behavior changes, only string-format edits + a restructured shell block in publish-dist).
  • actionlint .github/workflows/release.yml — clean.
  • Smoke-test scripts/append-release-entry.mjs with ACTION=verify-token VERSION=v1.0.0 — produces the new slash-formatted Tag: and Sentry release: lines, file written to verify-token/RELEASES.md correctly.
  • Dispatch the workflow against verify-token with version=v0.0.2-test, prerelease=true. Verify:
    • new tag verify-token/v0.0.2-test is created
    • the dist/verify-token branch's tip commit contains action.yml + dist/ at the root (no verify-token/ subdir, no leftover files from the legacy layout)
    • uses: fern-api/actions@verify-token/v0.0.2-test resolves end-to-end from a consumer repo (use fern-api/federico-automations-tests — it already has the smoke-test workflow ready)
    • the verify-token/RELEASES.md ledger entry on main reflects the new format (Tag: verify-token/v0.0.2-test, Sentry release: verify-token/v0.0.2-test)
  • Confirm the baked RELEASE_TAG constant inside the bundle is verify-token/v0.0.2-test: git show verify-token/v0.0.2-test:dist/index.js | grep -F 'verify-token/v0.0.2-test'
  • Sentry release identifier matches the bundle's baked RELEASE_TAG (so sourcemap deobfuscation resolves).

Out of scope

Failure that motivated this PR

A consumer workflow in fern-api/federico-automations-tests pinned to uses: fern-api/actions/verify-token@v0.0.1-test errored with:

Unable to resolve action `fern-api/actions@v0.0.1-test`,
unable to find version `v0.0.1-test`

The parser stripped the verify-token path component (since v0.0.1-test isn't a valid ref) and reported the bare repo + the truncated ref. Root cause is the @ in the tag name — see the "Why this combination" section above.

Restructures the release pipeline so action releases are consumable via
`uses: fern-api/actions@<action>/<version>` (no path component). Two
coupled changes:

1. Dist branch layout — `publish-dist` now puts `action.yml` and `dist/`
   at the *root* of `dist/<action>` (was: under a `${ACTION}/` subdir
   mirroring main's layout). The subdir was structurally redundant on a
   per-action orphan branch, and dropping it lets consumers reference the
   tag without a path component — the standard `owner/repo@tag` shape the
   ecosystem expects (`actions/checkout`, `docker/build-push-action`, etc.).
   The publish step now wipes the working tree on every release, which
   also handles migration from the legacy subdir layout transparently.

2. Tag format — `<action>/<version>` (slash) instead of `<action>@<version>`
   (at-sign), everywhere: validate, build env `RELEASE_TAG`, sentry-release
   version, publish-dist tag, alias-tags major/minor, github-release tag,
   marker commit messages, and the ledger entry written by
   `scripts/append-release-entry.mjs`. The at-sign in the old tag format
   was unconsumable: GitHub Actions parses `owner/repo/path@ref` by
   anchoring on the final `@`, so `fern-api/actions/verify-token@<tag>`
   would split as path=`verify-token`, ref=`v0.0.1-test`, never finding
   the actual `verify-token@v0.0.1-test` tag.

Docs updated to match (CONTRIBUTING.md, root README, preview/, setup-cli/,
sync-openapi/ READMEs). `@main` examples in upgrade/ and resolve-cli/
READMEs are unchanged — those use the source-tree layout and stay
parseable because `main` has no `@`.

The two prerelease tags created with the old format
(`verify-token@v0.0.1-test`, `preview@v0.0.1-ci-test`) remain in place
but are unconsumable; flagged in CONTRIBUTING.md's backward-compat note.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@FedeZara FedeZara marked this pull request as ready for review May 18, 2026 14:15
@FedeZara FedeZara requested a review from Swimburger as a code owner May 18, 2026 14:15
@FedeZara FedeZara merged commit b81ee63 into main May 18, 2026
7 checks passed
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