diff --git a/.claude/hooks/prompt-context.sh b/.claude/hooks/prompt-context.sh index df55f07..8f4b784 100755 --- a/.claude/hooks/prompt-context.sh +++ b/.claude/hooks/prompt-context.sh @@ -40,6 +40,9 @@ fi if echo "$prompt" | grep -qE 'commit|message|changelog|conventional'; then inject+=('Conventional Commits (non-negotiable): (): (≤72 chars). Types: feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert. Use `!` for breaking changes. Body explains WHY if non-obvious. Examples: feat(auth): hash password before persisting; refactor(docs)!: consolidate scaffolding into guides. See CLAUDE.md Key Conventions.') fi +if echo "$prompt" | grep -qE 'release|version|bump|semver|tag\b'; then + inject+=('Releases: ADR-021 + docs/guides/releasing.md. Hybrid versioning — root template (template-v...) + 5 feature packages (auth-v..., blog-v..., etc.) version independently from 0.1.0. release-please reads Conventional Commits and opens a rolling release PR on every push to main; merging cuts per-package tags. Bump targeting is by commit-path, not (scope). Pre-1.0 policy: feat: -> patch, feat!: -> minor.') +fi if [ ${#inject[@]} -gt 0 ]; then echo "=== context-relevant pointers (from .claude/hooks/prompt-context.sh) ===" diff --git a/.claude/hooks/session-start.sh b/.claude/hooks/session-start.sh index 8687249..39c773b 100755 --- a/.claude/hooks/session-start.sh +++ b/.claude/hooks/session-start.sh @@ -10,6 +10,7 @@ Workflow: pnpm work status | pnpm work next | pnpm work dispatch (ADR-019) Generator-first: pnpm turbo gen beats hand-rolled scaffolding (non-negotiable) Conformance: pnpm conformance + pnpm fallow (5-gate drift detection) Conventional Commits (non-negotiable): (): — see CLAUDE.md Key Conventions +Releases: release-please reads commits + opens rolling release PR on merge to main (ADR-021) Skills: to-prd, grill-with-docs, grill-me, handoff (.claude/skills/) EOF diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..0525dd9 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,46 @@ +# Release Please — automated changelog + version bumps on merge to main. +# +# How it works: +# 1. On every push to main, release-please scans conventional commits since +# the last release tag for each tracked package. +# 2. It opens (or updates) a single rolling "release PR" containing: +# - version bumps in each affected package.json +# - new CHANGELOG.md entries grouped by section (Features / Bug Fixes +# / Performance / Refactoring / Documentation / Reverts) +# - updated .release-please-manifest.json +# 3. Merging that PR triggers tag creation (`template-vN.N.N`, `auth-vN.N.N`, +# etc.) and GitHub release notes. +# +# Hybrid versioning (ADR-021): root template versions independently from the +# 5 feature packages. Tags use the per-package component prefix so they don't +# collide (e.g. `template-v0.2.0` vs `auth-v0.1.1`). +# +# Tracked packages, manifest baseline, and changelog sections live in +# `release-please-config.json` + `.release-please-manifest.json`. + +name: Release Please + +on: + push: + branches: [main] + +permissions: + contents: write + pull-requests: write + +# A second push to main while a release PR is open shouldn't fight with the +# first invocation — release-please-action already updates the rolling PR +# idempotently, but concurrency keeps the audit trail clean. +concurrency: + group: release-please + cancel-in-progress: false + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - uses: googleapis/release-please-action@v4 + with: + config-file: release-please-config.json + manifest-file: .release-please-manifest.json + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..0fcdd6e --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,8 @@ +{ + ".": "0.1.0", + "packages/auth": "0.1.0", + "packages/blog": "0.1.0", + "packages/media": "0.1.0", + "packages/marketing-pages": "0.1.0", + "packages/navigation": "0.1.0" +} diff --git a/AGENTS.md b/AGENTS.md index b59e685..d3a16ae 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,6 +6,8 @@ This is a **Turborepo + pnpm monorepo** organized by vertical features. Each fea > **Commits:** Every commit message follows [Conventional Commits](https://www.conventionalcommits.org/): `(): ` (≤72 chars). Types: `feat | fix | docs | style | refactor | test | chore | perf | ci | build | revert`. Use `!` for breaking changes. The sandcastle implementer + reviewer prompts enforce this; agents authoring autonomously MUST honor it. +> **Releases:** Versioning is hybrid (ADR-021) — root template + 5 feature packages version independently from `0.1.0`. release-please reads Conventional Commits and opens a rolling release PR on every merge to main; merging it cuts tagged releases. See [`docs/guides/releasing.md`](./docs/guides/releasing.md). + ## Agent-driven development This template assumes agents (Claude, Codex, etc.) will author most feature work. The orchestration substrate is [Sandcastle](https://github.com/mattpocock/sandcastle) — see [ADR-019](./docs/decisions/adr-019-sandcastle-for-agent-orchestration.md). Day-to-day entry points: diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..3a2e46c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog — template-vertical + +All notable changes to this template at the root level. Per-feature changelogs live at `packages//CHANGELOG.md`. + +This file is maintained by [release-please](https://github.com/googleapis/release-please) — do not edit manually. Edits land via the rolling release PR triggered by merges to `main`. See [ADR-021](./docs/decisions/adr-021-versioning-and-changelog.md) for the architecture and [`docs/guides/releasing.md`](./docs/guides/releasing.md) for the day-to-day reference. + +## 0.1.0 (2026-05-13) + +### Initial baseline + +- Hybrid versioning established (ADR-021): root template + 5 feature packages each version independently. +- Conventional Commits required for every commit (CLAUDE.md Key Conventions). +- Coverage architecture shipped (ADR-020): L0 vitest thresholds, L1 `pnpm coverage:diff`, L2 `pnpm coverage:aggregate`, L3 `pnpm mutate`. +- Manifest-driven coverage bands in every `feature.manifest.ts`. +- Sandcastle agent orchestration (ADR-019) + PRD-lifecycle automation (`pnpm work prd-ship`). +- 5 features (auth / blog / media / marketing-pages / navigation) all green on declared L0 bands. + +Future entries appear above this section as release-please assembles them from conventional commits since the last release. diff --git a/CLAUDE.md b/CLAUDE.md index 8370ebe..336639a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,6 +57,7 @@ Turborepo + pnpm monorepo organized by vertical features. Each feature (`auth`, - `docs/guides/realtime.md` — Socket.IO channels, broadcasts, handlers (_requires `gen core-package realtime`_) - `docs/guides/audit-and-compliance.md` — DPA-compliant audit logging cookbook (_requires `gen core-package audit`_) - `docs/guides/coverage.md` — 4-layer coverage cookbook (L0 vitest thresholds, L1 `pnpm coverage:diff`, L2 aggregate, L3 mutation; ADR-020) +- `docs/guides/releasing.md` — release-please workflow: how Conventional Commits become tagged versions + per-package CHANGELOGs (ADR-021) - `docs/architecture/template-tiers.md` — must-have vs optional packages and how to scaffold the optionals ## Conformance system @@ -90,7 +91,8 @@ See `docs/guides/coverage.md` for the cookbook and ADR-020 for the full rational ## Key Conventions -- **Conventional Commits (non-negotiable)** — Every commit message MUST follow the [Conventional Commits](https://www.conventionalcommits.org/) spec: `(): ` (≤72 chars). Types: `feat | fix | docs | style | refactor | test | chore | perf | ci | build | revert`. Use `!` after type/scope for breaking changes. Body explains WHY if non-obvious. Examples: `feat(auth): hash password before persisting`, `test(blog): assert article not found error`, `refactor(docs)!: consolidate scaffolding into guides`. The sandcastle implementer + reviewer prompts both enforce this; agents authoring commits autonomously MUST honor it. +- **Conventional Commits (non-negotiable)** — Every commit message MUST follow the [Conventional Commits](https://www.conventionalcommits.org/) spec: `(): ` (≤72 chars). Types: `feat | fix | docs | style | refactor | test | chore | perf | ci | build | revert`. Use `!` after type/scope for breaking changes. Body explains WHY if non-obvious. Examples: `feat(auth): hash password before persisting`, `test(blog): assert article not found error`, `refactor(docs)!: consolidate scaffolding into guides`. The sandcastle implementer + reviewer prompts both enforce this; agents authoring commits autonomously MUST honor it. Commits become versions + changelog entries automatically via release-please (ADR-021 / `docs/guides/releasing.md`). +- **Versioning is hybrid (ADR-021)** — Root template (`template-vertical`) + 5 feature packages (`@repo/{auth,blog,media,marketing-pages,navigation}`) each version independently from `0.1.0`. release-please reads Conventional Commits since the last tag and opens a rolling release PR on every merge to main; merging it cuts per-package tags (`template-v0.2.0`, `auth-v0.1.1`, etc.) + GitHub releases. **Bump targeting is by commit path** — files under `packages//**` bump that feature; cross-cutting paths (`docs/`, `scripts/`, `.github/`, root configs) bump the root. Pre-1.0 policy: `feat:` → patch, `feat!:` → minor. - **Relative imports in `src/`** — Source files use relative paths (`../repositories/...`), not `@/` alias - **`@/` alias in tests** — Test files (`*.test.ts`) use `@/` to import from `src/` - **`vitest.config.ts`** — Every package must define `resolve.alias: { "@": path.resolve(__dirname, "./src") }` diff --git a/docs/README.md b/docs/README.md index e4496d4..433feff 100644 --- a/docs/README.md +++ b/docs/README.md @@ -33,6 +33,7 @@ docs/ │ ├── runbook.md # ← first time? read this end-to-end │ ├── conformance-quickref.md │ ├── coverage.md # 4-layer coverage cookbook (ADR-020) +│ ├── releasing.md # release-please workflow + CHANGELOG cookbook (ADR-021) │ ├── tdd-workflow.md │ ├── testing-strategy.md │ ├── adding-a-feature.md # manual path diff --git a/docs/decisions/adr-021-versioning-and-changelog.md b/docs/decisions/adr-021-versioning-and-changelog.md new file mode 100644 index 0000000..8448a7e --- /dev/null +++ b/docs/decisions/adr-021-versioning-and-changelog.md @@ -0,0 +1,111 @@ +# ADR-021 — Hybrid versioning + automated changelog via release-please + +**Status:** Accepted +**Date:** 2026-05-13 +**Builds on:** Conventional Commits convention (CLAUDE.md Key Conventions), ADR-019 (sandcastle agent orchestration) + +## Context + +Until this ADR the template had no versioning + no changelog. Every package shipped at `0.0.0`; there was no tag history, no "what changed since I forked this" answer, and no formal cadence for surfacing meaningful state changes. + +Two pressures motivated wiring this up now: + +1. **Conventional Commits had just been mandated** (visible across CLAUDE.md, AGENTS.md, session-start, and prompt-context hooks). Conventional commits are the substrate that automated versioning tools consume — leaving them unused would waste a free signal. +2. **The template is a fork target.** Downstream consumers need a version they can pin against ("I forked at v0.3.0, what's changed?") and a changelog they can diff. + +The available tools fall in three families: + +- **Changesets** (`@changesets/cli`) — contributors run `pnpm changeset` per PR; explicit semver intent. Higher friction; not needed when conventional commits are already mandated. +- **semantic-release** — fully automated from conventional commits; publishes on merge. Less control; monorepo support requires extra plumbing. +- **release-please** (Google) — parses conventional commits, opens a rolling release PR with bumps + CHANGELOG entries; merging the PR cuts tags + GitHub releases. + +`release-please` is the natural fit: conventional commits are already enforced, and the "release PR" model gives a human approval gate without requiring per-commit changeset files. + +The remaining design choice is **versioning scope**: + +- **Single root version** — one CHANGELOG.md at the root; the whole template moves together. Simplest. +- **Per-package versions** — every `@repo/*` package versions independently; one CHANGELOG.md per package. Useful for published packages; we publish nothing today. +- **Hybrid** — root template version (for cross-cutting changes: docs, scripts, ci, generators, core packages) + per-feature versions (for `packages//**` changes). One root CHANGELOG.md + one per feature. Decision boundary follows commit-path scoping. + +## Decision + +**1. Adopt `release-please` (Google) as the versioning + changelog substrate.** Configuration in `release-please-config.json` and `.release-please-manifest.json` at the repo root. The GitHub Action lives at `.github/workflows/release-please.yml` and runs on every push to `main`. + +**2. Hybrid versioning scope.** Six tracked packages: + +| Path | Package name | Component (tag prefix) | Initial version | +| -------------------------- | ----------------------- | ---------------------- | --------------- | +| `.` | `template-vertical` | `template` | `0.1.0` | +| `packages/auth` | `@repo/auth` | `auth` | `0.1.0` | +| `packages/blog` | `@repo/blog` | `blog` | `0.1.0` | +| `packages/media` | `@repo/media` | `media` | `0.1.0` | +| `packages/marketing-pages` | `@repo/marketing-pages` | `marketing-pages` | `0.1.0` | +| `packages/navigation` | `@repo/navigation` | `navigation` | `0.1.0` | + +Each gets its own `CHANGELOG.md`. Tags use the per-package component prefix to avoid collisions: `template-v0.2.0`, `auth-v0.1.1`, etc. + +Core packages (`core-shared`, `core-cms`, `core-api`, `core-eslint`, `core-typescript`, `core-testing`) and optional cores (`core-events`, `core-realtime`, etc., when scaffolded) are **NOT** independently versioned. Cross-cutting changes to those land in the root template version. Rationale: those packages cascade — bumping `core-shared` would functionally invalidate every feature anyway; surfacing them as separate versions creates noise without information. If a future consumer publishes individual core packages downstream, they can add per-package tracking then. + +Apps (`web-next`, `web-tanstack`, `cms`, `storybook`) stay at `0.0.0`. They're not consumable artifacts. + +**3. Pre-1.0 bump policy.** While each tracked package is `<1.0.0`: + +- `feat:` commits bump **patch** (not minor), per `bump-patch-for-minor-pre-major: true` +- `fix:` commits bump patch +- `feat!:` (or any `BREAKING CHANGE:` footer) bumps **minor** (not major) +- `chore`, `ci`, `build`, `style`, `test` commits don't bump + +Rationale: the conservative pre-1.0 default means surface area can change without exhausting the version space. When a package crosses `1.0.0`, standard semver kicks in. + +**4. Conventional-commit type → changelog section mapping.** From `release-please-config.json`: + +| Type | Section | Hidden | +| --------------------------------------- | ------------- | ------ | +| `feat` | Features | no | +| `fix` | Bug Fixes | no | +| `perf` | Performance | no | +| `refactor` | Refactoring | no | +| `docs` | Documentation | no | +| `revert` | Reverts | no | +| `test`, `chore`, `ci`, `build`, `style` | (omitted) | yes | + +Hidden sections still drive version bumps where applicable (none do, by current policy) but don't clutter the changelog. + +**5. Bump targeting is by commit-path, not commit-scope.** release-please decides which package(s) to bump based on the _files changed_ in a commit, NOT the conventional-commit `scope`. A commit changing `packages/auth/**` bumps `@repo/auth`; a commit changing `docs/**` or `scripts/**` or `CLAUDE.md` bumps the root template; a commit changing both bumps both. The conventional-commit `scope` field is for human readability in the changelog — it does not drive routing. + +**6. Release PR is a rolling document.** Every push to main re-evaluates the open release PR. Merging it cuts tags + creates GitHub releases for each affected package. No manual edits to the PR — the changelog content is reproducible from the commit history. + +**7. CHANGELOG files are committed and edited only by release-please.** Manual edits are discouraged because they will be overwritten the next time release-please assembles the rolling PR. Initial baseline content for each `0.1.0` entry is the only exception. + +## Alternatives considered + +- **Changesets (`@changesets/cli`)** — rejected primarily because Conventional Commits already capture the necessary intent. Per-PR changeset files would duplicate information. Could revisit if release-please ever fails to handle a release-shape edge case (e.g. needing a manual bump that's larger than commits imply). +- **semantic-release** — rejected for monorepo friction. Per-package support requires `semantic-release-monorepo` or `multi-semantic-release`; release-please handles this natively. +- **Single root version** — rejected because cross-cutting commits ARE a different kind of change from feature commits. A `fix(media): off-by-one in upload` shouldn't churn the root template version; a `refactor(coverage): unify L0 thresholds` shouldn't churn `@repo/auth`'s version. Separating gives consumers a finer-grained "what changed for me" signal. +- **Per-package for every workspace member (apps, core, tooling)** — rejected. Core packages cascade; bumping `core-shared` is effectively a template-wide change. Apps aren't consumable. Tooling churn is mostly mechanical. Adding versions for these adds bookkeeping without information value. +- **Tag prefix `template-vertical-v...` vs `template-v...`** — chose `template-v` for tag economy (shorter; consistent length with `auth-v`, `blog-v`, etc.). The package name `template-vertical` is still authoritative in `package.json`. +- **Auto-merge the release PR** — rejected. Human approval is the gate that catches the rare case where a commit's content doesn't match its conventional type (e.g. a `chore:` commit that actually shipped a feature). + +## Consequences + +**Positive:** + +- Every merge to main produces a tracked, dated record of what changed for downstream consumers. +- Conventional commits become load-bearing: their type + body shape the changelog directly. +- The "what version did I fork at" question has a real answer per tracked package. +- Tagged releases enable `git diff vX.Y.Z..HEAD -- ` for narrow "what changed in feature X since I last looked" questions. +- Release cadence is implicit (merge the PR when ready); no separate release planning required. + +**Negative:** + +- Six packages × independent versions means six CHANGELOG.md files to navigate. Mitigated by the `## Cross-references` section in each pointing at the relevant ADRs + this one. +- release-please-action is GitHub-Actions-coupled. A migration to a different CI provider would need a different runner (the release-please core CLI runs anywhere, but the orchestration around the rolling PR is Action-specific). +- The first release PR after the initial baseline could be large (covers everything merged after `0.1.0`). This is one-time; subsequent PRs are scoped to the work between releases. +- Apps stuck at `0.0.0` is mildly confusing if a contributor expects every package to be versioned. Mitigated by documentation here + in `docs/guides/releasing.md`. + +## Related + +- ADR-019 — sandcastle agent orchestration (the dispatch loop that ships these commits) +- ADR-020 — coverage architecture (one of the systems shipped at the 0.1.0 baseline) +- CLAUDE.md Key Conventions — Conventional Commits requirement (the substrate this ADR consumes) +- `docs/guides/releasing.md` — day-to-day cookbook diff --git a/docs/glossary.md b/docs/glossary.md index 4cc9bc8..ab22b97 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -145,6 +145,35 @@ The percentage of mutants killed (i.e., caught by tests) out of all mutants gene **`coverage/summary.json`**: The aggregated per-package + repo-level coverage snapshot, committed on merge to main. Grep-able from git history via `git log -- coverage/summary.json`. Includes timestamp + commit SHA for correlation with deploys. +## Releasing + +**Conventional Commits**: +The commit-message spec mandated by this template — `(): `. Types: `feat | fix | docs | style | refactor | test | chore | perf | ci | build | revert`. `!` for breaking changes. See CLAUDE.md Key Conventions. + +**release-please**: +The Google-maintained automation that reads Conventional Commits, derives semver bumps, and opens a rolling release PR with version bumps + per-package CHANGELOG entries on every push to main. Configured in `release-please-config.json` + `.release-please-manifest.json`. ADR-021. + +**Hybrid versioning**: +The template's versioning strategy (ADR-021) — root template (`template-vertical`) + 5 feature packages (`@repo/{auth,blog,media,marketing-pages,navigation}`) version independently from `0.1.0`. Core packages, tooling, and apps are NOT versioned (cascade-effect would invalidate the signal). + +**Tag prefix**: +The per-package prefix release-please uses to avoid tag collisions in a monorepo — `template-v0.1.0` for the root, `auth-v0.1.0` / `blog-v0.1.0` / etc. for features. + +**Rolling release PR**: +The single PR release-please keeps open on `main` and updates idempotently on every push. Contains all pending version bumps + changelog entries since the last release. Merging it cuts the tags + GitHub releases. + +**Bump targeting (by commit-path)**: +How release-please decides which tracked package(s) a commit bumps — by the **files changed**, not the conventional-commit `(scope)`. A commit touching `packages/auth/**` bumps `@repo/auth`; a commit touching `docs/**` or `scripts/**` bumps the root template; a commit touching both bumps both. + +**Pre-1.0 bump policy**: +While each tracked package is `<1.0.0`: `feat:` bumps patch, `feat!:` bumps minor (NOT major). Configured via `bump-patch-for-minor-pre-major: true`. Once a package crosses 1.0.0, standard semver kicks in. + +**`Release-As:` trailer**: +A commit-body trailer (`Release-As: 0.5.0`) that overrides release-please's auto-derived bump for the package(s) whose paths the commit touches. Used for manual semver corrections or explicitly crossing 1.0.0. + +**CHANGELOG.md**: +A per-package, release-please-managed changelog (one at repo root for the template; one at `packages//CHANGELOG.md` for each feature). Do not edit manually — release-please regenerates from commit history. + ## Cross-feature mechanisms **Event bus** (`IEventBus`): diff --git a/docs/guides/releasing.md b/docs/guides/releasing.md new file mode 100644 index 0000000..0fce746 --- /dev/null +++ b/docs/guides/releasing.md @@ -0,0 +1,150 @@ +# Releasing + +> **Architecture:** [ADR-021](../decisions/adr-021-versioning-and-changelog.md). **Convention:** [Conventional Commits](https://www.conventionalcommits.org/) — see `CLAUDE.md` Key Conventions. + +This template uses [release-please](https://github.com/googleapis/release-please) to derive versions + changelog entries from Conventional Commits. On every push to `main`, release-please updates a rolling release PR. Merging that PR cuts tags + GitHub releases. + +## The six tracked packages + +| Path | Package | Tag prefix | Initial version | +| -------------------------- | ----------------------------------- | ---------------------- | --------------- | +| `.` | `template-vertical` (root template) | `template-v...` | `0.1.0` | +| `packages/auth` | `@repo/auth` | `auth-v...` | `0.1.0` | +| `packages/blog` | `@repo/blog` | `blog-v...` | `0.1.0` | +| `packages/media` | `@repo/media` | `media-v...` | `0.1.0` | +| `packages/marketing-pages` | `@repo/marketing-pages` | `marketing-pages-v...` | `0.1.0` | +| `packages/navigation` | `@repo/navigation` | `navigation-v...` | `0.1.0` | + +Core packages, tooling packages, and apps are intentionally NOT versioned (ADR-021). + +## How a commit lands in a release + +1. Author the commit per Conventional Commits: `(): `. +2. Push / merge to main. +3. release-please's GH Action runs: scans commits since the last tag, groups by tracked package (using **commit-path**, not the conventional-commit scope), and updates the rolling release PR. +4. When you (or a reviewer) merge the release PR, tags are cut + GitHub releases are created. + +### Bump targeting + +release-please looks at the **files changed** in each commit, not the commit's `(scope)`: + +- A commit touching `packages/auth/**` → bumps `@repo/auth`. +- A commit touching `docs/**`, `scripts/**`, `.github/**`, root config files → bumps the root template (`template-v...`). +- A commit touching `packages/auth/**` + `docs/**` → bumps BOTH. +- A commit touching ONLY `packages/core-shared/**` → bumps the root template (core packages aren't independently versioned). + +The conventional-commit `(scope)` parenthetical is for human readability in the changelog — it doesn't drive routing. + +### Pre-1.0 bump policy + +While each package is `<1.0.0`: + +| Commit | Bump | +| --------------------------------------- | ----- | +| `feat:` | patch | +| `fix:` | patch | +| `feat!:` or `BREAKING CHANGE:` footer | minor | +| `perf:` | patch | +| `refactor:` | patch | +| `docs:` | patch | +| `revert:` | patch | +| `chore`, `ci`, `build`, `style`, `test` | none | + +When a package crosses `1.0.0`, standard semver kicks in: `feat:` → minor, `feat!:` → major. + +## Day-to-day commands + +```bash +# Inspect current versions +cat .release-please-manifest.json +node -e "console.log(JSON.parse(require('fs').readFileSync('package.json','utf8')).version)" +git tag --list 'template-v*' --sort=-version:refname | head -5 +git tag --list 'auth-v*' --sort=-version:refname | head -5 + +# See what changed in a feature since a tag +git log auth-v0.1.0..HEAD -- packages/auth/ + +# View a tag's changelog entry +git show :CHANGELOG.md # root +git show :packages/auth/CHANGELOG.md # feature +``` + +## Common scenarios + +### "I want to ship the open release PR right now" + +Merge it. The Action cuts tags + creates GitHub releases for each affected package. + +### "The release PR's grouping looks wrong" + +It's reproducible from commits — you can't directly edit. Either: + +- (a) Land a follow-up commit and let release-please regenerate, OR +- (b) Close the PR, push the missing/fixing commits, release-please will reopen with the new state. + +### "I want to bypass a bump for a specific commit" + +Use `chore:` / `ci:` / `build:` / `style:` / `test:` — those types are configured as hidden + non-bumping. Or extend the commit body with the manual override directive (see release-please [release-as: footer](https://github.com/googleapis/release-please/blob/main/docs/manifest-releaser.md#release-as-vs-release-as)). + +### "I want a manual bump to a specific version" + +Add this trailer to your commit body: + +``` +Release-As: 0.5.0 +``` + +release-please will honor it for the package whose path the commit touches. + +### "I want to skip releasing entirely for one merge" + +Add `[skip release-please]` to the commit subject. The Action will still run but produce no PR changes. + +### "I need to mark a package as breaking-changes-allowed-pre-1.0" + +By default the pre-1.0 policy treats `feat!:` as a minor bump (not major). That's typically what you want — pre-1.0 means the surface can change. If you genuinely want to start signalling stability earlier, edit `bump-patch-for-minor-pre-major: false` in `release-please-config.json`. Restart from a fresh major when crossing 1.0. + +### "Where do I see the per-package CHANGELOG?" + +- Root template: `CHANGELOG.md` at the repo root +- Per-feature: `packages//CHANGELOG.md` + +Don't edit these manually — release-please regenerates them. The exception is the initial `0.1.0` baseline content, which was hand-seeded. + +## Cutting a 1.0.0 release + +The Conventional Commits pre-1.0 policy says `feat!:` bumps minor. To explicitly cross 1.0: + +``` +feat!: cross 1.0 stability boundary + +Release-As: 1.0.0 +``` + +The `Release-As:` trailer overrides the auto-derived bump. From `1.0.0` onward, `feat:` → minor and `feat!:` → major per standard semver. + +## Verifying release-please locally + +```bash +# Install once +pnpm dlx release-please --help + +# Dry-run the manifest workflow +pnpm dlx release-please manifest-pr \ + --token "$GITHUB_TOKEN" \ + --repo-url "$(git remote get-url origin)" \ + --config-file release-please-config.json \ + --manifest-file .release-please-manifest.json \ + --dry-run +``` + +(The CI workflow itself does this on every push to main; local dry-runs are mostly for debugging config changes.) + +## Cross-references + +- [ADR-021](../decisions/adr-021-versioning-and-changelog.md) — the architecture + design rationale +- [Conventional Commits spec](https://www.conventionalcommits.org/) +- [release-please docs](https://github.com/googleapis/release-please) +- `release-please-config.json` — package list + section mapping + bump policy +- `.release-please-manifest.json` — current version per tracked package +- `.github/workflows/release-please.yml` — CI wiring diff --git a/package.json b/package.json index 703811e..0aa02dd 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { - "name": "template", + "name": "template-vertical", + "version": "0.1.0", "private": true, "packageManager": "pnpm@9.15.4", "engines": { diff --git a/packages/auth/CHANGELOG.md b/packages/auth/CHANGELOG.md new file mode 100644 index 0000000..f1a5373 --- /dev/null +++ b/packages/auth/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog — @repo/auth + +All notable changes to the `auth` feature package. Maintained by [release-please](https://github.com/googleapis/release-please) on merges to `main`. See [ADR-021](../../docs/decisions/adr-021-versioning-and-changelog.md) and [`docs/guides/releasing.md`](../../docs/guides/releasing.md). + +## 0.1.0 (2026-05-13) + +### Initial baseline + +The `auth` feature established at v0.1.0 alongside the hybrid versioning rollout (ADR-021). The feature has been stable since the template-reset cleanup; this is the first formally versioned baseline. + +Future entries appear above this section as release-please assembles them from conventional commits scoped to `packages/auth/**` since the last release. diff --git a/packages/auth/package.json b/packages/auth/package.json index c8229cd..af6fde7 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,7 +1,7 @@ { "name": "@repo/auth", "private": true, - "version": "0.0.0", + "version": "0.1.0", "type": "module", "exports": { ".": "./src/index.ts", diff --git a/packages/blog/CHANGELOG.md b/packages/blog/CHANGELOG.md new file mode 100644 index 0000000..416b551 --- /dev/null +++ b/packages/blog/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog — @repo/blog + +All notable changes to the `blog` feature package. Maintained by [release-please](https://github.com/googleapis/release-please) on merges to `main`. See [ADR-021](../../docs/decisions/adr-021-versioning-and-changelog.md) and [`docs/guides/releasing.md`](../../docs/guides/releasing.md). + +## 0.1.0 (2026-05-13) + +### Initial baseline + +The `blog` feature established at v0.1.0 alongside the hybrid versioning rollout (ADR-021). The feature has been stable since the template-reset cleanup; this is the first formally versioned baseline. + +Future entries appear above this section as release-please assembles them from conventional commits scoped to `packages/blog/**` since the last release. diff --git a/packages/blog/package.json b/packages/blog/package.json index 48490ff..e950062 100644 --- a/packages/blog/package.json +++ b/packages/blog/package.json @@ -1,7 +1,7 @@ { "name": "@repo/blog", "private": true, - "version": "0.0.0", + "version": "0.1.0", "type": "module", "exports": { ".": "./src/index.ts", diff --git a/packages/marketing-pages/CHANGELOG.md b/packages/marketing-pages/CHANGELOG.md new file mode 100644 index 0000000..d6f4e66 --- /dev/null +++ b/packages/marketing-pages/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog — @repo/marketing-pages + +All notable changes to the `marketing-pages` feature package. Maintained by [release-please](https://github.com/googleapis/release-please) on merges to `main`. See [ADR-021](../../docs/decisions/adr-021-versioning-and-changelog.md) and [`docs/guides/releasing.md`](../../docs/guides/releasing.md). + +## 0.1.0 (2026-05-13) + +### Initial baseline + +The `marketing-pages` feature established at v0.1.0 alongside the hybrid versioning rollout (ADR-021). The feature has been stable since the template-reset cleanup; this is the first formally versioned baseline. + +Future entries appear above this section as release-please assembles them from conventional commits scoped to `packages/marketing-pages/**` since the last release. diff --git a/packages/marketing-pages/package.json b/packages/marketing-pages/package.json index c0ec6e9..d328a85 100644 --- a/packages/marketing-pages/package.json +++ b/packages/marketing-pages/package.json @@ -1,7 +1,7 @@ { "name": "@repo/marketing-pages", "private": true, - "version": "0.0.0", + "version": "0.1.0", "type": "module", "exports": { ".": "./src/index.ts", diff --git a/packages/media/CHANGELOG.md b/packages/media/CHANGELOG.md new file mode 100644 index 0000000..995844b --- /dev/null +++ b/packages/media/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog — @repo/media + +All notable changes to the `media` feature package. Maintained by [release-please](https://github.com/googleapis/release-please) on merges to `main`. See [ADR-021](../../docs/decisions/adr-021-versioning-and-changelog.md) and [`docs/guides/releasing.md`](../../docs/guides/releasing.md). + +## 0.1.0 (2026-05-13) + +### Initial baseline + +The `media` feature established at v0.1.0 alongside the hybrid versioning rollout (ADR-021). The feature has been stable since the template-reset cleanup; this is the first formally versioned baseline. + +Future entries appear above this section as release-please assembles them from conventional commits scoped to `packages/media/**` since the last release. diff --git a/packages/media/package.json b/packages/media/package.json index 86389d3..2c3b651 100644 --- a/packages/media/package.json +++ b/packages/media/package.json @@ -1,7 +1,7 @@ { "name": "@repo/media", "private": true, - "version": "0.0.0", + "version": "0.1.0", "type": "module", "exports": { ".": "./src/index.ts", diff --git a/packages/navigation/CHANGELOG.md b/packages/navigation/CHANGELOG.md new file mode 100644 index 0000000..5f39c56 --- /dev/null +++ b/packages/navigation/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog — @repo/navigation + +All notable changes to the `navigation` feature package. Maintained by [release-please](https://github.com/googleapis/release-please) on merges to `main`. See [ADR-021](../../docs/decisions/adr-021-versioning-and-changelog.md) and [`docs/guides/releasing.md`](../../docs/guides/releasing.md). + +## 0.1.0 (2026-05-13) + +### Initial baseline + +The `navigation` feature established at v0.1.0 alongside the hybrid versioning rollout (ADR-021). The feature has been stable since the template-reset cleanup; this is the first formally versioned baseline. + +Future entries appear above this section as release-please assembles them from conventional commits scoped to `packages/navigation/**` since the last release. diff --git a/packages/navigation/package.json b/packages/navigation/package.json index bb53384..8a914f1 100644 --- a/packages/navigation/package.json +++ b/packages/navigation/package.json @@ -1,7 +1,7 @@ { "name": "@repo/navigation", "private": true, - "version": "0.0.0", + "version": "0.1.0", "type": "module", "exports": { ".": "./src/index.ts", diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..5c8422f --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,55 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "release-type": "node", + "include-component-in-tag": true, + "separate-pull-requests": false, + "bump-minor-pre-major": false, + "bump-patch-for-minor-pre-major": true, + "draft-pull-request": false, + "prerelease": false, + "changelog-sections": [ + { "type": "feat", "section": "Features" }, + { "type": "fix", "section": "Bug Fixes" }, + { "type": "perf", "section": "Performance" }, + { "type": "refactor", "section": "Refactoring" }, + { "type": "docs", "section": "Documentation" }, + { "type": "revert", "section": "Reverts" }, + { "type": "test", "section": "Tests", "hidden": true }, + { "type": "chore", "section": "Chores", "hidden": true }, + { "type": "ci", "section": "CI", "hidden": true }, + { "type": "build", "section": "Build", "hidden": true }, + { "type": "style", "section": "Style", "hidden": true } + ], + "packages": { + ".": { + "package-name": "template-vertical", + "component": "template", + "changelog-path": "CHANGELOG.md" + }, + "packages/auth": { + "package-name": "@repo/auth", + "component": "auth", + "changelog-path": "CHANGELOG.md" + }, + "packages/blog": { + "package-name": "@repo/blog", + "component": "blog", + "changelog-path": "CHANGELOG.md" + }, + "packages/media": { + "package-name": "@repo/media", + "component": "media", + "changelog-path": "CHANGELOG.md" + }, + "packages/marketing-pages": { + "package-name": "@repo/marketing-pages", + "component": "marketing-pages", + "changelog-path": "CHANGELOG.md" + }, + "packages/navigation": { + "package-name": "@repo/navigation", + "component": "navigation", + "changelog-path": "CHANGELOG.md" + } + } +}