Files

137 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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?