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:
4
build.py
4
build.py
@@ -371,7 +371,7 @@ _DRONE_COMPONENTS: list[dict[str, str]] = [
|
|||||||
{"kind": "drone-pypi", "path": "python-lib.yaml", "placeholder": "",
|
{"kind": "drone-pypi", "path": "python-lib.yaml", "placeholder": "",
|
||||||
"description": "Drone pipeline: build + publish a Python package to the private PyPI index."},
|
"description": "Drone pipeline: build + publish a Python package to the private PyPI index."},
|
||||||
{"kind": "drone-service", "path": "service-docker.yaml", "placeholder": "",
|
{"kind": "drone-service", "path": "service-docker.yaml", "placeholder": "",
|
||||||
"description": "Drone pipeline: build, push, and deploy a Docker service or site."},
|
"description": "Drone pipeline: build, push, and deploy a Docker service."},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -447,7 +447,7 @@ _RELEASE_COMPONENTS: list[dict[str, str]] = [
|
|||||||
{"kind": "changelog", "path": "CHANGELOG.md", "placeholder": "",
|
{"kind": "changelog", "path": "CHANGELOG.md", "placeholder": "",
|
||||||
"description": "Keep a Changelog skeleton: one [Unreleased] section + compare links."},
|
"description": "Keep a Changelog skeleton: one [Unreleased] section + compare links."},
|
||||||
{"kind": "version-file", "path": "VERSION", "placeholder": "",
|
{"kind": "version-file", "path": "VERSION", "placeholder": "",
|
||||||
"description": "Single-line SemVer source of truth for services and sites."},
|
"description": "Single-line SemVer source of truth for services."},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
16
registry.py
16
registry.py
@@ -105,10 +105,10 @@ _COMPLIANCE_TEXT = (
|
|||||||
"For each repository, bring it to the standard for its `type`:\n\n"
|
"For each repository, bring it to the standard for its `type`:\n\n"
|
||||||
"- **package** — `version` in `pyproject.toml`; root `CHANGELOG.md`; Drone file\n"
|
"- **package** — `version` in `pyproject.toml`; root `CHANGELOG.md`; Drone file\n"
|
||||||
" from `get_drone_template(\"drone-pypi\")` (`python-lib.yaml`, tag-triggered).\n"
|
" from `get_drone_template(\"drone-pypi\")` (`python-lib.yaml`, tag-triggered).\n"
|
||||||
"- **service / site** — root `VERSION`; root `CHANGELOG.md`; Drone file from\n"
|
"- **service** — root `VERSION`; root `CHANGELOG.md`; Drone file from\n"
|
||||||
" `get_drone_template(\"drone-service\")` (`service-docker.yaml`; tag-triggered\n"
|
" `get_drone_template(\"drone-service\")` (`service-docker.yaml`, tag-triggered\n"
|
||||||
" service variant, push-triggered site variant). The service must read its\n"
|
" with a `tag == VERSION` guard). The service must read its version from\n"
|
||||||
" version from `VERSION` (e.g. a build arg) — never hardcode it.\n\n"
|
" `VERSION` (e.g. a build arg) — never hardcode it.\n\n"
|
||||||
"Steps:\n"
|
"Steps:\n"
|
||||||
"1. Inspect each repo (`git -C <path> tag`, version file, `CHANGELOG.md`,\n"
|
"1. Inspect each repo (`git -C <path> tag`, version file, `CHANGELOG.md`,\n"
|
||||||
" `.drone.yml`) and list the gaps — never assume.\n"
|
" `.drone.yml`) and list the gaps — never assume.\n"
|
||||||
@@ -129,7 +129,7 @@ _RELEASE_TEXT = (
|
|||||||
"1. Wait for that explicit go-ahead.\n"
|
"1. Wait for that explicit go-ahead.\n"
|
||||||
"2. Find the commit that introduced the version bump `x.y.z`:\n"
|
"2. Find the commit that introduced the version bump `x.y.z`:\n"
|
||||||
" - package: `git log -S 'version = \"x.y.z\"' -n 1 --format='%H %s' -- pyproject.toml`\n"
|
" - package: `git log -S 'version = \"x.y.z\"' -n 1 --format='%H %s' -- pyproject.toml`\n"
|
||||||
" - service / site: `git log -S 'x.y.z' -n 1 --format='%H %s' -- VERSION`\n"
|
" - service: `git log -S 'x.y.z' -n 1 --format='%H %s' -- VERSION`\n"
|
||||||
"3. Verify that commit's version file equals `x.y.z` (the Drone guard enforces\n"
|
"3. Verify that commit's version file equals `x.y.z` (the Drone guard enforces\n"
|
||||||
" `tag == version`).\n"
|
" `tag == version`).\n"
|
||||||
"4. Tag **only** that commit, lightweight and bare: `git tag x.y.z <sha>`.\n"
|
"4. Tag **only** that commit, lightweight and bare: `git tag x.y.z <sha>`.\n"
|
||||||
@@ -517,13 +517,13 @@ class StandardsMCPServer(MCPServer):
|
|||||||
def list_repos(type: str | None = None) -> list[dict[str, Any]]:
|
def list_repos(type: str | None = None) -> list[dict[str, Any]]:
|
||||||
"""List the repositories covered by the CI and versioning standards.
|
"""List the repositories covered by the CI and versioning standards.
|
||||||
|
|
||||||
Each entry has: ``name``, ``type`` ("package" | "service" | "site"),
|
Each entry has: ``name``, ``type`` ("package" | "service"),
|
||||||
``path`` (relative to the aetos/ workspace root), ``version_source``
|
``path`` (relative to the aetos/ workspace root), ``version_source``
|
||||||
("pyproject.toml" | "VERSION"), ``drone`` (the template file),
|
("pyproject.toml" | "VERSION"), ``drone`` (the template file),
|
||||||
``trigger`` ("tag" | "push"), ``docs`` kinds, and ``mcp``.
|
``trigger`` ("tag"), ``docs`` kinds, and ``mcp``.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
type (str | None): Optional filter, e.g. "package" or "site".
|
type (str | None): Optional filter, e.g. "package" or "service".
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[dict[str, Any]]: Matching repository entries.
|
list[dict[str, Any]]: Matching repository entries.
|
||||||
|
|||||||
@@ -769,7 +769,7 @@ chosen by what the repository produces:</p>
|
|||||||
<td><strong>B — Docker deployment</strong></td>
|
<td><strong>B — Docker deployment</strong></td>
|
||||||
<td><code>templates/drone/service-docker.yaml</code></td>
|
<td><code>templates/drone/service-docker.yaml</code></td>
|
||||||
<td>An image pushed to the registry and run as a container</td>
|
<td>An image pushed to the registry and run as a container</td>
|
||||||
<td><code>auth-server</code>, <code>docs</code>, <code>mcp</code></td>
|
<td><code>auth-server</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -808,30 +808,25 @@ with <code>from_secret</code> — never commit credentials.</p>
|
|||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="type-b-docker-deployment-service-dockeryaml">Type B — Docker deployment (<code>service-docker.yaml</code>)</h2>
|
<h2 id="type-b-docker-deployment-service-dockeryaml">Type B — Docker deployment (<code>service-docker.yaml</code>)</h2>
|
||||||
<p>For services and sites that ship a container image. The file contains <strong>two
|
<p>For a service that ships a container image — a long-running API/worker, e.g.
|
||||||
pipelines</strong>, each mirroring a repo family:</p>
|
<code>auth-server</code>. The file contains <strong>one pipeline</strong>, <code>build-and-deploy-service</code>,
|
||||||
<ol>
|
triggered on <code>tag</code>:</p>
|
||||||
<li><strong><code>build-and-deploy-service</code></strong> — triggered on <code>tag</code> (a long-running
|
<ul>
|
||||||
API/worker service, e.g. <code>auth-server</code>):</li>
|
|
||||||
<li><code>fetch-tags</code> — resolve the latest tag into <code>LATEST_TAG.txt</code>; fail if there
|
<li><code>fetch-tags</code> — resolve the latest tag into <code>LATEST_TAG.txt</code>; fail if there
|
||||||
are no tags.</li>
|
are no tags.</li>
|
||||||
<li><code>check-version</code> — <strong>guard</strong>: fail unless <code>$DRONE_TAG</code> equals the repo-root
|
<li><code>check-version</code> — <strong>guard</strong>: fail unless <code>$DRONE_TAG</code> equals the repo-root
|
||||||
<code>VERSION</code> file (see <a href="../11_versioning_and_changelog/">11 – Versioning & Changelog</a>).</li>
|
<code>VERSION</code> file (see <a href="../11_versioning_and_changelog/">11 – Versioning & Changelog</a>).</li>
|
||||||
<li><code>check-remote-image</code> — if <code>aetos/<app>:<tag></code> already exists locally, exit
|
<li><code>check-remote-image</code> — if <code>aetos/<app>:<tag></code> already exists locally, exit
|
||||||
<code>78</code> and skip the build.</li>
|
<code>78</code> and skip the build.</li>
|
||||||
<li><code>quality-gate</code> — install from <code>requirements.txt</code> and run
|
<li><code>quality-gate</code> — install from <code>requirements.txt</code> and run
|
||||||
<code>black</code> / <code>ruff</code> / <code>mypy</code> / <code>pytest</code>.</li>
|
<code>black</code> / <code>ruff</code> / <code>mypy</code> / <code>pytest</code>.</li>
|
||||||
<li><code>build-image</code> — <code>docker build</code> with the private-index build args, tagged
|
<li><code>build-image</code> — <code>docker build</code> with the private-index build args, tagged
|
||||||
<code>aetos/<app>:<tag></code> and <code>:latest</code>.</li>
|
<code>aetos/<app>:<tag></code> and <code>:latest</code>.</li>
|
||||||
<li><code>push-image</code> — login, re-tag with <code>$REGISTRY_HOST</code>, push both tags.</li>
|
<li><code>push-image</code> — login, re-tag with <code>$REGISTRY_HOST</code>, push both tags.</li>
|
||||||
<li><code>stop-old</code> — <code>docker rm -f <app></code>.</li>
|
<li><code>stop-old</code> — <code>docker rm -f <app></code>.</li>
|
||||||
<li><code>run-container</code> — <code>docker run -d --name <app> -p <host>:<container>
|
<li><code>run-container</code> — <code>docker run -d --name <app> -p <host>:<container>
|
||||||
--restart always</code> with the service's <code>-e</code> environment.</li>
|
--restart always</code> with the service's <code>-e</code> environment.</li>
|
||||||
<li><strong><code>build-and-deploy-site</code></strong> — triggered on <code>push</code> (a static/HTTP site, e.g.
|
</ul>
|
||||||
<code>docs</code>, <code>mcp</code>): <code>build-image</code> → <code>push-image</code> → <code>stop-old</code> → <code>run-container</code>,
|
|
||||||
with no tag resolution, no quality gate, and no version guard (it ships
|
|
||||||
<code>:latest</code> only).</li>
|
|
||||||
</ol>
|
|
||||||
<p>Secrets: <code>PIP_REPO_URL</code>, <code>PIP_USERNAME</code>, <code>PIP_PASSWORD</code> (service build args),
|
<p>Secrets: <code>PIP_REPO_URL</code>, <code>PIP_USERNAME</code>, <code>PIP_PASSWORD</code> (service build args),
|
||||||
<code>REGISTRY_HOST</code>, <code>REGISTRY_USER</code>, <code>REGISTRY_PASS</code>, plus the service runtime env
|
<code>REGISTRY_HOST</code>, <code>REGISTRY_USER</code>, <code>REGISTRY_PASS</code>, plus the service runtime env
|
||||||
(for example <code>MONGO_*</code>, <code>JWT_SECRET</code>).</p>
|
(for example <code>MONGO_*</code>, <code>JWT_SECRET</code>).</p>
|
||||||
@@ -861,11 +856,6 @@ pipelines</strong>, each mirroring a repo family:</p>
|
|||||||
<td><code>9003:8000</code></td>
|
<td><code>9003:8000</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code><site></code></td>
|
|
||||||
<td>site image / container name</td>
|
|
||||||
<td><code>docs</code></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code><ENV_VAR></code></td>
|
<td><code><ENV_VAR></code></td>
|
||||||
<td>runtime environment variable</td>
|
<td>runtime environment variable</td>
|
||||||
<td><code>MONGO_HOST</code></td>
|
<td><code>MONGO_HOST</code></td>
|
||||||
@@ -883,8 +873,7 @@ pipelines</strong>, each mirroring a repo family:</p>
|
|||||||
version to equal the tag; a service needs a root <code>VERSION</code> file that equals
|
version to equal the tag; a service needs a root <code>VERSION</code> file that equals
|
||||||
the tag. See <a href="../11_versioning_and_changelog/">11 – Versioning & Changelog</a>.</li>
|
the tag. See <a href="../11_versioning_and_changelog/">11 – Versioning & Changelog</a>.</li>
|
||||||
<li>Do not rename the pipelines; tooling and runbooks key off
|
<li>Do not rename the pipelines; tooling and runbooks key off
|
||||||
<code>build-and-publish-pypi</code>, <code>build-and-deploy-service</code>, and
|
<code>build-and-publish-pypi</code> and <code>build-and-deploy-service</code>.</li>
|
||||||
<code>build-and-deploy-site</code>.</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="related">Related</h2>
|
<h2 id="related">Related</h2>
|
||||||
|
|||||||
@@ -556,6 +556,21 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<nav class="md-nav" aria-label="The changelog">
|
||||||
|
<ul class="md-nav__list">
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#reconcile-before-releasing" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Reconcile before releasing
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
@@ -771,6 +786,21 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<nav class="md-nav" aria-label="The changelog">
|
||||||
|
<ul class="md-nav__list">
|
||||||
|
|
||||||
|
<li class="md-nav__item">
|
||||||
|
<a href="#reconcile-before-releasing" class="md-nav__link">
|
||||||
|
<span class="md-ellipsis">
|
||||||
|
Reconcile before releasing
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
@@ -842,7 +872,7 @@
|
|||||||
|
|
||||||
<h1 id="versioning-changelog">Versioning & Changelog</h1>
|
<h1 id="versioning-changelog">Versioning & Changelog</h1>
|
||||||
<h2 id="summary">Summary</h2>
|
<h2 id="summary">Summary</h2>
|
||||||
<p>Every Aetos artifact — library, service, or site — is versioned with
|
<p>Every Aetos artifact — library or service — is versioned with
|
||||||
<a href="https://semver.org/spec/v2.0.0.html">Semantic Versioning</a> and documents its
|
<a href="https://semver.org/spec/v2.0.0.html">Semantic Versioning</a> and documents its
|
||||||
releases in a <a href="https://keepachangelog.com/en/1.1.0/">Keep a Changelog</a> file.
|
releases in a <a href="https://keepachangelog.com/en/1.1.0/">Keep a Changelog</a> file.
|
||||||
The <strong>version file is the source of truth</strong>, the <strong>git tag must equal it</strong>, and
|
The <strong>version file is the source of truth</strong>, the <strong>git tag must equal it</strong>, and
|
||||||
@@ -867,7 +897,7 @@ and tag contract is exactly what those two CI shapes consume.</p>
|
|||||||
<td><code>check-version</code> also reads it to skip duplicates</td>
|
<td><code>check-version</code> also reads it to skip duplicates</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Service / site (Docker)</td>
|
<td>Service (Docker)</td>
|
||||||
<td><code>VERSION</code> file at the repo root</td>
|
<td><code>VERSION</code> file at the repo root</td>
|
||||||
<td>one line, <code>x.y.z</code>; the service <strong>reads</strong> it (build-arg) — never hardcode the version</td>
|
<td>one line, <code>x.y.z</code>; the service <strong>reads</strong> it (build-arg) — never hardcode the version</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -934,16 +964,35 @@ one open <code>[Unreleased]</code> section with the change categories that apply
|
|||||||
<li>Categories: <strong>Added</strong>, <strong>Changed</strong>, <strong>Deprecated</strong>, <strong>Removed</strong>, <strong>Fixed</strong>,
|
<li>Categories: <strong>Added</strong>, <strong>Changed</strong>, <strong>Deprecated</strong>, <strong>Removed</strong>, <strong>Fixed</strong>,
|
||||||
<strong>Security</strong>. Omit empty categories.</li>
|
<strong>Security</strong>. Omit empty categories.</li>
|
||||||
<li>Security fixes get their own entry even if the change is otherwise a bug fix.</li>
|
<li>Security fixes get their own entry even if the change is otherwise a bug fix.</li>
|
||||||
|
<li>Keep <code>[Unreleased]</code> current: add an entry in the same change that introduces
|
||||||
|
the behaviour, not later.</li>
|
||||||
<li>The <code>[Unreleased]</code> heading is never renamed; copy its content into the new
|
<li>The <code>[Unreleased]</code> heading is never renamed; copy its content into the new
|
||||||
version heading and clear it.</li>
|
version heading and clear it.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<h3 id="reconcile-before-releasing">Reconcile before releasing</h3>
|
||||||
|
<p><code>[Unreleased]</code> is a <strong>draft, not the source of truth</strong>. 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:</p>
|
||||||
|
<ol>
|
||||||
|
<li>List what actually changed since the last release:
|
||||||
|
<code>git log <last-tag>..HEAD</code> and <code>git diff --stat <last-tag>..HEAD</code>.</li>
|
||||||
|
<li>Compare that against <code>[Unreleased]</code>; add an entry for every user-facing change
|
||||||
|
that is missing.</li>
|
||||||
|
<li><strong>Always show the proposed additions to the user and wait for approval</strong>
|
||||||
|
before promoting. If an entry is ambiguous, ask rather than guess.</li>
|
||||||
|
</ol>
|
||||||
|
<p>Only then promote. Never cut a release with a known gap between the diff and the
|
||||||
|
changelog.</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="tagging-strategy-mr-based">Tagging strategy (MR-based)</h2>
|
<h2 id="tagging-strategy-mr-based">Tagging strategy (MR-based)</h2>
|
||||||
<p>The release version is known while the change is still a merge request, so the
|
<p>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:</p>
|
bump travels with the change and only the tag is added afterwards:</p>
|
||||||
<ol>
|
<ol>
|
||||||
<li><strong>In the MR</strong> — set the intended release version: <code>version</code> in
|
<li><strong>In the MR</strong> — reconcile <code>[Unreleased]</code> against the change (see
|
||||||
<code>pyproject.toml</code> (or the <code>VERSION</code> file) to <code>x.y.z</code>, and promote the
|
<a href="#reconcile-before-releasing">Reconcile before releasing</a>) and get the user's
|
||||||
|
approval for any additions. Then set the intended release version: <code>version</code>
|
||||||
|
in <code>pyproject.toml</code> (or the <code>VERSION</code> file) to <code>x.y.z</code>, and promote the
|
||||||
changelog (below). Changelog entries and link references name the
|
changelog (below). Changelog entries and link references name the
|
||||||
<strong>version/tag</strong>, never a commit id.</li>
|
<strong>version/tag</strong>, never a commit id.</li>
|
||||||
<li><strong>Merge or squash</strong> the MR into <code>main</code>.</li>
|
<li><strong>Merge or squash</strong> the MR into <code>main</code>.</li>
|
||||||
@@ -970,8 +1019,11 @@ After the user confirms the MR is merged/squashed and says to proceed:</p>
|
|||||||
<h2 id="release-checklist">Release checklist</h2>
|
<h2 id="release-checklist">Release checklist</h2>
|
||||||
<ol>
|
<ol>
|
||||||
<li>Confirm the quality gate is green and <code>main</code> is up to date.</li>
|
<li>Confirm the quality gate is green and <code>main</code> is up to date.</li>
|
||||||
<li>In the MR: move every <code>[Unreleased]</code> entry under the new version heading with
|
<li>In the MR: reconcile <code>[Unreleased]</code> against the change being released —
|
||||||
the target date (<code>## [x.y.z] - YYYY-MM-DD</code>); restore an empty <code>[Unreleased]</code>
|
<code>git log <last-tag>..HEAD</code> and <code>git diff --stat <last-tag>..HEAD</code> — add any
|
||||||
|
missing user-facing entries, and get the user's approval for the additions.
|
||||||
|
Then move every <code>[Unreleased]</code> entry under the new version heading with the
|
||||||
|
target date (<code>## [x.y.z] - YYYY-MM-DD</code>) and restore an empty <code>[Unreleased]</code>
|
||||||
on top.</li>
|
on top.</li>
|
||||||
<li>In the MR: add the two compare-link references at the bottom of the
|
<li>In the MR: add the two compare-link references at the bottom of the
|
||||||
changelog, naming the version/tag (never a commit id).</li>
|
changelog, naming the version/tag (never a commit id).</li>
|
||||||
|
|||||||
@@ -557,9 +557,9 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#services-sites-type-b" class="md-nav__link">
|
<a href="#services-type-b" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Services & sites (Type B)
|
Services (Type B)
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -711,9 +711,9 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="md-nav__item">
|
<li class="md-nav__item">
|
||||||
<a href="#services-sites-type-b" class="md-nav__link">
|
<a href="#services-type-b" class="md-nav__link">
|
||||||
<span class="md-ellipsis">
|
<span class="md-ellipsis">
|
||||||
Services & sites (Type B)
|
Services (Type B)
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -837,14 +837,13 @@ Paths are relative to the <code>aetos/</code> workspace root.</p>
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="services-sites-type-b">Services & sites (Type B)</h2>
|
<h2 id="services-type-b">Services (Type B)</h2>
|
||||||
<p>A container image pushed to the registry and run on the Docker host. Version
|
<p>A container image pushed to the registry and run on the Docker host. Version
|
||||||
source is the <code>VERSION</code> file; Drone file is <code>service-docker.yaml</code>.</p>
|
source is the <code>VERSION</code> file; Drone file is <code>service-docker.yaml</code>.</p>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Repo</th>
|
<th>Repo</th>
|
||||||
<th>Type</th>
|
|
||||||
<th>Path</th>
|
<th>Path</th>
|
||||||
<th>Trigger</th>
|
<th>Trigger</th>
|
||||||
<th>Docs</th>
|
<th>Docs</th>
|
||||||
@@ -853,29 +852,13 @@ source is the <code>VERSION</code> file; Drone file is <code>service-docker.yaml
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>auth-server</code></td>
|
<td><code>auth-server</code></td>
|
||||||
<td>service</td>
|
|
||||||
<td><code>auth-server</code></td>
|
<td><code>auth-server</code></td>
|
||||||
<td>tag</td>
|
<td>tag</td>
|
||||||
<td>api, lib, wiki</td>
|
<td>api, lib, wiki</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td><code>docs</code></td>
|
|
||||||
<td>site</td>
|
|
||||||
<td><code>docs</code></td>
|
|
||||||
<td>push</td>
|
|
||||||
<td>—</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>mcp</code></td>
|
|
||||||
<td>site</td>
|
|
||||||
<td><code>mcp</code></td>
|
|
||||||
<td>push</td>
|
|
||||||
<td>—</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p>The <strong>service</strong> variant is tag-triggered and enforces <code>tag == VERSION</code>. The
|
<p>Tag-triggered and enforces <code>tag == VERSION</code>.</p>
|
||||||
<strong>site</strong> variant is push-triggered, ships <code>:latest</code>, and has no tag guard.</p>
|
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="required-files-per-repo">Required files per repo</h2>
|
<h2 id="required-files-per-repo">Required files per repo</h2>
|
||||||
<table>
|
<table>
|
||||||
@@ -895,7 +878,7 @@ source is the <code>VERSION</code> file; Drone file is <code>service-docker.yaml
|
|||||||
<td><code>python-lib.yaml</code></td>
|
<td><code>python-lib.yaml</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>service / site</td>
|
<td>service</td>
|
||||||
<td><code>VERSION</code></td>
|
<td><code>VERSION</code></td>
|
||||||
<td><code>CHANGELOG.md</code></td>
|
<td><code>CHANGELOG.md</code></td>
|
||||||
<td><code>service-docker.yaml</code></td>
|
<td><code>service-docker.yaml</code></td>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -16,7 +16,7 @@
|
|||||||
"kind": "drone-service",
|
"kind": "drone-service",
|
||||||
"path": "service-docker.yaml",
|
"path": "service-docker.yaml",
|
||||||
"placeholder": "",
|
"placeholder": "",
|
||||||
"description": "Drone pipeline: build, push, and deploy a Docker service or site."
|
"description": "Drone pipeline: build, push, and deploy a Docker service."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
|
|||||||
"module": "repository_inventory",
|
"module": "repository_inventory",
|
||||||
"content": {
|
"content": {
|
||||||
"path": "repository_inventory",
|
"path": "repository_inventory",
|
||||||
"docstring": "# Repository Inventory\n\n## Summary\n\nThe canonical list of Aetos repositories covered by the CI and versioning\nstandards, with the artifact each one produces, its version source, and its\nDrone shape. Every entry maps to the contract in\n[11 – Versioning & Changelog](11_versioning_and_changelog.md) and the two CI\nfiles in [10 – Drone Pipelines](10_drone_pipelines.md).\n\nThe same data is published machine-readably as the `registry://repos` MCP\nresource (and the `list_repos` tool), sourced from `standards/src/repos.json`.\nPaths are relative to the `aetos/` workspace root.\n\n---\n\n## Packages (Type A)\n\nA wheel + sdist published to the private index. Version source is\n`pyproject.toml`; Drone file is `python-lib.yaml` (tag-triggered).\n\n| Repo | Path | Docs | MCP server |\n|------|------|------|------------|\n| `dagpipe` | `dagpipe` | lib, wiki | `dagpipe:8006` |\n| `doc-forge` | `doc-forge` | lib, wiki | `docforge:8001` |\n| `hexa` | `hexa` | lib, wiki | `hexa:8008` |\n| `mail-intake` | `mail-intake` | lib, wiki | `mail_intake:8002` |\n| `mongo-ops` | `mongo-ops` | lib, wiki | `mongo_ops:8007` |\n| `omniread` | `omniread` | lib, wiki | `omniread:8003` |\n| `openapi-first` | `openapi-first` | lib, wiki | `openapi_first:8004` |\n| `py-jwt` | `py-jwt` | lib, wiki | `jwtlib:8005` |\n\n---\n\n## Services & sites (Type B)\n\nA container image pushed to the registry and run on the Docker host. Version\nsource is the `VERSION` file; Drone file is `service-docker.yaml`.\n\n| Repo | Type | Path | Trigger | Docs |\n|------|------|------|---------|------|\n| `auth-server` | service | `auth-server` | tag | api, lib, wiki |\n| `docs` | site | `docs` | push | — |\n| `mcp` | site | `mcp` | push | — |\n\nThe **service** variant is tag-triggered and enforces `tag == VERSION`. The\n**site** variant is push-triggered, ships `:latest`, and has no tag guard.\n\n---\n\n## Required files per repo\n\n| Type | Version source | Changelog | Drone |\n|------|----------------|-----------|-------|\n| package | `pyproject.toml` | `CHANGELOG.md` | `python-lib.yaml` |\n| service / site | `VERSION` | `CHANGELOG.md` | `service-docker.yaml` |\n\nEvery covered repository must be able to satisfy `tag == version`:\nthe version file (never a commit id) is bumped in the release MR, the MR is\nmerged, and the tag is created on the merged commit.\n\n---\n\n## Reading this at runtime\n\n- Resource: `registry://repos`\n- Tool: `list_repos()`\n- Humans: this page (mirror of `standards/src/repos.json`).\n\n---\n\n## Related\n\n- [10 – Drone Pipelines](10_drone_pipelines.md) · [11 – Versioning & Changelog](11_versioning_and_changelog.md) · [13 – Library Registry](13_library_registry.md)\n",
|
"docstring": "# Repository Inventory\n\n## Summary\n\nThe canonical list of Aetos repositories covered by the CI and versioning\nstandards, with the artifact each one produces, its version source, and its\nDrone shape. Every entry maps to the contract in\n[11 – Versioning & Changelog](11_versioning_and_changelog.md) and the two CI\nfiles in [10 – Drone Pipelines](10_drone_pipelines.md).\n\nThe same data is published machine-readably as the `registry://repos` MCP\nresource (and the `list_repos` tool), sourced from `standards/src/repos.json`.\nPaths are relative to the `aetos/` workspace root.\n\n---\n\n## Packages (Type A)\n\nA wheel + sdist published to the private index. Version source is\n`pyproject.toml`; Drone file is `python-lib.yaml` (tag-triggered).\n\n| Repo | Path | Docs | MCP server |\n|------|------|------|------------|\n| `dagpipe` | `dagpipe` | lib, wiki | `dagpipe:8006` |\n| `doc-forge` | `doc-forge` | lib, wiki | `docforge:8001` |\n| `hexa` | `hexa` | lib, wiki | `hexa:8008` |\n| `mail-intake` | `mail-intake` | lib, wiki | `mail_intake:8002` |\n| `mongo-ops` | `mongo-ops` | lib, wiki | `mongo_ops:8007` |\n| `omniread` | `omniread` | lib, wiki | `omniread:8003` |\n| `openapi-first` | `openapi-first` | lib, wiki | `openapi_first:8004` |\n| `py-jwt` | `py-jwt` | lib, wiki | `jwtlib:8005` |\n\n---\n\n## Services (Type B)\n\nA container image pushed to the registry and run on the Docker host. Version\nsource is the `VERSION` file; Drone file is `service-docker.yaml`.\n\n| Repo | Path | Trigger | Docs |\n|------|------|---------|------|\n| `auth-server` | `auth-server` | tag | api, lib, wiki |\n\nTag-triggered and enforces `tag == VERSION`.\n\n---\n\n## Required files per repo\n\n| Type | Version source | Changelog | Drone |\n|------|----------------|-----------|-------|\n| package | `pyproject.toml` | `CHANGELOG.md` | `python-lib.yaml` |\n| service | `VERSION` | `CHANGELOG.md` | `service-docker.yaml` |\n\nEvery covered repository must be able to satisfy `tag == version`:\nthe version file (never a commit id) is bumped in the release MR, the MR is\nmerged, and the tag is created on the merged commit.\n\n---\n\n## Reading this at runtime\n\n- Resource: `registry://repos`\n- Tool: `list_repos()`\n- Humans: this page (mirror of `standards/src/repos.json`).\n\n---\n\n## Related\n\n- [10 – Drone Pipelines](10_drone_pipelines.md) · [11 – Versioning & Changelog](11_versioning_and_changelog.md) · [13 – Library Registry](13_library_registry.md)\n",
|
||||||
"objects": {}
|
"objects": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
|
|||||||
"module": "wiki_development_releasing",
|
"module": "wiki_development_releasing",
|
||||||
"content": {
|
"content": {
|
||||||
"path": "wiki_development_releasing",
|
"path": "wiki_development_releasing",
|
||||||
"docstring": "# Releasing & Versioning\n\nHow to cut a release of any Aetos repository. The rule lives in the reference\npage [Versioning & Changelog](../lib/11_versioning_and_changelog/); this is the\nrunbook you follow.\n\nEvery repository is one of two Drone shapes — a **PyPI package** or a **Docker\ndeployment** ([Drone Pipelines](../lib/10_drone_pipelines/)). Both release the\nsame way: the version bump and changelog promotion go **in the merge request**,\nand the tag is created on the merged commit.\n\n---\n\n## 🧾 Before you start\n\n- You have an open **merge request** holding the change, and `main` is green\n (the Drone quality gate passed on the last run).\n- You know whether this is a **breaking**, **feature**, or **fix** release.\n- The version file exists:\n - package → `version` in `pyproject.toml`\n - service / site → `VERSION` at the repo root\n\n---\n\n## 🚦 Pick the next version\n\nThe version file and the tag must be identical. For `0.y.z`, `0.y` is the major\nline (see the reference page); once at `1.0.0`, normal SemVer applies.\n\n| Change | `0.y.z` → | `1.0.0+` → |\n|--------|-----------|------------|\n| Breaking | `0.(y+1).0` | `(x+1).0.0` |\n| Feature | `0.y.(z+1)` | `x.(y+1).0` |\n| Fix | `0.y.(z+1)` | `x.y.(z+1)` |\n\n---\n\n## 🌿 Release in the MR\n\nThe version is known while the change is still a merge request, so the bump\nrides along with it. Do both edits **in the MR**:\n\n- **Promote the changelog** in `CHANGELOG.md`: move the `[Unreleased]` content\n under a new `## [x.y.z] - YYYY-MM-DD` heading, leave a fresh empty\n `[Unreleased]` on top, and add the compare links — naming the **version/tag**,\n never a commit id:\n\n ```markdown\n [Unreleased]: https://git.aetoskia.com/aetos/<repo>/compare/x.y.z...HEAD\n [x.y.z]: https://git.aetoskia.com/aetos/<repo>/compare/x.y.(z-1)...x.y.z\n ```\n\n- **Bump the version file** to the intended release version: `version = \"x.y.z\"`\n in `pyproject.toml` (packages) or the `VERSION` file (services/sites).\n\n---\n\n## 🏷️ Tag the merged commit\n\n1. **Merge or squash** the MR into `main`.\n2. **Create the tag** on the resulting commit: `git tag x.y.z` (lightweight).\n3. **Push the tag** (`git push origin x.y.z`) — Drone builds the release.\n\n### 🤖 Agent-assisted tagging\n\nThe agent does not author or merge the MR and never tags before the merge. After\nyou confirm the MR is merged/squashed and tell it to proceed, the agent:\n\n1. Waits for that explicit go-ahead.\n2. Finds the commit that introduced the bump:\n - package:\n `git log -S 'version = \"x.y.z\"' -n 1 --format='%H %s' -- pyproject.toml`\n - service / site:\n `git log -S 'x.y.z' -n 1 --format='%H %s' -- VERSION`\n3. Verifies that commit's version file equals `x.y.z`, then tags **only** that\n commit: `git tag x.y.z <sha>`.\n4. Does **not** push — it asks you to run `git push origin x.y.z`.\n\n---\n\n## 🤖 What Drone does\n\n- **Type A (package):** asserts `tag == pyproject version`, then skips if that\n version already exists on the index, runs the quality gate, builds, and\n uploads the wheel + sdist.\n- **Type B (service):** asserts `tag == VERSION`, resolves the tag, skips if the\n image already exists, runs the quality gate, builds, pushes, and restarts the\n container.\n- **Type B (site):** triggered on push, ships `:latest` and has no tag guard.\n\nIf the guard fails, nothing is published — fix the version file, amend the\ncommit, and move the tag **only** if it was never pushed.\n\n---\n\n## 🛟 Backfill / recovery\n\nIf an older tag never published (a past version-file mismatch, an index\noutage):\n\n- **Package:** run the `backfill-pypi-from-tags` pipeline (custom event). It\n checks out every tag, verifies `tag == pyproject version` at that commit,\n and uploads the ones missing from the index. Already-fixed history is never\n rewritten.\n- **Service:** rebuild the image for the missing tag with a fresh commit and a\n new patch version; do not move a published tag.\n\n---\n\n## 🔁 If a release is wrong\n\n- Never reuse or force-move a pushed tag.\n- Fix forward: bump to the next patch, promote the changelog, tag again.\n\n---\n\n## Related\n\n- [Testing & Quality Gates](01_testing.md) · [Versioning & Changelog](../lib/11_versioning_and_changelog/) · [Drone Pipelines](../lib/10_drone_pipelines/)\n",
|
"docstring": "# Releasing & Versioning\n\nHow to cut a release of any Aetos repository. The rule lives in the reference\npage [Versioning & Changelog](../lib/11_versioning_and_changelog/); this is the\nrunbook you follow.\n\nEvery repository is one of two Drone shapes — a **PyPI package** or a **Docker\ndeployment** ([Drone Pipelines](../lib/10_drone_pipelines/)). Both release the\nsame way: the version bump and changelog promotion go **in the merge request**,\nand the tag is created on the merged commit.\n\n---\n\n## 🧾 Before you start\n\n- You have an open **merge request** holding the change, and `main` is green\n (the Drone quality gate passed on the last run).\n- You know whether this is a **breaking**, **feature**, or **fix** release.\n- The version file exists:\n - package → `version` in `pyproject.toml`\n - service → `VERSION` at the repo root\n\n---\n\n## 🚦 Pick the next version\n\nThe version file and the tag must be identical. For `0.y.z`, `0.y` is the major\nline (see the reference page); once at `1.0.0`, normal SemVer applies.\n\n| Change | `0.y.z` → | `1.0.0+` → |\n|--------|-----------|------------|\n| Breaking | `0.(y+1).0` | `(x+1).0.0` |\n| Feature | `0.y.(z+1)` | `x.(y+1).0` |\n| Fix | `0.y.(z+1)` | `x.y.(z+1)` |\n\n---\n\n## 🌿 Release in the MR\n\nThe version is known while the change is still a merge request, so the bump\nrides along with it. Do all edits **in the MR**:\n\n- **Reconcile the changelog first.** `[Unreleased]` is a **draft, not the source\n of truth**: diff the change against the last tag\n (`git log <last-tag>..HEAD`, `git diff --stat <last-tag>..HEAD`) and add an\n entry for every user-facing change that is missing. **Always show the proposed\n additions to the user and wait for approval** before promoting — ask rather\n than guess when an entry is ambiguous.\n\n- **Promote the changelog** in `CHANGELOG.md`: move the (now reconciled)\n `[Unreleased]` content under a new `## [x.y.z] - YYYY-MM-DD` heading, leave a\n fresh empty `[Unreleased]` on top, and add the compare links — naming the\n **version/tag**, never a commit id:\n\n ```markdown\n [Unreleased]: https://git.aetoskia.com/aetos/<repo>/compare/x.y.z...HEAD\n [x.y.z]: https://git.aetoskia.com/aetos/<repo>/compare/x.y.(z-1)...x.y.z\n ```\n\n- **Bump the version file** to the intended release version: `version = \"x.y.z\"`\n in `pyproject.toml` (packages) or the `VERSION` file (services).\n\n---\n\n## 🏷️ Tag the merged commit\n\n1. **Merge or squash** the MR into `main`.\n2. **Create the tag** on the resulting commit: `git tag x.y.z` (lightweight).\n3. **Push the tag** (`git push origin x.y.z`) — Drone builds the release.\n\n### 🤖 Agent-assisted tagging\n\nThe agent does not author or merge the MR and never tags before the merge. After\nyou confirm the MR is merged/squashed and tell it to proceed, the agent:\n\n1. Waits for that explicit go-ahead.\n2. Finds the commit that introduced the bump:\n - package:\n `git log -S 'version = \"x.y.z\"' -n 1 --format='%H %s' -- pyproject.toml`\n - service:\n `git log -S 'x.y.z' -n 1 --format='%H %s' -- VERSION`\n3. Verifies that commit's version file equals `x.y.z`, then tags **only** that\n commit: `git tag x.y.z <sha>`.\n4. Does **not** push — it asks you to run `git push origin x.y.z`.\n\n---\n\n## 🤖 What Drone does\n\n- **Type A (package):** asserts `tag == pyproject version`, then skips if that\n version already exists on the index, runs the quality gate, builds, and\n uploads the wheel + sdist.\n- **Type B (service):** asserts `tag == VERSION`, resolves the tag, skips if the\n image already exists, runs the quality gate, builds, pushes, and restarts the\n container.\n\nIf the guard fails, nothing is published — fix the version file, amend the\ncommit, and move the tag **only** if it was never pushed.\n\n---\n\n## 🛟 Backfill / recovery\n\nIf an older tag never published (a past version-file mismatch, an index\noutage):\n\n- **Package:** run the `backfill-pypi-from-tags` pipeline (custom event). It\n checks out every tag, verifies `tag == pyproject version` at that commit,\n and uploads the ones missing from the index. Already-fixed history is never\n rewritten.\n- **Service:** rebuild the image for the missing tag with a fresh commit and a\n new patch version; do not move a published tag.\n\n---\n\n## 🔁 If a release is wrong\n\n- Never reuse or force-move a pushed tag.\n- Fix forward: bump to the next patch, promote the changelog, tag again.\n\n---\n\n## Related\n\n- [Testing & Quality Gates](01_testing.md) · [Versioning & Changelog](../lib/11_versioning_and_changelog/) · [Drone Pipelines](../lib/10_drone_pipelines/)\n",
|
||||||
"objects": {}
|
"objects": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
"kind": "version-file",
|
"kind": "version-file",
|
||||||
"path": "VERSION",
|
"path": "VERSION",
|
||||||
"placeholder": "",
|
"placeholder": "",
|
||||||
"description": "Single-line SemVer source of truth for services and sites."
|
"description": "Single-line SemVer source of truth for services."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -144,26 +144,6 @@
|
|||||||
"wiki"
|
"wiki"
|
||||||
],
|
],
|
||||||
"mcp": null
|
"mcp": null
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "docs",
|
|
||||||
"type": "site",
|
|
||||||
"path": "docs",
|
|
||||||
"version_source": "VERSION",
|
|
||||||
"drone": "service-docker.yaml",
|
|
||||||
"trigger": "push",
|
|
||||||
"docs": [],
|
|
||||||
"mcp": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mcp",
|
|
||||||
"type": "site",
|
|
||||||
"path": "mcp",
|
|
||||||
"version_source": "VERSION",
|
|
||||||
"drone": "service-docker.yaml",
|
|
||||||
"trigger": "push",
|
|
||||||
"docs": [],
|
|
||||||
"mcp": null
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,13 @@ All notable changes to this project are documented here.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Keep [Unreleased] current as you land changes. On release, reconcile it against
|
||||||
|
the change (git log / git diff since the last tag), add any missing user-facing
|
||||||
|
entries, and get the user's approval before promoting it into a version heading.
|
||||||
|
See standards: Versioning & Changelog (registry://release).
|
||||||
|
-->
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -1,29 +1,25 @@
|
|||||||
# Drone standard — Type B: Docker deployment
|
# Drone standard — Type B: Docker deployment
|
||||||
#
|
#
|
||||||
# Canonical pipelines for services that build an image, push it to the private
|
# Canonical pipeline for a service that builds an image, pushes it to the
|
||||||
# registry, and run it on the Docker host (auth-server, docs, mcp).
|
# private registry, and runs it on the Docker host (auth-server).
|
||||||
#
|
#
|
||||||
# Two variants ship together:
|
# build-and-deploy-service — fetch the git tag, fail unless it equals the
|
||||||
# build-and-deploy-service — a long-running API/worker service: fetch the git
|
# repo-root VERSION file, skip if the image already
|
||||||
# tag, skip if the image already exists, run the
|
# exists, run the quality gate, build, push, and
|
||||||
# quality gate, build, push, restart the container.
|
# restart the container.
|
||||||
# build-and-deploy-site — a static/HTTP site: build, push, restart, on
|
|
||||||
# every push (no tag, no quality gate).
|
|
||||||
#
|
#
|
||||||
# Secrets (Drone): PIP_REPO_URL, PIP_USERNAME, PIP_PASSWORD (service only),
|
# Secrets (Drone): PIP_REPO_URL, PIP_USERNAME, PIP_PASSWORD, REGISTRY_HOST,
|
||||||
# REGISTRY_HOST, REGISTRY_USER, REGISTRY_PASS, plus the service's runtime env
|
# REGISTRY_USER, REGISTRY_PASS, plus the service's runtime env (for example
|
||||||
# (for example MONGO_* / JWT_SECRET).
|
# MONGO_* / JWT_SECRET).
|
||||||
#
|
#
|
||||||
# Placeholders to set per repo:
|
# Placeholders to set per repo:
|
||||||
# <app> image / container / network name (e.g. auth-server).
|
# <app> image / container / network name (e.g. auth-server).
|
||||||
# aetos/<app> image repository on the private registry.
|
# aetos/<app> image repository on the private registry.
|
||||||
# <host-port> published host port; <container-port> the in-image port.
|
# <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.
|
# <env-var> runtime environment variable consumed by the service.
|
||||||
#
|
#
|
||||||
# Version guard: the tag-triggered service variant fails unless the git tag
|
# Version guard: the tag-triggered pipeline fails unless the git tag equals the
|
||||||
# equals the repo-root VERSION file (see the versioning standard). The
|
# repo-root VERSION file (see the versioning standard).
|
||||||
# push-triggered site variant ships :latest only and has no version to guard.
|
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
@@ -203,75 +199,3 @@ trigger:
|
|||||||
event:
|
event:
|
||||||
- tag
|
- tag
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: build-and-deploy-site
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
workspace:
|
|
||||||
path: /drone/src
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: dockersock
|
|
||||||
host:
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: build-image
|
|
||||||
image: docker:24
|
|
||||||
volumes:
|
|
||||||
- name: dockersock
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
commands:
|
|
||||||
- echo "Building Docker image for aetos/<site>..."
|
|
||||||
- docker build -t aetos/<site>:latest .
|
|
||||||
|
|
||||||
- name: push-image
|
|
||||||
image: docker:24
|
|
||||||
environment:
|
|
||||||
REGISTRY_HOST:
|
|
||||||
from_secret: REGISTRY_HOST
|
|
||||||
REGISTRY_USER:
|
|
||||||
from_secret: REGISTRY_USER
|
|
||||||
REGISTRY_PASS:
|
|
||||||
from_secret: REGISTRY_PASS
|
|
||||||
volumes:
|
|
||||||
- name: dockersock
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
commands:
|
|
||||||
- echo "Logging into registry $REGISTRY_HOST ..."
|
|
||||||
- echo "$REGISTRY_PASS" | docker login $REGISTRY_HOST -u "$REGISTRY_USER" --password-stdin
|
|
||||||
- docker tag aetos/<site>:latest $REGISTRY_HOST/aetos/<site>:latest
|
|
||||||
- echo "Pushing image to $REGISTRY_HOST..."
|
|
||||||
- docker push $REGISTRY_HOST/aetos/<site>:latest
|
|
||||||
|
|
||||||
- name: stop-old
|
|
||||||
image: docker:24
|
|
||||||
volumes:
|
|
||||||
- name: dockersock
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
commands:
|
|
||||||
- echo "Stopping old container..."
|
|
||||||
- docker rm -f <site> || true
|
|
||||||
|
|
||||||
- name: run-container
|
|
||||||
image: docker:24
|
|
||||||
volumes:
|
|
||||||
- name: dockersock
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
commands:
|
|
||||||
- echo "Starting aetos/<site>:latest ..."
|
|
||||||
- |
|
|
||||||
docker run -d \
|
|
||||||
--name <site> \
|
|
||||||
-p <host-port>:<container-port> \
|
|
||||||
--restart always \
|
|
||||||
aetos/<site>:latest
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|||||||
@@ -20,9 +20,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Security
|
### Security
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
In the release merge request, promote the entries above into a new heading and
|
In the release merge request, first reconcile the entries above against the
|
||||||
leave a fresh, empty [Unreleased] section. Name the version/tag, never a commit
|
change: diff it against the last tag (`git log <last-tag>..HEAD`,
|
||||||
id:
|
`git diff --stat <last-tag>..HEAD`) and add any missing user-facing entries.
|
||||||
|
`[Unreleased]` is a draft — always show the additions to the user for approval
|
||||||
|
before promoting. Then promote the entries above into a new heading and leave a
|
||||||
|
fresh, empty [Unreleased] section. Name the version/tag, never a commit id:
|
||||||
|
|
||||||
## [0.1.0] - YYYY-MM-DD
|
## [0.1.0] - YYYY-MM-DD
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ chosen by what the repository produces:
|
|||||||
| Type | File | Produces | Repos |
|
| 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` |
|
| **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
|
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
|
`/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`)
|
## Type B — Docker deployment (`service-docker.yaml`)
|
||||||
|
|
||||||
For services and sites that ship a container image. The file contains **two
|
For a service that ships a container image — a long-running API/worker, e.g.
|
||||||
pipelines**, each mirroring a repo family:
|
`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
|
- `fetch-tags` — resolve the latest tag into `LATEST_TAG.txt`; fail if there
|
||||||
API/worker service, e.g. `auth-server`):
|
are no tags.
|
||||||
- `fetch-tags` — resolve the latest tag into `LATEST_TAG.txt`; fail if there
|
- `check-version` — **guard**: fail unless `$DRONE_TAG` equals the repo-root
|
||||||
are no tags.
|
`VERSION` file (see [11 – Versioning & Changelog](11_versioning_and_changelog.md)).
|
||||||
- `check-version` — **guard**: fail unless `$DRONE_TAG` equals the repo-root
|
- `check-remote-image` — if `aetos/<app>:<tag>` already exists locally, exit
|
||||||
`VERSION` file (see [11 – Versioning & Changelog](11_versioning_and_changelog.md)).
|
`78` and skip the build.
|
||||||
- `check-remote-image` — if `aetos/<app>:<tag>` already exists locally, exit
|
- `quality-gate` — install from `requirements.txt` and run
|
||||||
`78` and skip the build.
|
`black` / `ruff` / `mypy` / `pytest`.
|
||||||
- `quality-gate` — install from `requirements.txt` and run
|
- `build-image` — `docker build` with the private-index build args, tagged
|
||||||
`black` / `ruff` / `mypy` / `pytest`.
|
`aetos/<app>:<tag>` and `:latest`.
|
||||||
- `build-image` — `docker build` with the private-index build args, tagged
|
- `push-image` — login, re-tag with `$REGISTRY_HOST`, push both tags.
|
||||||
`aetos/<app>:<tag>` and `:latest`.
|
- `stop-old` — `docker rm -f <app>`.
|
||||||
- `push-image` — login, re-tag with `$REGISTRY_HOST`, push both tags.
|
- `run-container` — `docker run -d --name <app> -p <host>:<container>
|
||||||
- `stop-old` — `docker rm -f <app>`.
|
--restart always` with the service's `-e` environment.
|
||||||
- `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).
|
|
||||||
|
|
||||||
Secrets: `PIP_REPO_URL`, `PIP_USERNAME`, `PIP_PASSWORD` (service build args),
|
Secrets: `PIP_REPO_URL`, `PIP_USERNAME`, `PIP_PASSWORD` (service build args),
|
||||||
`REGISTRY_HOST`, `REGISTRY_USER`, `REGISTRY_PASS`, plus the service runtime env
|
`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` |
|
| `<app>` | service image / container name | `auth-server` |
|
||||||
| `aetos/<app>` | image repository on the registry | `aetos/auth-server` |
|
| `aetos/<app>` | image repository on the registry | `aetos/auth-server` |
|
||||||
| `<host-port>` / `<container-port>` | published : in-image port | `9003:8000` |
|
| `<host-port>` / `<container-port>` | published : in-image port | `9003:8000` |
|
||||||
| `<site>` | site image / container name | `docs` |
|
|
||||||
| `<ENV_VAR>` | runtime environment variable | `MONGO_HOST` |
|
| `<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
|
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).
|
the tag. See [11 – Versioning & Changelog](11_versioning_and_changelog.md).
|
||||||
- Do not rename the pipelines; tooling and runbooks key off
|
- Do not rename the pipelines; tooling and runbooks key off
|
||||||
`build-and-publish-pypi`, `build-and-deploy-service`, and
|
`build-and-publish-pypi` and `build-and-deploy-service`.
|
||||||
`build-and-deploy-site`.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Summary
|
## 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
|
[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.
|
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
|
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 |
|
| Artifact | Source of truth | Notes |
|
||||||
|----------|-----------------|-------|
|
|----------|-----------------|-------|
|
||||||
| Library (PyPI package) | `version` in `pyproject.toml` | `check-version` also reads it to skip duplicates |
|
| 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:
|
Rules:
|
||||||
|
|
||||||
@@ -84,9 +84,28 @@ Conventions:
|
|||||||
- Categories: **Added**, **Changed**, **Deprecated**, **Removed**, **Fixed**,
|
- Categories: **Added**, **Changed**, **Deprecated**, **Removed**, **Fixed**,
|
||||||
**Security**. Omit empty categories.
|
**Security**. Omit empty categories.
|
||||||
- Security fixes get their own entry even if the change is otherwise a bug fix.
|
- 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
|
- The `[Unreleased]` heading is never renamed; copy its content into the new
|
||||||
version heading and clear it.
|
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)
|
## Tagging strategy (MR-based)
|
||||||
@@ -94,8 +113,10 @@ Conventions:
|
|||||||
The release version is known while the change is still a merge request, so the
|
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:
|
bump travels with the change and only the tag is added afterwards:
|
||||||
|
|
||||||
1. **In the MR** — set the intended release version: `version` in
|
1. **In the MR** — reconcile `[Unreleased]` against the change (see
|
||||||
`pyproject.toml` (or the `VERSION` file) to `x.y.z`, and promote the
|
[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
|
changelog (below). Changelog entries and link references name the
|
||||||
**version/tag**, never a commit id.
|
**version/tag**, never a commit id.
|
||||||
2. **Merge or squash** the MR into `main`.
|
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
|
## Release checklist
|
||||||
|
|
||||||
1. Confirm the quality gate is green and `main` is up to date.
|
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
|
2. In the MR: reconcile `[Unreleased]` against the change being released —
|
||||||
the target date (`## [x.y.z] - YYYY-MM-DD`); restore an empty `[Unreleased]`
|
`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.
|
on top.
|
||||||
3. In the MR: add the two compare-link references at the bottom of the
|
3. In the MR: add the two compare-link references at the bottom of the
|
||||||
changelog, naming the version/tag (never a commit id).
|
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
|
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`.
|
source is the `VERSION` file; Drone file is `service-docker.yaml`.
|
||||||
|
|
||||||
| Repo | Type | Path | Trigger | Docs |
|
| Repo | Path | Trigger | Docs |
|
||||||
|------|------|------|---------|------|
|
|------|------|---------|------|
|
||||||
| `auth-server` | service | `auth-server` | tag | api, lib, wiki |
|
| `auth-server` | `auth-server` | tag | api, lib, wiki |
|
||||||
| `docs` | site | `docs` | push | — |
|
|
||||||
| `mcp` | site | `mcp` | push | — |
|
|
||||||
|
|
||||||
The **service** variant is tag-triggered and enforces `tag == VERSION`. The
|
Tag-triggered and enforces `tag == VERSION`.
|
||||||
**site** variant is push-triggered, ships `:latest`, and has no tag guard.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -53,7 +50,7 @@ The **service** variant is tag-triggered and enforces `tag == VERSION`. The
|
|||||||
| Type | Version source | Changelog | Drone |
|
| Type | Version source | Changelog | Drone |
|
||||||
|------|----------------|-----------|-------|
|
|------|----------------|-----------|-------|
|
||||||
| package | `pyproject.toml` | `CHANGELOG.md` | `python-lib.yaml` |
|
| 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`:
|
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
|
the version file (never a commit id) is bumped in the release MR, the MR is
|
||||||
|
|||||||
@@ -92,26 +92,6 @@
|
|||||||
"trigger": "tag",
|
"trigger": "tag",
|
||||||
"docs": ["api", "lib", "wiki"],
|
"docs": ["api", "lib", "wiki"],
|
||||||
"mcp": null
|
"mcp": null
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "docs",
|
|
||||||
"type": "site",
|
|
||||||
"path": "docs",
|
|
||||||
"version_source": "VERSION",
|
|
||||||
"drone": "service-docker.yaml",
|
|
||||||
"trigger": "push",
|
|
||||||
"docs": [],
|
|
||||||
"mcp": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mcp",
|
|
||||||
"type": "site",
|
|
||||||
"path": "mcp",
|
|
||||||
"version_source": "VERSION",
|
|
||||||
"drone": "service-docker.yaml",
|
|
||||||
"trigger": "push",
|
|
||||||
"docs": [],
|
|
||||||
"mcp": null
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ All notable changes to this project are documented here.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Keep [Unreleased] current as you land changes. On release, reconcile it against
|
||||||
|
the change (git log / git diff since the last tag), add any missing user-facing
|
||||||
|
entries, and get the user's approval before promoting it into a version heading.
|
||||||
|
See standards: Versioning & Changelog (registry://release).
|
||||||
|
-->
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -1,29 +1,25 @@
|
|||||||
# Drone standard — Type B: Docker deployment
|
# Drone standard — Type B: Docker deployment
|
||||||
#
|
#
|
||||||
# Canonical pipelines for services that build an image, push it to the private
|
# Canonical pipeline for a service that builds an image, pushes it to the
|
||||||
# registry, and run it on the Docker host (auth-server, docs, mcp).
|
# private registry, and runs it on the Docker host (auth-server).
|
||||||
#
|
#
|
||||||
# Two variants ship together:
|
# build-and-deploy-service — fetch the git tag, fail unless it equals the
|
||||||
# build-and-deploy-service — a long-running API/worker service: fetch the git
|
# repo-root VERSION file, skip if the image already
|
||||||
# tag, skip if the image already exists, run the
|
# exists, run the quality gate, build, push, and
|
||||||
# quality gate, build, push, restart the container.
|
# restart the container.
|
||||||
# build-and-deploy-site — a static/HTTP site: build, push, restart, on
|
|
||||||
# every push (no tag, no quality gate).
|
|
||||||
#
|
#
|
||||||
# Secrets (Drone): PIP_REPO_URL, PIP_USERNAME, PIP_PASSWORD (service only),
|
# Secrets (Drone): PIP_REPO_URL, PIP_USERNAME, PIP_PASSWORD, REGISTRY_HOST,
|
||||||
# REGISTRY_HOST, REGISTRY_USER, REGISTRY_PASS, plus the service's runtime env
|
# REGISTRY_USER, REGISTRY_PASS, plus the service's runtime env (for example
|
||||||
# (for example MONGO_* / JWT_SECRET).
|
# MONGO_* / JWT_SECRET).
|
||||||
#
|
#
|
||||||
# Placeholders to set per repo:
|
# Placeholders to set per repo:
|
||||||
# <app> image / container / network name (e.g. auth-server).
|
# <app> image / container / network name (e.g. auth-server).
|
||||||
# aetos/<app> image repository on the private registry.
|
# aetos/<app> image repository on the private registry.
|
||||||
# <host-port> published host port; <container-port> the in-image port.
|
# <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.
|
# <env-var> runtime environment variable consumed by the service.
|
||||||
#
|
#
|
||||||
# Version guard: the tag-triggered service variant fails unless the git tag
|
# Version guard: the tag-triggered pipeline fails unless the git tag equals the
|
||||||
# equals the repo-root VERSION file (see the versioning standard). The
|
# repo-root VERSION file (see the versioning standard).
|
||||||
# push-triggered site variant ships :latest only and has no version to guard.
|
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
@@ -203,75 +199,3 @@ trigger:
|
|||||||
event:
|
event:
|
||||||
- tag
|
- tag
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: build-and-deploy-site
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
workspace:
|
|
||||||
path: /drone/src
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: dockersock
|
|
||||||
host:
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: build-image
|
|
||||||
image: docker:24
|
|
||||||
volumes:
|
|
||||||
- name: dockersock
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
commands:
|
|
||||||
- echo "Building Docker image for aetos/<site>..."
|
|
||||||
- docker build -t aetos/<site>:latest .
|
|
||||||
|
|
||||||
- name: push-image
|
|
||||||
image: docker:24
|
|
||||||
environment:
|
|
||||||
REGISTRY_HOST:
|
|
||||||
from_secret: REGISTRY_HOST
|
|
||||||
REGISTRY_USER:
|
|
||||||
from_secret: REGISTRY_USER
|
|
||||||
REGISTRY_PASS:
|
|
||||||
from_secret: REGISTRY_PASS
|
|
||||||
volumes:
|
|
||||||
- name: dockersock
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
commands:
|
|
||||||
- echo "Logging into registry $REGISTRY_HOST ..."
|
|
||||||
- echo "$REGISTRY_PASS" | docker login $REGISTRY_HOST -u "$REGISTRY_USER" --password-stdin
|
|
||||||
- docker tag aetos/<site>:latest $REGISTRY_HOST/aetos/<site>:latest
|
|
||||||
- echo "Pushing image to $REGISTRY_HOST..."
|
|
||||||
- docker push $REGISTRY_HOST/aetos/<site>:latest
|
|
||||||
|
|
||||||
- name: stop-old
|
|
||||||
image: docker:24
|
|
||||||
volumes:
|
|
||||||
- name: dockersock
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
commands:
|
|
||||||
- echo "Stopping old container..."
|
|
||||||
- docker rm -f <site> || true
|
|
||||||
|
|
||||||
- name: run-container
|
|
||||||
image: docker:24
|
|
||||||
volumes:
|
|
||||||
- name: dockersock
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
commands:
|
|
||||||
- echo "Starting aetos/<site>:latest ..."
|
|
||||||
- |
|
|
||||||
docker run -d \
|
|
||||||
--name <site> \
|
|
||||||
-p <host-port>:<container-port> \
|
|
||||||
--restart always \
|
|
||||||
aetos/<site>:latest
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|||||||
@@ -20,9 +20,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Security
|
### Security
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
In the release merge request, promote the entries above into a new heading and
|
In the release merge request, first reconcile the entries above against the
|
||||||
leave a fresh, empty [Unreleased] section. Name the version/tag, never a commit
|
change: diff it against the last tag (`git log <last-tag>..HEAD`,
|
||||||
id:
|
`git diff --stat <last-tag>..HEAD`) and add any missing user-facing entries.
|
||||||
|
`[Unreleased]` is a draft — always show the additions to the user for approval
|
||||||
|
before promoting. Then promote the entries above into a new heading and leave a
|
||||||
|
fresh, empty [Unreleased] section. Name the version/tag, never a commit id:
|
||||||
|
|
||||||
## [0.1.0] - YYYY-MM-DD
|
## [0.1.0] - YYYY-MM-DD
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ and the tag is created on the merged commit.
|
|||||||
- You know whether this is a **breaking**, **feature**, or **fix** release.
|
- You know whether this is a **breaking**, **feature**, or **fix** release.
|
||||||
- The version file exists:
|
- The version file exists:
|
||||||
- package → `version` in `pyproject.toml`
|
- package → `version` in `pyproject.toml`
|
||||||
- service / site → `VERSION` at the repo root
|
- service → `VERSION` at the repo root
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -38,12 +38,19 @@ line (see the reference page); once at `1.0.0`, normal SemVer applies.
|
|||||||
## 🌿 Release in the MR
|
## 🌿 Release in the MR
|
||||||
|
|
||||||
The version is known while the change is still a merge request, so the bump
|
The version is known while the change is still a merge request, so the bump
|
||||||
rides along with it. Do both edits **in the MR**:
|
rides along with it. Do all edits **in the MR**:
|
||||||
|
|
||||||
- **Promote the changelog** in `CHANGELOG.md`: move the `[Unreleased]` content
|
- **Reconcile the changelog first.** `[Unreleased]` is a **draft, not the source
|
||||||
under a new `## [x.y.z] - YYYY-MM-DD` heading, leave a fresh empty
|
of truth**: diff the change against the last tag
|
||||||
`[Unreleased]` on top, and add the compare links — naming the **version/tag**,
|
(`git log <last-tag>..HEAD`, `git diff --stat <last-tag>..HEAD`) and add an
|
||||||
never a commit id:
|
entry for every user-facing change that is missing. **Always show the proposed
|
||||||
|
additions to the user and wait for approval** before promoting — ask rather
|
||||||
|
than guess when an entry is ambiguous.
|
||||||
|
|
||||||
|
- **Promote the changelog** in `CHANGELOG.md`: move the (now reconciled)
|
||||||
|
`[Unreleased]` content under a new `## [x.y.z] - YYYY-MM-DD` heading, leave a
|
||||||
|
fresh empty `[Unreleased]` on top, and add the compare links — naming the
|
||||||
|
**version/tag**, never a commit id:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
[Unreleased]: https://git.aetoskia.com/aetos/<repo>/compare/x.y.z...HEAD
|
[Unreleased]: https://git.aetoskia.com/aetos/<repo>/compare/x.y.z...HEAD
|
||||||
@@ -51,7 +58,7 @@ rides along with it. Do both edits **in the MR**:
|
|||||||
```
|
```
|
||||||
|
|
||||||
- **Bump the version file** to the intended release version: `version = "x.y.z"`
|
- **Bump the version file** to the intended release version: `version = "x.y.z"`
|
||||||
in `pyproject.toml` (packages) or the `VERSION` file (services/sites).
|
in `pyproject.toml` (packages) or the `VERSION` file (services).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -70,8 +77,8 @@ you confirm the MR is merged/squashed and tell it to proceed, the agent:
|
|||||||
2. Finds the commit that introduced the bump:
|
2. Finds the commit that introduced the bump:
|
||||||
- package:
|
- package:
|
||||||
`git log -S 'version = "x.y.z"' -n 1 --format='%H %s' -- pyproject.toml`
|
`git log -S 'version = "x.y.z"' -n 1 --format='%H %s' -- pyproject.toml`
|
||||||
- service / site:
|
- service:
|
||||||
`git log -S 'x.y.z' -n 1 --format='%H %s' -- VERSION`
|
`git log -S 'x.y.z' -n 1 --format='%H %s' -- VERSION`
|
||||||
3. Verifies that commit's version file equals `x.y.z`, then tags **only** that
|
3. Verifies that commit's version file equals `x.y.z`, then tags **only** that
|
||||||
commit: `git tag x.y.z <sha>`.
|
commit: `git tag x.y.z <sha>`.
|
||||||
4. Does **not** push — it asks you to run `git push origin x.y.z`.
|
4. Does **not** push — it asks you to run `git push origin x.y.z`.
|
||||||
@@ -86,7 +93,6 @@ you confirm the MR is merged/squashed and tell it to proceed, the agent:
|
|||||||
- **Type B (service):** asserts `tag == VERSION`, resolves the tag, skips if the
|
- **Type B (service):** asserts `tag == VERSION`, resolves the tag, skips if the
|
||||||
image already exists, runs the quality gate, builds, pushes, and restarts the
|
image already exists, runs the quality gate, builds, pushes, and restarts the
|
||||||
container.
|
container.
|
||||||
- **Type B (site):** triggered on push, ships `:latest` and has no tag guard.
|
|
||||||
|
|
||||||
If the guard fails, nothing is published — fix the version file, amend the
|
If the guard fails, nothing is published — fix the version file, amend the
|
||||||
commit, and move the tag **only** if it was never pushed.
|
commit, and move the tag **only** if it was never pushed.
|
||||||
|
|||||||
@@ -636,7 +636,7 @@ and the tag is created on the merged commit.</p>
|
|||||||
<li>You know whether this is a <strong>breaking</strong>, <strong>feature</strong>, or <strong>fix</strong> release.</li>
|
<li>You know whether this is a <strong>breaking</strong>, <strong>feature</strong>, or <strong>fix</strong> release.</li>
|
||||||
<li>The version file exists:</li>
|
<li>The version file exists:</li>
|
||||||
<li>package → <code>version</code> in <code>pyproject.toml</code></li>
|
<li>package → <code>version</code> in <code>pyproject.toml</code></li>
|
||||||
<li>service / site → <code>VERSION</code> at the repo root</li>
|
<li>service → <code>VERSION</code> at the repo root</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="pick-the-next-version">🚦 Pick the next version</h2>
|
<h2 id="pick-the-next-version">🚦 Pick the next version</h2>
|
||||||
@@ -671,12 +671,22 @@ line (see the reference page); once at <code>1.0.0</code>, normal SemVer applies
|
|||||||
<hr />
|
<hr />
|
||||||
<h2 id="release-in-the-mr">🌿 Release in the MR</h2>
|
<h2 id="release-in-the-mr">🌿 Release in the MR</h2>
|
||||||
<p>The version is known while the change is still a merge request, so the bump
|
<p>The version is known while the change is still a merge request, so the bump
|
||||||
rides along with it. Do both edits <strong>in the MR</strong>:</p>
|
rides along with it. Do all edits <strong>in the MR</strong>:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Promote the changelog</strong> in <code>CHANGELOG.md</code>: move the <code>[Unreleased]</code> content
|
<li>
|
||||||
under a new <code>## [x.y.z] - YYYY-MM-DD</code> heading, leave a fresh empty
|
<p><strong>Reconcile the changelog first.</strong> <code>[Unreleased]</code> is a <strong>draft, not the source
|
||||||
<code>[Unreleased]</code> on top, and add the compare links — naming the <strong>version/tag</strong>,
|
of truth</strong>: diff the change against the last tag
|
||||||
never a commit id:</li>
|
(<code>git log <last-tag>..HEAD</code>, <code>git diff --stat <last-tag>..HEAD</code>) and add an
|
||||||
|
entry for every user-facing change that is missing. <strong>Always show the proposed
|
||||||
|
additions to the user and wait for approval</strong> before promoting — ask rather
|
||||||
|
than guess when an entry is ambiguous.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><strong>Promote the changelog</strong> in <code>CHANGELOG.md</code>: move the (now reconciled)
|
||||||
|
<code>[Unreleased]</code> content under a new <code>## [x.y.z] - YYYY-MM-DD</code> heading, leave a
|
||||||
|
fresh empty <code>[Unreleased]</code> on top, and add the compare links — naming the
|
||||||
|
<strong>version/tag</strong>, never a commit id:</p>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="language-markdown highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-1">1</a></span>
|
<div class="language-markdown highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-1">1</a></span>
|
||||||
<span class="normal"><a href="#__codelineno-0-2">2</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1"></a>[<span class="nl">Unreleased</span>]: <span class="na">https://git.aetoskia.com/aetos/<repo>/compare/x.y.z...HEAD</span>
|
<span class="normal"><a href="#__codelineno-0-2">2</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1"></a>[<span class="nl">Unreleased</span>]: <span class="na">https://git.aetoskia.com/aetos/<repo>/compare/x.y.z...HEAD</span>
|
||||||
@@ -684,7 +694,7 @@ rides along with it. Do both edits <strong>in the MR</strong>:</p>
|
|||||||
</span></code></pre></div></td></tr></table></div>
|
</span></code></pre></div></td></tr></table></div>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Bump the version file</strong> to the intended release version: <code>version = "x.y.z"</code>
|
<li><strong>Bump the version file</strong> to the intended release version: <code>version = "x.y.z"</code>
|
||||||
in <code>pyproject.toml</code> (packages) or the <code>VERSION</code> file (services/sites).</li>
|
in <code>pyproject.toml</code> (packages) or the <code>VERSION</code> file (services).</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
<h2 id="tag-the-merged-commit">🏷️ Tag the merged commit</h2>
|
<h2 id="tag-the-merged-commit">🏷️ Tag the merged commit</h2>
|
||||||
@@ -701,8 +711,8 @@ you confirm the MR is merged/squashed and tell it to proceed, the agent:</p>
|
|||||||
<li>Finds the commit that introduced the bump:</li>
|
<li>Finds the commit that introduced the bump:</li>
|
||||||
<li>package:
|
<li>package:
|
||||||
<code>git log -S 'version = "x.y.z"' -n 1 --format='%H %s' -- pyproject.toml</code></li>
|
<code>git log -S 'version = "x.y.z"' -n 1 --format='%H %s' -- pyproject.toml</code></li>
|
||||||
<li>service / site:
|
<li>service:
|
||||||
<code>git log -S 'x.y.z' -n 1 --format='%H %s' -- VERSION</code></li>
|
<code>git log -S 'x.y.z' -n 1 --format='%H %s' -- VERSION</code></li>
|
||||||
<li>Verifies that commit's version file equals <code>x.y.z</code>, then tags <strong>only</strong> that
|
<li>Verifies that commit's version file equals <code>x.y.z</code>, then tags <strong>only</strong> that
|
||||||
commit: <code>git tag x.y.z <sha></code>.</li>
|
commit: <code>git tag x.y.z <sha></code>.</li>
|
||||||
<li>Does <strong>not</strong> push — it asks you to run <code>git push origin x.y.z</code>.</li>
|
<li>Does <strong>not</strong> push — it asks you to run <code>git push origin x.y.z</code>.</li>
|
||||||
@@ -716,7 +726,6 @@ you confirm the MR is merged/squashed and tell it to proceed, the agent:</p>
|
|||||||
<li><strong>Type B (service):</strong> asserts <code>tag == VERSION</code>, resolves the tag, skips if the
|
<li><strong>Type B (service):</strong> asserts <code>tag == VERSION</code>, resolves the tag, skips if the
|
||||||
image already exists, runs the quality gate, builds, pushes, and restarts the
|
image already exists, runs the quality gate, builds, pushes, and restarts the
|
||||||
container.</li>
|
container.</li>
|
||||||
<li><strong>Type B (site):</strong> triggered on push, ships <code>:latest</code> and has no tag guard.</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<p>If the guard fails, nothing is published — fix the version file, amend the
|
<p>If the guard fails, nothing is published — fix the version file, amend the
|
||||||
commit, and move the tag <strong>only</strong> if it was never pushed.</p>
|
commit, and move the tag <strong>only</strong> if it was never pushed.</p>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Reference in New Issue
Block a user