Running your own release pipeline across 145 packages

← All posts

Publishing one npm package is trivial. Publishing 145 packages that depend on each other, with correct version bumps, verified artifacts, generated changelogs, and a rollback plan — that's a pipeline.

We built one inside KB Labs itself. The release plugin orchestrates the full flow: plan → snapshot → checks → build → verify → version → changelog → publish → git → report. Here's what we learned.

The pipeline stages

1. Plan

The planner scans git history since the last release tag to determine which packages changed and what bump they need. It uses scoped tags (@kb-labs/core-types@1.2.3) for independent versioning and v* tags for lockstep releases. Three strategies:

2. Snapshot

Before touching any file, the pipeline saves every package.json version to .kb/release/backup.json. If anything fails downstream, restoreSnapshot() recovers the original state. History is preserved with timestamps in .kb/release/history/ for audit.

3. Checks

Configurable pre-publish checks: audit, lint, type-check, test. Runs up to 8 checks in parallel. Each check can be optional (warning only) or strict (blocks publish). Failed strict checks abort the pipeline before any version bump happens.

4. Build (the safe build)

Here's a subtle problem: tsup with clean: true wipes the dist/ directory before building. If you're running a REST API locally from that same dist/, the running service crashes mid-build.

Fix: build into a temp directory, then atomic-rename swap. The running service never sees an incomplete dist/.

5. Pack verification

This is the stage that catches what CI doesn't. For each package, the verifier runs npm pack, extracts the tarball, and checks:

This catches a class of bugs that only manifest after publish: the package works in the monorepo because of hoisted dependencies, but fails when installed standalone.

6–9. Version → Changelog → Publish → Git

Version bumps are written to package.json. Changelogs are generated per-package from git commit ranges — with templates (compact, corporate, technical) and optional LLM enhancement for human-readable summaries. Publishing uses OTP for CLI or token for CI. Git gets a commit and tags.

The adapter pattern inside the pipeline

The pipeline core doesn't know about CLI prompts or REST APIs. Two key interfaces make it portable:

The CLI and REST API are thin adapters over the same runReleasePipeline(options) core.

What we learned publishing 145 packages