Skip to main content
Status: partly live, partly target. @changesets/cli is configured (.changeset/config.json) and pnpm changeset, pnpm changeset:status and pnpm changeset:version exist. .github/workflows/release.yml opens the version pull request and, on its merge, runs changeset publish to npm.figentra.com. Not built: the Infisical release identity, a packed-artifact smoke test and signed artifacts. Today every package is still "private": true, so nothing publishes yet. Policy: 0025 (supersedes 0013 decision 3), 0024 (MIT), 0119 (the owner merges the version pull request).

The rules

  • Every change to a member ships with a changeset. pnpm changeset records it; pnpm changeset:status fails when a changed member has none (target: chained into pnpm verify).
  • CHANGELOG.md files are generated by changesets and never hand-edited.
  • Versions are bumped by the release job on main, not on a feature branch. guard-bash denies changeset version locally for that reason.
  • Merging the version pull request is the release decision. Claude prepares it; the owner merges it (0119 D7).
  • Published or tagged versions are permanent. A version is never republished or silently replaced; roll forward with a new patch.
  • Only packages are versioned. Apps and services deploy by commit SHA and get no version or tag (privatePackages: { version: false, tag: false }, 0025 decision 4).
  • A package publishes when it flips to "private": false. It then builds to dist/*.mjs + .d.mts and publishes to npm.figentra.com (@stackra scope).

A normal release

1

Land changesets with the work

In each pull request that changes a member: pnpm changeset, pick the members, pick the bump (patch, minor, major) and write one user-facing line. Commit the generated file under .changeset/.
2

Let CI open the version pull request

After a push to main the release job runs pnpm changeset:version (which also refreshes the lockfile) and opens or updates one pull request titled chore(release): version packages.
3

Review it

Read the CHANGELOG.md diffs and the version bumps. Check the bump matches the change. The release job pushes and opens it as stackra-bot (a GitHub App token from actions/create-github-app-token, repo secrets STACKRA_BOT_APP_ID and STACKRA_BOT_PRIVATE_KEY), so the Quality Gate runs on it without a close and reopen.
4

Merge it

The merge to main is the release. The job builds, publishes every non-private package to npm.figentra.com, tags it and creates the release notes.
5

Verify

git ls-remote --tags origin shows the new tags on the merge commit, and each member’s CHANGELOG.md contains the entry.

Before the first release (once, by a human)

  • Branch protection on main requires the Quality Gate check (CI).
  • A tag ruleset on the version tag pattern blocks deletion and non-fast-forward updates for everyone. Version tags are immutable.
  • The host setting that lets the workflow token create pull requests is on.
  • The release job runs in a protected environment restricted to main, so a branch that edits the workflow cannot obtain a credential.

Publishing to npm.figentra.com

0025 records the decision. Before the first package flips to "private": false, a human:
  • creates an Infisical machine identity for the release job (OIDC, trusting only this repository’s release environment on main) with read access to /ci in prod;
  • stores the registry publish token in /ci as NPM_FIGENTRA_TOKEN;
  • sets the repository variables INFISICAL_RELEASE_IDENTITY_ID and INFISICAL_PROJECT_ID (names and ids, never a token). Until they are set the release job skips the token step.
The procedure the job follows, some of it still target:
  • The publish token comes only from Infisical /ci over OIDC (0009); no token sits in .npmrc, a repository secret or a file.
  • Build the tarball and an SBOM with no privileges, then sign both keylessly in the one job that holds id-token: write.
  • Publish is idempotent: the same bytes already on the registry are not published again, so re-running failed jobs is safe; different bytes fail the job.
  • Read access for consumers is a read-only token held in Infisical /ci and fetched through OIDC (Secrets).
  • A prerelease publishes under a next dist-tag and moves no floating tag.

When something fails

Re-run only the failed jobs of the same run unless a row says otherwise.

Revert

  • Before merging: close the version pull request. The next push recreates it from the remaining changesets.
  • After merging: roll forward. For a bad release that was published, deprecate it (npm deprecate <pkg>@<bad> "<reason; use <good>>"), move the latest dist-tag back to the last good version with npm dist-tag add if needed, and ship a fixed patch. Prefer deprecate over unpublish.
  • A bad release record on the host: mark it a prerelease or delete the release page; leave the tag.
  • The workflow itself: revert the workflow file on main; nothing else depends on it.

Checks that back this page

See also Mobile release, Deploys and Commits and releases.