Skip to content

Release Process

@franksauvag/rpg-commons uses semantic-release to automate versioning, tagging, changelog generation, and publishing to GitHub Packages.


How it works

Every push to main triggers the Release workflow. If the CI gate passes (lint, typecheck, test, build), semantic-release analyzes commits since the last release and decides whether a new version is warranted.

push to main


┌─────────────────────────────────┐
│ CI gate                         │
│  lint → typecheck → test → build│
└────────────────┬────────────────┘
                 │ passes

┌─────────────────────────────────┐
│ semantic-release                │
│  analyze commits                │
│  → bump version (X.Y.Z)         │
│  → update CHANGELOG.md          │
│  → commit + tag vX.Y.Z          │
│  → GitHub Release               │
│  → pnpm publish → npm.pkg.github│
└─────────────────────────────────┘

If no commit since the last release is "releasable" (e.g. only chore: or docs:), semantic-release does nothing — no new version is published.


Conventional Commits

All commits must follow the Conventional Commits specification. This is what drives automatic versioning.

Format

<type>(<scope>): <description>

[optional body]

[optional footer(s)]

Version bump rules

Commit typeExampleVersion bump
fix:fix(media): handle null apiBaseUrlPatch 0.0.X
perf:perf(srd): cache bundle responsesPatch 0.0.X
feat:feat(srd): add buildSpellsBundleMinor 0.X.0
feat!: or BREAKING CHANGE:feat!: rename SrdClient.fetchMajor X.0.0
chore:, docs:, refactor:, style:, test:, build:, ci:No release

Breaking change syntax

feat: rename createSrdClient config key

BREAKING CHANGE: `baseUrl` is now `apiBaseUrl` — update all call sites.

Or using the shorthand !:

feat!: rename createSrdClient config key

Examples

# Patch release (0.0.X)
fix(srd): prevent infinite retry on 401 responses
perf(components): memoize SelectionCard render

# Minor release (0.X.0)
feat(ui): add TooltipButton component
feat(srd): expose fetchDataset in SrdClient public API

# Major release (X.0.0)
feat!: drop support for React 18 — require React 19+

feat(srd): remove resolveEntryMediaSrc

BREAKING CHANGE: resolveEntryMediaSrc has been removed.
Use resolveSrdMediaUrl directly.

# No release (chore/docs/refactor)
chore: update pnpm to 10.12.1
docs(srd): add fetchDataset example to srd-data-api.md
refactor(normalizers): extract costToGp helper
test(srd): add missing edge cases for toSpellsBundle

Two publication channels

ChannelTrigger (on main)ArtifactAudience
npm packagerelease job after ci@franksauvag/rpg-commons@X.Y.Z on GitHub PackagesConsumer apps
Documentation sitedeploy-docs after ci + docsHTML at libs.heritiersdudonjon.comHumans + agents (browser)

A docs: commit can update the site without a new npm version. A feat: triggers a minor npm release when merged to main; the doc site redeploys on every green main build regardless.


CI jobs

JobRuns onRole
ciPR + mainlint, typecheck, test, pnpm build (library)
releasemain onlysemantic-release → npm, tag, CHANGELOG
docsPR + mainpnpm docs:build (VitePress + TypeDoc + Storybook)
deploy-docsmain onlyFTP deploy to OVH /www-libs/
push to main
     ├─► ci ──┬─► release ──► npm publish
     │        └─► docs ──► deploy-docs ──► libs.heritiersdudonjon.com

pull request
     ├─► ci
     └─► docs (no deploy)

Two-phase rule (library → consumers)

  1. Merge to rpg-commons main and wait for npm publish + git tag.
  2. Only then open/update consumer PRs with the new semver.

Do not merge consumer changes that depend on unpublished exports. Avoid long-lived file:../, pnpm link, or npm pack for shared CI.


Pipeline files

FileRole
.github/workflows/ci.ymlCI, release, docs, deploy-docs
.releaserc.jsonsemantic-release plugins
CHANGELOG.mdAuto-generated — do not edit manually
typedoc.jsonAPI reference generation
scripts/build-docs.mjsFull doc site build

Release bot commits use chore(release): X.Y.Z [skip ci] — the next normal push re-runs all jobs.

See also Documentation pipeline.


First release — bootstrapping

The current package.json version (0.0.1) is the starting point. semantic-release will set the initial published version based on commits. To ensure it publishes v0.1.0 on the first run, push at least one feat: commit.

If the repo has no previous tags, semantic-release treats all commits as new and will publish v1.0.0 for a feat: or v0.1.0 if configured with "firstRelease". The default behavior uses v1.0.0 as the first release for feat:. To start at v0.1.0, add a lightweight tag v0.0.0 before the first pipeline run:

bash
git tag v0.0.0
git push origin v0.0.0

This anchors semantic-release's history so the next feat: commit produces v0.1.0.


Consuming the package

Local development

Prefer a user-level ~/.npmrc with your PAT (do not commit tokens to the consumer repo):

ini
@franksauvag:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_PAT

Then install as usual (pnpm add, npm install, etc.).

Optionally commit a project .npmrc that contains only the @franksauvag:registry=… line so the scope is explicit for everyone; tokens stay in ~/.npmrc or CI.

CI (GitHub Actions)

For a consumer repo's CI pipeline, add a PAT with read:packages scope as a repository secret (e.g. PACKAGES_READ_TOKEN), then configure setup-node and install:

yaml
- uses: actions/setup-node@v5
  with:
    node-version: 22
    registry-url: https://npm.pkg.github.com
    scope: "@franksauvag"
  env:
    NODE_AUTH_TOKEN: ${{ secrets.PACKAGES_READ_TOKEN }}

- name: Install
  run: pnpm install --frozen-lockfile

Note: The built-in GITHUB_TOKEN of a consumer repo can only access packages published by that same repo. A PAT is required to read packages from rpg-commons.


Emergency manual release

If semantic-release is blocked or you need to release from a local machine:

bash
# Ensure clean working tree on main
git checkout main && git pull

# Dry-run to preview what would be released
GITHUB_TOKEN=$(gh auth token) NODE_AUTH_TOKEN=$(gh auth token) pnpm exec semantic-release --dry-run

# Actual release
GITHUB_TOKEN=$(gh auth token) NODE_AUTH_TOKEN=$(gh auth token) pnpm exec semantic-release