docs(prd): add automated release process PRD

This commit is contained in:
Rotimi Best
2026-07-20 01:21:38 +01:00
parent 16803bfa3d
commit 55e6ddd37e
2 changed files with 137 additions and 0 deletions
+1
View File
@@ -26,6 +26,7 @@ Use this file to keep a single prioritized view of all PRDs.
| - | - | `prd/reset-student-course-progress` | todo | | - | - | `prd/reset-student-course-progress` | todo |
| - | 15 | `prd/webhooks` | todo | | - | 15 | `prd/webhooks` | todo |
| - | - | `prd/zapier` | draft | | - | - | `prd/zapier` | draft |
| - | - | `prd/release-process` | draft |
## DONE (Completed PRDs) ## DONE (Completed PRDs)
+136
View File
@@ -0,0 +1,136 @@
# Automated Release Process & Changelog PRD
## Purpose
Give ClassroomIO a real **release** mechanism — versioned, tagged, changelog'd
snapshots that self-hosters can pin to and read about — on top of the existing
continuous **deploy** to production.
Today `main` merges deploy to prod continuously (CI builds Docker images), but
there is no reliable release: the `release` script runs the **deprecated**
`standard-version` manually and has drifted (`package.json` says `0.1.13`, the
latest git tag is `v1.0.0`, and `CHANGELOG.md` only reflects `0.1.x`). This PRD
replaces that manual step with **release-please**, which prepares a versioned
release continuously and lets a maintainer cut it on a cadence with one merge.
## The governing distinction
> **Deploy ≠ Release.** A deploy is "the code running on our prod server" and
> stays continuous/automatic on every `main` merge (unchanged by this PRD). A
> release is a named, tagged, changelog'd snapshot for the **community** to
> self-host and pin to. This PRD only adds the release track; it does not touch
> how prod deploys.
## Confirmed decisions
1. **Tool: [release-please](https://github.com/googleapis/release-please)
(v4 action).** GitHub-native, reads Conventional Commits (already enforced),
maintains a permanent "Release PR." This is the maintainer-recommended
successor to `standard-version`.
2. **Model: cadence-by-habit, not release-on-every-merge and not cron.**
release-please keeps a Release PR always up to date; a maintainer merges it on
a chosen rhythm (target: **weekly**). Merging *work* PRs never releases;
merging the *Release PR* is the one and only release trigger. This mirrors how
cal.com operates (human-triggered, machine-prepared).
3. **Versioning: SemVer, single version for the whole app.** `feat` → minor,
`fix` → patch, `feat!`/`BREAKING CHANGE` → major. One version tag `vX.Y.Z`
covers the Dockerized app (api + dashboard + jobs + packages).
4. **Side packages stay independently published.** `@classroomio/mcp`
(`publish-mcp.yml`) and `@classroomio/course-app` (`publish-course-app.yml`)
keep their own manual `workflow_dispatch` publish flows. release-please does
**not** version or publish them. (Not Changesets — that only pays off with
many independently-versioned npm packages; we ship one app.)
5. **Docker images reuse the existing tag trigger.** `docker-publish.yml` already
fires on `push: tags: v*`; a release-please tag builds the versioned image
automatically. No new build pipeline.
6. **Retire `standard-version`.** Remove the dep and the `"release"` script.
## Current state audit
| Capability | Status | Location |
| --- | --- | --- |
| Continuous deploy on `main` | works | `docker-publish.yml` (`push: branches: main`, path-filtered) |
| Docker build on version tag | works | `docker-publish.yml` (`push: tags: v*`) + `workflow_dispatch` |
| Conventional Commits enforced | works (PR-title lint) | `semantic-pull-requests.yml` (`amannn/action-semantic-pull-request@v5`) |
| Squash-merge → conventional commit on `main` | works | commit history (`feat(...): ... (#NNN)`) |
| Changelog generation | **deprecated + drifted** | `standard-version` dep + `"release": "standard-version"`; `CHANGELOG.md` stale at `0.1.x` |
| Version source of truth | **inconsistent** | `package.json` `0.1.13` vs latest git tag `v1.0.0` |
| GitHub Releases | none/ad hoc | — |
| Side-package publish | manual, independent | `publish-mcp.yml`, `publish-course-app.yml` (`workflow_dispatch`) |
## Target flow
```
Mon–Thu merge work PRs → prod deploys continuously (unchanged)
release-please keeps the "Release PR" (version + CHANGELOG) up to date
Friday maintainer skims the Release PR changelog → merges it
→ tag vX.Y.Z + GitHub Release + CHANGELOG.md committed
→ docker-publish.yml builds classroomio:vX.Y.Z automatically
```
## Backend / infra changes
### New files
- **`.github/workflows/release-please.yml`** — `on: push: branches: [main]`;
`googleapis/release-please-action@v4`; permissions `contents: write`,
`pull-requests: write`.
- **`release-please-config.json`** — single root package:
`{ "packages": { ".": { "release-type": "node", "changelog-path": "CHANGELOG.md", "bump-minor-pre-major": true } } }`.
- **`release-please-manifest.json`** — seed with the agreed current version
(see Open questions): `{ ".": "1.0.0" }`.
### Changes
- **`package.json`** — remove `standard-version` from `devDependencies` and
remove the `"release": "standard-version"` script. Reconcile `version` to the
manifest seed so it stops drifting.
- **`docker-publish.yml`** — ensure the tag-triggered build reliably fires for
release-please tags (see Risk below); optionally add a `release: [published]`
trigger as a belt-and-suspenders path.
## Risks / gotchas
1. **`GITHUB_TOKEN` does not trigger downstream workflows.** A tag pushed by
release-please using the default `GITHUB_TOKEN` will **not** fire
`docker-publish.yml`'s `tags: v*` trigger. Resolve by giving release-please a
**GitHub App token / PAT**, **or** adding a `release: [published]` trigger to
`docker-publish.yml`. Must be decided before rollout or releases won't build
images.
2. **Version reconciliation.** The manifest seed must match reality or the first
release computes a wrong next version. Pick `v1.0.0` (latest tag) vs `0.1.13`
(package.json) up front.
3. **Unattended cron rejected.** Fully-automated scheduled publishing was
considered and rejected: a bad image would reach real self-hosted deployments
with unreviewed notes. Cadence stays a deliberate human merge.
## How modern OSS projects informed this
- **cal.com** — SemVer, ~monthly headline releases + patches; machine-generated
draft release notes, **human triggers and reviews** before publish; Docker
builds on the `v*` tag. (This PRD automates one step further: the version is
computed, not hand-typed.)
- **Polar** — Changesets, per-package SemVer, frequent alpha/beta releases —
fits their **many published SDKs**, which is why we don't adopt it.
- **Supabase** — two-track: continuous per-repo releases **plus** a curated
quarterly marketing changelog. Track 2 is out of scope here (see below).
## Out of scope (possible follow-ups)
- **Curated end-user changelog** (a cal.com/Supabase-style blog or docs "What's
new in vX" page) written for humans, batched at a slower cadence. Track 1
(GitHub Releases + `CHANGELOG.md`) ships first; this is Track 2.
- Bringing `@classroomio/mcp` / `@classroomio/course-app` into release-please as
independently-versioned components (would move us toward a manifest with
multiple packages).
- Pre-release / release-candidate channel (e.g. `next` tag) before promoting to
stable.
## Open questions
1. **Manifest seed version** — start from `v1.0.0` (latest git tag,
recommended) or `0.1.13` (current `package.json`)?
2. **Downstream-trigger fix** — GitHub App token/PAT for release-please, or add
`release: [published]` to `docker-publish.yml`? (Recommend the App token so
the existing tag trigger keeps working unchanged.)
3. **Cadence** — weekly (recommended) or biweekly Release-PR merge?