feat(standards): drop the site CI variant + require changelog reconciliation before release
- Remove the push-triggered build-and-deploy-site pipeline and every "site" reference; Type B is now a single tag-triggered service pipeline (service-docker.yaml). - Inventory now covers packages + the auth-server service only (repos.json 11 -> 9; docs/mcp are no longer part of the standard). - Versioning standard: reconcile [Unreleased] against the last tag before promoting, and always show proposed entries to the user for approval. - Update registry.py compliance/release text and list_repos types, build.py component descriptions, and rebuild the MCP bundle + lib/wiki HTML.
This commit is contained in:
@@ -9,7 +9,7 @@ chosen by what the repository produces:
|
||||
| Type | File | Produces | Repos |
|
||||
|------|------|----------|-------|
|
||||
| **A — PyPI package** | `templates/drone/python-lib.yaml` | A wheel + sdist on the private index | `dagpipe`, `doc-forge`, `hexa`, `mail-intake`, `mongo-ops`, `omniread`, `openapi-first`, `py-jwt` |
|
||||
| **B — Docker deployment** | `templates/drone/service-docker.yaml` | An image pushed to the registry and run as a container | `auth-server`, `docs`, `mcp` |
|
||||
| **B — Docker deployment** | `templates/drone/service-docker.yaml` | An image pushed to the registry and run as a container | `auth-server` |
|
||||
|
||||
Both templates run on `platform: linux / arm64` with the workspace at
|
||||
`/drone/src`. Copy the matching template to `<repo>/.drone.yml`; the Type A file
|
||||
@@ -50,29 +50,24 @@ Secrets: `PIP_REPO_URL`, `PIP_USERNAME`, `PIP_PASSWORD`.
|
||||
|
||||
## Type B — Docker deployment (`service-docker.yaml`)
|
||||
|
||||
For services and sites that ship a container image. The file contains **two
|
||||
pipelines**, each mirroring a repo family:
|
||||
For a service that ships a container image — a long-running API/worker, e.g.
|
||||
`auth-server`. The file contains **one pipeline**, `build-and-deploy-service`,
|
||||
triggered on `tag`:
|
||||
|
||||
1. **`build-and-deploy-service`** — triggered on `tag` (a long-running
|
||||
API/worker service, e.g. `auth-server`):
|
||||
- `fetch-tags` — resolve the latest tag into `LATEST_TAG.txt`; fail if there
|
||||
are no tags.
|
||||
- `check-version` — **guard**: fail unless `$DRONE_TAG` equals the repo-root
|
||||
`VERSION` file (see [11 – Versioning & Changelog](11_versioning_and_changelog.md)).
|
||||
- `check-remote-image` — if `aetos/<app>:<tag>` already exists locally, exit
|
||||
`78` and skip the build.
|
||||
- `quality-gate` — install from `requirements.txt` and run
|
||||
`black` / `ruff` / `mypy` / `pytest`.
|
||||
- `build-image` — `docker build` with the private-index build args, tagged
|
||||
`aetos/<app>:<tag>` and `:latest`.
|
||||
- `push-image` — login, re-tag with `$REGISTRY_HOST`, push both tags.
|
||||
- `stop-old` — `docker rm -f <app>`.
|
||||
- `run-container` — `docker run -d --name <app> -p <host>:<container>
|
||||
--restart always` with the service's `-e` environment.
|
||||
2. **`build-and-deploy-site`** — triggered on `push` (a static/HTTP site, e.g.
|
||||
`docs`, `mcp`): `build-image` → `push-image` → `stop-old` → `run-container`,
|
||||
with no tag resolution, no quality gate, and no version guard (it ships
|
||||
`:latest` only).
|
||||
- `fetch-tags` — resolve the latest tag into `LATEST_TAG.txt`; fail if there
|
||||
are no tags.
|
||||
- `check-version` — **guard**: fail unless `$DRONE_TAG` equals the repo-root
|
||||
`VERSION` file (see [11 – Versioning & Changelog](11_versioning_and_changelog.md)).
|
||||
- `check-remote-image` — if `aetos/<app>:<tag>` already exists locally, exit
|
||||
`78` and skip the build.
|
||||
- `quality-gate` — install from `requirements.txt` and run
|
||||
`black` / `ruff` / `mypy` / `pytest`.
|
||||
- `build-image` — `docker build` with the private-index build args, tagged
|
||||
`aetos/<app>:<tag>` and `:latest`.
|
||||
- `push-image` — login, re-tag with `$REGISTRY_HOST`, push both tags.
|
||||
- `stop-old` — `docker rm -f <app>`.
|
||||
- `run-container` — `docker run -d --name <app> -p <host>:<container>
|
||||
--restart always` with the service's `-e` environment.
|
||||
|
||||
Secrets: `PIP_REPO_URL`, `PIP_USERNAME`, `PIP_PASSWORD` (service build args),
|
||||
`REGISTRY_HOST`, `REGISTRY_USER`, `REGISTRY_PASS`, plus the service runtime env
|
||||
@@ -85,7 +80,6 @@ Placeholders to fill in per repo:
|
||||
| `<app>` | service image / container name | `auth-server` |
|
||||
| `aetos/<app>` | image repository on the registry | `aetos/auth-server` |
|
||||
| `<host-port>` / `<container-port>` | published : in-image port | `9003:8000` |
|
||||
| `<site>` | site image / container name | `docs` |
|
||||
| `<ENV_VAR>` | runtime environment variable | `MONGO_HOST` |
|
||||
|
||||
---
|
||||
@@ -100,8 +94,7 @@ Placeholders to fill in per repo:
|
||||
version to equal the tag; a service needs a root `VERSION` file that equals
|
||||
the tag. See [11 – Versioning & Changelog](11_versioning_and_changelog.md).
|
||||
- Do not rename the pipelines; tooling and runbooks key off
|
||||
`build-and-publish-pypi`, `build-and-deploy-service`, and
|
||||
`build-and-deploy-site`.
|
||||
`build-and-publish-pypi` and `build-and-deploy-service`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Summary
|
||||
|
||||
Every Aetos artifact — library, service, or site — is versioned with
|
||||
Every Aetos artifact — library or service — is versioned with
|
||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html) and documents its
|
||||
releases in a [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) file.
|
||||
The **version file is the source of truth**, the **git tag must equal it**, and
|
||||
@@ -19,7 +19,7 @@ and tag contract is exactly what those two CI shapes consume.
|
||||
| Artifact | Source of truth | Notes |
|
||||
|----------|-----------------|-------|
|
||||
| Library (PyPI package) | `version` in `pyproject.toml` | `check-version` also reads it to skip duplicates |
|
||||
| Service / site (Docker) | `VERSION` file at the repo root | one line, `x.y.z`; the service **reads** it (build-arg) — never hardcode the version |
|
||||
| Service (Docker) | `VERSION` file at the repo root | one line, `x.y.z`; the service **reads** it (build-arg) — never hardcode the version |
|
||||
|
||||
Rules:
|
||||
|
||||
@@ -84,9 +84,28 @@ Conventions:
|
||||
- Categories: **Added**, **Changed**, **Deprecated**, **Removed**, **Fixed**,
|
||||
**Security**. Omit empty categories.
|
||||
- Security fixes get their own entry even if the change is otherwise a bug fix.
|
||||
- Keep `[Unreleased]` current: add an entry in the same change that introduces
|
||||
the behaviour, not later.
|
||||
- The `[Unreleased]` heading is never renamed; copy its content into the new
|
||||
version heading and clear it.
|
||||
|
||||
### Reconcile before releasing
|
||||
|
||||
`[Unreleased]` is a **draft, not the source of truth**. It is only trustworthy
|
||||
when it was maintained alongside each change; a long-running branch or a change
|
||||
that skipped the changelog leaves it stale, so it can silently under-report a
|
||||
release. Verify it against the change before you promote:
|
||||
|
||||
1. List what actually changed since the last release:
|
||||
`git log <last-tag>..HEAD` and `git diff --stat <last-tag>..HEAD`.
|
||||
2. Compare that against `[Unreleased]`; add an entry for every user-facing change
|
||||
that is missing.
|
||||
3. **Always show the proposed additions to the user and wait for approval**
|
||||
before promoting. If an entry is ambiguous, ask rather than guess.
|
||||
|
||||
Only then promote. Never cut a release with a known gap between the diff and the
|
||||
changelog.
|
||||
|
||||
---
|
||||
|
||||
## Tagging strategy (MR-based)
|
||||
@@ -94,8 +113,10 @@ Conventions:
|
||||
The release version is known while the change is still a merge request, so the
|
||||
bump travels with the change and only the tag is added afterwards:
|
||||
|
||||
1. **In the MR** — set the intended release version: `version` in
|
||||
`pyproject.toml` (or the `VERSION` file) to `x.y.z`, and promote the
|
||||
1. **In the MR** — reconcile `[Unreleased]` against the change (see
|
||||
[Reconcile before releasing](#reconcile-before-releasing)) and get the user's
|
||||
approval for any additions. Then set the intended release version: `version`
|
||||
in `pyproject.toml` (or the `VERSION` file) to `x.y.z`, and promote the
|
||||
changelog (below). Changelog entries and link references name the
|
||||
**version/tag**, never a commit id.
|
||||
2. **Merge or squash** the MR into `main`.
|
||||
@@ -125,8 +146,11 @@ After the user confirms the MR is merged/squashed and says to proceed:
|
||||
## Release checklist
|
||||
|
||||
1. Confirm the quality gate is green and `main` is up to date.
|
||||
2. In the MR: move every `[Unreleased]` entry under the new version heading with
|
||||
the target date (`## [x.y.z] - YYYY-MM-DD`); restore an empty `[Unreleased]`
|
||||
2. In the MR: reconcile `[Unreleased]` against the change being released —
|
||||
`git log <last-tag>..HEAD` and `git diff --stat <last-tag>..HEAD` — add any
|
||||
missing user-facing entries, and get the user's approval for the additions.
|
||||
Then move every `[Unreleased]` entry under the new version heading with the
|
||||
target date (`## [x.y.z] - YYYY-MM-DD`) and restore an empty `[Unreleased]`
|
||||
on top.
|
||||
3. In the MR: add the two compare-link references at the bottom of the
|
||||
changelog, naming the version/tag (never a commit id).
|
||||
|
||||
@@ -32,19 +32,16 @@ A wheel + sdist published to the private index. Version source is
|
||||
|
||||
---
|
||||
|
||||
## Services & sites (Type B)
|
||||
## Services (Type B)
|
||||
|
||||
A container image pushed to the registry and run on the Docker host. Version
|
||||
source is the `VERSION` file; Drone file is `service-docker.yaml`.
|
||||
|
||||
| Repo | Type | Path | Trigger | Docs |
|
||||
|------|------|------|---------|------|
|
||||
| `auth-server` | service | `auth-server` | tag | api, lib, wiki |
|
||||
| `docs` | site | `docs` | push | — |
|
||||
| `mcp` | site | `mcp` | push | — |
|
||||
| Repo | Path | Trigger | Docs |
|
||||
|------|------|---------|------|
|
||||
| `auth-server` | `auth-server` | tag | api, lib, wiki |
|
||||
|
||||
The **service** variant is tag-triggered and enforces `tag == VERSION`. The
|
||||
**site** variant is push-triggered, ships `:latest`, and has no tag guard.
|
||||
Tag-triggered and enforces `tag == VERSION`.
|
||||
|
||||
---
|
||||
|
||||
@@ -53,7 +50,7 @@ The **service** variant is tag-triggered and enforces `tag == VERSION`. The
|
||||
| Type | Version source | Changelog | Drone |
|
||||
|------|----------------|-----------|-------|
|
||||
| package | `pyproject.toml` | `CHANGELOG.md` | `python-lib.yaml` |
|
||||
| service / site | `VERSION` | `CHANGELOG.md` | `service-docker.yaml` |
|
||||
| service | `VERSION` | `CHANGELOG.md` | `service-docker.yaml` |
|
||||
|
||||
Every covered repository must be able to satisfy `tag == version`:
|
||||
the version file (never a commit id) is bumped in the release MR, the MR is
|
||||
|
||||
Reference in New Issue
Block a user