feat(standards): add versioning + changelog standard, release templates, and Drone version guard
Add lib page 11 (SemVer contract, pre-1.0 rule, tag==version invariant, Keep a Changelog), the releasing wiki runbook, and canonical release templates (CHANGELOG.md, VERSION). Vendor them into the MCP bundle as templates/release + release.json and expose registry://release with list/get tools. Add a changelog component to the app template. Enforce the tag<->version contract in both Drone shapes: Type A check-version and the backfill loop verify pyproject version; Type B adds a VERSION guard step.
This commit is contained in:
@@ -186,6 +186,12 @@
|
||||
"placeholder": "",
|
||||
"description": "Packaging + dependency pins."
|
||||
},
|
||||
{
|
||||
"kind": "changelog",
|
||||
"path": "CHANGELOG.md",
|
||||
"placeholder": "",
|
||||
"description": "Keep a Changelog file with an [Unreleased] section (versioning standard)."
|
||||
},
|
||||
{
|
||||
"kind": "dockerfile",
|
||||
"path": "Dockerfile",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"project": "standards",
|
||||
"type": "standards",
|
||||
"modules_count": 14,
|
||||
"modules_count": 15,
|
||||
"source": "hand-authored"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
|
||||
"module": "index",
|
||||
"content": {
|
||||
"path": "index",
|
||||
"docstring": "# Standards Reference\n\nThe reference half of the Standards description: the enforceable rules,\npatterns, and the internal library catalog that agents consult while coding.\n\n- **Build Recipe** — the ordered, agentic checklist for a new app.\n- **Layering Rules** — the strict request → domain → repository layering and\n the rules that enforce it.\n- **Composition Root** — a single composition point wiring the whole app.\n- **Spec-First Routing** — the OpenAPI contract as the routing source of truth.\n- **CRUD Template** — the standardized generic resource endpoint scaffold.\n- **Pipeline Scaffold** — the deterministic pipeline skeleton for multi-stage\n jobs.\n- **Worker & Progress** — queued work, progress reporting, and visibility.\n- **Human-in-the-Loop** — guardrails and approvals inside automations.\n- **Infra Adapters** — how infrastructure connects without leaking into domain.\n- **Drone Pipelines** — the two CI shapes: a PyPI package or a Docker deployment.\n- **Genericity Matrix** — what is library-backed, template-provided, or authored.\n- **Auth Integration** — consuming the shared auth-server service (spec-driven,\n no auth code).\n- **Library Registry** — the catalog of internal libraries behind all of it.\n\nRead the **Wiki** for the philosophy and rationale; these pages are the\nreference that mirrors the `standards` MCP bundle.\n",
|
||||
"docstring": "# Standards Reference\n\nThe reference half of the Standards description: the enforceable rules,\npatterns, and the internal library catalog that agents consult while coding.\n\n- **Build Recipe** — the ordered, agentic checklist for a new app.\n- **Layering Rules** — the strict request → domain → repository layering and\n the rules that enforce it.\n- **Composition Root** — a single composition point wiring the whole app.\n- **Spec-First Routing** — the OpenAPI contract as the routing source of truth.\n- **CRUD Template** — the standardized generic resource endpoint scaffold.\n- **Pipeline Scaffold** — the deterministic pipeline skeleton for multi-stage\n jobs.\n- **Worker & Progress** — queued work, progress reporting, and visibility.\n- **Human-in-the-Loop** — guardrails and approvals inside automations.\n- **Infra Adapters** — how infrastructure connects without leaking into domain.\n- **Drone Pipelines** — the two CI shapes: a PyPI package or a Docker deployment.\n- **Versioning & Changelog** — SemVer, the tag↔version contract, and the release\n changelog.\n- **Genericity Matrix** — what is library-backed, template-provided, or authored.\n- **Auth Integration** — consuming the shared auth-server service (spec-driven,\n no auth code).\n- **Library Registry** — the catalog of internal libraries behind all of it.\n\nRead the **Wiki** for the philosophy and rationale; these pages are the\nreference that mirrors the `standards` MCP bundle.\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
8
standards/mcp/modules/versioning_and_changelog.json
Normal file
8
standards/mcp/modules/versioning_and_changelog.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"module": "versioning_and_changelog",
|
||||
"content": {
|
||||
"path": "versioning_and_changelog",
|
||||
"docstring": "# Versioning & Changelog\n\n## Summary\n\nEvery Aetos artifact — library, service, or site — is versioned with\n[Semantic Versioning](https://semver.org/spec/v2.0.0.html) and documents its\nreleases in a [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) file.\nThe **version file is the source of truth**, the **git tag must equal it**, and\na Drone guard enforces that equality before anything is published. Releases are\ncut manually; a tag is what triggers the pipeline.\n\nThis page pairs with [10 – Drone Pipelines](10_drone_pipelines.md): the version\nand tag contract is exactly what those two CI shapes consume.\n\n---\n\n## The version contract\n\n| Artifact | Source of truth | Notes |\n|----------|-----------------|-------|\n| Library (PyPI package) | `version` in `pyproject.toml` | `check-version` also reads it to skip duplicates |\n| Service / site (Docker) | `VERSION` file at the repo root | one line, `x.y.z` |\n\nRules:\n\n1. **One version per repository.** No per-module versions.\n2. **Tag = version.** The git tag is bare SemVer (`0.0.6`), no `v` prefix, and\n it MUST match the version file byte-for-byte. The Drone guard fails the\n build otherwise — this is what stops a tag from silently publishing nothing.\n3. **A release is a tag.** Bump the version file, promote the changelog, commit,\n tag, push. Everything else is automated by Drone.\n4. **Never reuse a tag.** If a release is wrong, cut a new patch version.\n\n### Pre-1.0 (0.y.z)\n\nWhile a package is `0.y.z`, **`0.y` is treated as the major line**:\n\n- breaking change → `0.(y+1).0`\n- new feature → `0.y.(z+1)`\n- bug fix → `0.y.(z+1)`\n\nOnce a package reaches `1.0.0`, normal SemVer applies. Do not ship `1.0.0`\nuntil the public API is considered stable.\n\n---\n\n## The changelog\n\n`CHANGELOG.md` at the repo root, in Keep a Changelog form. It always has exactly\none open `[Unreleased]` section with the change categories that apply:\n\n```markdown\n## [Unreleased]\n\n### Added\n### Changed\n### Deprecated\n### Removed\n### Fixed\n### Security\n```\n\nOn release you **promote** it — never delete history:\n\n```markdown\n## [0.0.6] - 2026-03-07\n\n### Added\n- ...\n```\n\nand add a link reference at the bottom:\n\n```markdown\n[Unreleased]: https://git.aetoskia.com/aetos/<repo>/compare/0.0.6...HEAD\n[0.0.6]: https://git.aetoskia.com/aetos/<repo>/compare/0.0.5...0.0.6\n```\n\nConventions:\n\n- Write entries for humans: one line each, imperative, referencing the user\n impact, not the commit hash.\n- Categories: **Added**, **Changed**, **Deprecated**, **Removed**, **Fixed**,\n **Security**. Omit empty categories.\n- Security fixes get their own entry even if the change is otherwise a bug fix.\n- The `[Unreleased]` heading is never renamed; copy its content into the new\n version heading and clear it.\n\n---\n\n## Release checklist\n\n1. Confirm the quality gate is green on `main`.\n2. Move every `[Unreleased]` entry under the new version heading with today's\n date (`## [x.y.z] - YYYY-MM-DD`); restore an empty `[Unreleased]` on top.\n3. Add the two compare-link references at the bottom of the changelog.\n4. Set the version file: `pyproject.toml` (`version = \"x.y.z\"`) or `VERSION`.\n5. Commit: `chore(release): x.y.z`.\n6. Tag: `git tag x.y.z` and push the tag (`git push origin x.y.z`).\n7. Watch Drone: Type A publishes the wheel/sdist, Type B builds and deploys the\n image. Both first assert `tag == version`.\n8. Do not reuse the tag if the run fails — fix and cut `x.y.(z+1)`.\n\n---\n\n## Why the guard matters\n\nThe version file and the tag are two facts that must agree; nothing in git\nenforces that by itself. A mismatch is not a theoretical risk — a package can be\ntagged `0.0.2` while its `pyproject.toml` still says `0.0.1`, and the publish\nstep (which compares the **file** against the index) then skips the build\nentirely, so `0.0.2` never reaches the index while looking like a release. The\nDrone guard turns that silent no-op into a hard failure.\n\n---\n\n## Related\n\n- [10 – Drone Pipelines](10_drone_pipelines.md) · [01 – Build Recipe](01_build_recipe.md) · [13 – Library Registry](13_library_registry.md)\n",
|
||||
"objects": {}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,11 @@
|
||||
"resource": "docs://modules/drone_pipelines",
|
||||
"title": "Drone Pipelines"
|
||||
},
|
||||
{
|
||||
"module": "versioning_and_changelog",
|
||||
"resource": "docs://modules/versioning_and_changelog",
|
||||
"title": "Versioning & Changelog"
|
||||
},
|
||||
{
|
||||
"module": "genericity_matrix",
|
||||
"resource": "docs://modules/genericity_matrix",
|
||||
|
||||
19
standards/mcp/release.json
Normal file
19
standards/mcp/release.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"project": "standards",
|
||||
"template": "templates/release",
|
||||
"invariant": "git tag == version file (enforced by the Drone guard)",
|
||||
"components": [
|
||||
{
|
||||
"kind": "changelog",
|
||||
"path": "CHANGELOG.md",
|
||||
"placeholder": "",
|
||||
"description": "Keep a Changelog skeleton: one [Unreleased] section + compare links."
|
||||
},
|
||||
{
|
||||
"kind": "version-file",
|
||||
"path": "VERSION",
|
||||
"placeholder": "",
|
||||
"description": "Single-line SemVer source of truth for services and sites."
|
||||
}
|
||||
]
|
||||
}
|
||||
22
standards/mcp/templates/app/CHANGELOG.md
Normal file
22
standards/mcp/templates/app/CHANGELOG.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project are documented here.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
## [0.1.0] - YYYY-MM-DD
|
||||
|
||||
### Added
|
||||
- Initial blueprint app scaffolded from the Aetos standard.
|
||||
|
||||
[Unreleased]: <repo-url>/compare/0.1.0...HEAD
|
||||
[0.1.0]: <repo-url>/releases/tag/0.1.0
|
||||
@@ -12,6 +12,10 @@
|
||||
# Secrets (Drone): PIP_REPO_URL, PIP_USERNAME, PIP_PASSWORD.
|
||||
# Copy to <repo>/.drone.yml unchanged; the package name and version are read
|
||||
# from pyproject.toml, so no per-repo edits are required.
|
||||
#
|
||||
# Version guard: the tag build fails unless the git tag equals the version in
|
||||
# pyproject.toml (see the versioning standard). This stops a tag from looking
|
||||
# like a release while silently publishing nothing.
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
@@ -37,6 +41,11 @@ steps:
|
||||
commands:
|
||||
- PACKAGE_NAME=$(grep -E '^name\s*=' pyproject.toml | head -1 | cut -d'"' -f2)
|
||||
- VERSION=$(grep -E '^version\s*=' pyproject.toml | head -1 | cut -d'"' -f2)
|
||||
- |
|
||||
if [ "$DRONE_TAG" != "$VERSION" ]; then
|
||||
echo "Tag '$DRONE_TAG' does not match pyproject version '$VERSION'; refusing to publish."
|
||||
exit 1
|
||||
fi
|
||||
- echo "Checking if $PACKAGE_NAME==$VERSION exists on $PIP_REPO_URL ..."
|
||||
- |
|
||||
if curl -fsSL -u "$PIP_USERNAME:$PIP_PASSWORD" "$PIP_REPO_URL/simple/$PACKAGE_NAME/" | grep -qE "${PACKAGE_NAME}-${VERSION}(-|\.tar\.gz|\.whl)"; then
|
||||
@@ -147,6 +156,12 @@ steps:
|
||||
|
||||
git checkout --force "$TAG"
|
||||
|
||||
TAG_VERSION=$(grep -E '^version\s*=' pyproject.toml | head -1 | cut -d'"' -f2)
|
||||
if [ "$TAG" != "$TAG_VERSION" ]; then
|
||||
echo "Tag $TAG != pyproject version $TAG_VERSION at that commit; skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "Building $VERSION"
|
||||
rm -rf dist
|
||||
python -m build
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
# <host-port> published host port; <container-port> the in-image port.
|
||||
# <site> site image name for the site variant (e.g. docs, mcp).
|
||||
# <env-var> runtime environment variable consumed by the service.
|
||||
#
|
||||
# Version guard: the tag-triggered service variant fails unless the git tag
|
||||
# equals the repo-root VERSION file (see the versioning standard). The
|
||||
# push-triggered site variant ships :latest only and has no version to guard.
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
@@ -64,6 +68,22 @@ steps:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: check-version
|
||||
image: alpine:3.20
|
||||
commands:
|
||||
- VERSION=$(cat VERSION 2>/dev/null | tr -d '\n')
|
||||
- echo "tag=$DRONE_TAG version=$VERSION"
|
||||
- |
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "VERSION file missing or empty; add it (see versioning standard)."
|
||||
exit 1
|
||||
fi
|
||||
- |
|
||||
if [ "$DRONE_TAG" != "$VERSION" ]; then
|
||||
echo "Tag '$DRONE_TAG' does not match VERSION '$VERSION'; refusing to deploy."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: check-remote-image
|
||||
image: docker:24
|
||||
volumes:
|
||||
|
||||
36
standards/mcp/templates/release/CHANGELOG.md
Normal file
36
standards/mcp/templates/release/CHANGELOG.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project are documented here.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
|
||||
### Deprecated
|
||||
|
||||
### Removed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Security
|
||||
|
||||
<!--
|
||||
On release, promote the entries above into a new heading with today's date and
|
||||
leave a fresh, empty [Unreleased] section:
|
||||
|
||||
## [0.1.0] - YYYY-MM-DD
|
||||
|
||||
### Added
|
||||
- Initial release.
|
||||
|
||||
Then update the compare links below. See the standard:
|
||||
Versioning & Changelog (registry://release).
|
||||
-->
|
||||
|
||||
[Unreleased]: <repo-url>/compare/0.1.0...HEAD
|
||||
[0.1.0]: <repo-url>/releases/tag/0.1.0
|
||||
1
standards/mcp/templates/release/VERSION
Normal file
1
standards/mcp/templates/release/VERSION
Normal file
@@ -0,0 +1 @@
|
||||
0.0.0
|
||||
Reference in New Issue
Block a user