Skip to content

Release Process

Mapo uses multi-semantic-release to automate versioning, changelog generation, GitHub tagging, and npm publishing across all packages in the monorepo.

The entire process is triggered automatically by CI on every push to main. No manual version bumping or publishing is ever required.


How It Works

1. Conventional Commits

Every commit must follow the Conventional Commits specification, enforced by commitlint + husky on every git commit.

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

feat(core): add useMapoAuth composable
fix(form): correct accessor path for nested fields
docs(uikit): add MapoSidebar slot reference
chore(deps): update nuxt to 4.5.0

The type determines the version bump:

Commit typeVersion bump
fix:patch (0.0.x)
feat:minor (0.x.0)
feat!: or BREAKING CHANGE: footermajor (x.0.0)
chore:, docs:, test:, ci:no release

2. Per-Package Analysis

multi-semantic-release analyzes commits for each package independently. Only packages with relevant commits since their last release are versioned and published.

For example, a feat(form): commit triggers a release only for @mapomodule/form — not for @mapomodule/core, @mapomodule/store, or any other unrelated package.

3. Cascade Bumping

mapomodule (the meta-package) depends on all @mapomodule/* packages via workspace:*. When any dependency receives a release, mapomodule is automatically bumped using the inherit strategy — it inherits the highest bump level among its updated dependencies.

feat(core): add new composable
  → @mapomodule/core: minor bump (0.1.0 → 0.2.0)
  → mapomodule: minor bump (0.1.0 → 0.2.0)  ← cascaded

4. CI Pipeline

The full pipeline runs on every push to main:

push to main

  ├── lint       ESLint + TypeScript check (turbo)
  ├── test       Vitest + Cypress (turbo) — runs after lint
  └── release    Runs after lint + test pass

        └── pnpm release

              ├── pnpm build           (turbo build all packages)
              └── multi-semantic-release

                    ├── Analyze commits per package
                    ├── Determine version bumps
                    ├── Update CHANGELOG.md per package
                    ├── Bump version in package.json
                    ├── Create Git tag  (e.g. @mapomodule/core@0.2.0)
                    ├── Create GitHub Release with notes
                    └── Publish to npm  (@semantic-release/npm)

The release job only runs on push to main, never on pull requests.

TODO: extend the validation stage in the future with a test matrix across multiple supported Node.js versions, Nuxt versions, and other compatibility dimensions so releases are validated against more than one environment.


Scripts

ScriptDescription
pnpm releaseFull release: build all packages then run multi-semantic-release. Used by CI.
pnpm release:dryDry-run: simulates the full release without writing tags, changelogs, or publishing to npm. Use this locally to preview what would be released.
pnpm publish:packagesEmergency manual publish: publishes all packages under packages/ directly via pnpm without going through semantic-release. Use only if CI is broken.

Running a Dry-Run Locally

Before pushing to main, you can preview the release output without publishing anything:

bash
# Requires GITHUB_TOKEN to be set (read-only token is enough for dry-run)
GITHUB_TOKEN=<your-token> NPM_TOKEN=<your-token> pnpm release:dry

The dry-run output shows:

  • Which packages would be released
  • What version bump would be applied to each
  • The generated changelog entries
  • The npm publish command that would run (without executing it)

Required Secrets

Two secrets must be configured in the GitHub repository (Settings → Secrets and variables → Actions):

SecretDescription
GITHUB_TOKENAutomatically provided by GitHub Actions. No setup needed.
NPM_TOKENAn npm automation token with publish rights. Generate at npmjs.comAccess Tokens → Generate New Token → Automation.

Scoped Packages and npm Access

All @mapomodule/* packages and mapomodule are published with --access public since they live under a scoped namespace. This is configured in .releaserc.json via @semantic-release/npm.

Private packages (@mapomodule/docs, @mapomodule/example) have "private": true in their package.json and are automatically skipped by @semantic-release/npm — they are never published to npm.


Changelog

Each package gets its own CHANGELOG.md at the package root, generated and updated automatically by @semantic-release/changelog on every release. The changelog follows the Keep a Changelog format grouped by release version.


Git Tags

multi-semantic-release creates one tag per released package using the format:

@mapomodule/core@0.2.0
@mapomodule/form@1.0.0
mapomodule@0.2.0

Tags are pushed to origin by @semantic-release/git as part of the release step.

Released under the MIT License.