docs(prd): add automated release process PRD
This commit is contained in:
@@ -26,6 +26,7 @@ Use this file to keep a single prioritized view of all PRDs.
|
||||
| - | - | `prd/reset-student-course-progress` | todo |
|
||||
| - | 15 | `prd/webhooks` | todo |
|
||||
| - | - | `prd/zapier` | draft |
|
||||
| - | - | `prd/release-process` | draft |
|
||||
|
||||
## DONE (Completed PRDs)
|
||||
|
||||
|
||||
@@ -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?
|
||||
Reference in New Issue
Block a user