Skip to content

Commit 4892194

Browse files
committed
chore: align admina-core to 0.9.2 + CI version-alignment guard
1 parent 8c8db6b commit 4892194

6 files changed

Lines changed: 126 additions & 3 deletions

File tree

.github/workflows/ci.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ on:
2121
branches: [main]
2222

2323
jobs:
24+
# ── Version alignment ───────────────────────────────────────
25+
# Cross-manifest guard: pyproject (framework), __init__, core-rust
26+
# (pyproject + Cargo.toml + Cargo.lock) must all carry the same
27+
# version. Independent of release-time tag checks; catches drift
28+
# at PR time.
29+
versions:
30+
name: Version alignment
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
34+
- name: Check artefact versions are aligned
35+
run: python3 scripts/check-versions.py
36+
2437
# ── Python: lint ────────────────────────────────────────────
2538
lint:
2639
name: Python Lint

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ seeing a working `admina dev` after `pip install`.
4444

4545
- Funding link in `.github/FUNDING.yml` points to the dedicated sponsor
4646
landing page (`https://admina.org/sponsor/`).
47+
- **admina-core bumped to 0.9.2 (sync release)** — no Rust changes,
48+
but the crate / wheel / sdist versions now track admina-framework so
49+
the two artefacts always carry the same number on PyPI, crates.io,
50+
and ghcr.io. From this release on, every published artefact in the
51+
monorepo (admina-framework, admina-core, admina-proxy image,
52+
admina-dashboard image) ships with the same version. A new CI job
53+
(`scripts/check-versions.py`) blocks PRs that drift the manifests
54+
out of alignment.
4755

4856
---
4957

core-rust/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core-rust/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
[package]
1717
name = "admina_core"
18-
version = "0.9.1"
18+
version = "0.9.2"
1919
edition = "2021"
2020
description = "Admina governance engines — Rust core with Python bindings"
2121
license = "Apache-2.0"

core-rust/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ build-backend = "maturin"
1919

2020
[project]
2121
name = "admina-core"
22-
version = "0.9.1"
22+
version = "0.9.2"
2323
description = "Admina governance engines — Rust core with Python bindings (firewall, PII, loop breaker, hash chain)"
2424
readme = "README.md"
2525
requires-python = ">=3.11"

scripts/check-versions.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright © 2025–2026 Stefano Noferi & Admina contributors
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
"""Assert all admina artefact versions are aligned.
18+
19+
Run from the repository root. Exits non-zero (and prints a table) when
20+
any of the tracked manifests disagrees with the canonical version in
21+
``pyproject.toml``.
22+
23+
Tracked points of truth:
24+
1. pyproject.toml → admina-framework (canonical)
25+
2. admina/__init__.py → runtime __version__
26+
3. core-rust/pyproject.toml → admina-core (PyPI)
27+
4. core-rust/Cargo.toml → admina_core crate
28+
5. core-rust/Cargo.lock → resolved admina_core entry
29+
30+
Docker images and dashboard HTML derive their version dynamically from
31+
pyproject.toml at build time, so they need no separate check here.
32+
"""
33+
34+
from __future__ import annotations
35+
36+
import re
37+
import sys
38+
import tomllib
39+
from pathlib import Path
40+
41+
REPO = Path(__file__).resolve().parent.parent
42+
43+
44+
def _toml_version(path: Path, *, table: str = "project") -> str:
45+
data = tomllib.loads(path.read_text(encoding="utf-8"))
46+
if table == "project":
47+
return data["project"]["version"]
48+
if table == "package":
49+
return data["package"]["version"]
50+
raise ValueError(f"unknown table {table!r}")
51+
52+
53+
def _python_dunder_version(path: Path) -> str:
54+
m = re.search(r'__version__\s*=\s*"([^"]+)"', path.read_text(encoding="utf-8"))
55+
if not m:
56+
raise RuntimeError(f"__version__ not found in {path}")
57+
return m.group(1)
58+
59+
60+
def _cargo_lock_admina_core(path: Path) -> str:
61+
"""Pull the `version = "..."` line that follows `name = "admina_core"`."""
62+
text = path.read_text(encoding="utf-8")
63+
m = re.search(r'name = "admina_core"\s*\nversion = "([^"]+)"', text)
64+
if not m:
65+
raise RuntimeError("admina_core entry not found in Cargo.lock")
66+
return m.group(1)
67+
68+
69+
def main() -> int:
70+
versions: dict[str, str] = {
71+
"pyproject.toml": _toml_version(REPO / "pyproject.toml"),
72+
"admina/__init__.py": _python_dunder_version(REPO / "admina" / "__init__.py"),
73+
"core-rust/pyproject.toml": _toml_version(REPO / "core-rust" / "pyproject.toml"),
74+
"core-rust/Cargo.toml": _toml_version(REPO / "core-rust" / "Cargo.toml", table="package"),
75+
"core-rust/Cargo.lock": _cargo_lock_admina_core(REPO / "core-rust" / "Cargo.lock"),
76+
}
77+
78+
canonical = versions["pyproject.toml"]
79+
width = max(len(p) for p in versions)
80+
print(f"Canonical (pyproject.toml): {canonical}\n")
81+
print(f"{'File':<{width}} Version Status")
82+
print(f"{'-' * width} {'-' * 9} {'-' * 6}")
83+
drift = []
84+
for path, ver in versions.items():
85+
ok = ver == canonical
86+
status = "OK" if ok else "DRIFT"
87+
print(f"{path:<{width}} {ver:<9} {status}")
88+
if not ok:
89+
drift.append(path)
90+
91+
if drift:
92+
print(
93+
f"\nERROR: {len(drift)} file(s) drift from canonical {canonical}: {', '.join(drift)}",
94+
file=sys.stderr,
95+
)
96+
return 1
97+
print("\nAll versions aligned.")
98+
return 0
99+
100+
101+
if __name__ == "__main__":
102+
sys.exit(main())

0 commit comments

Comments
 (0)