mirror of
https://github.com/Sea-Haven-Industries/afterhours-shift-manager.git
synced 2026-09-30 17:13:12 +00:00
73 lines
2.5 KiB
Python
73 lines
2.5 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""CI guard: validate CHANGELOG.md versioning and the in-package copy.
|
||
|
|
|
||
|
|
Run on pull requests. Two checks:
|
||
|
|
|
||
|
|
1. If CHANGELOG.md changed in the PR, its top version must be a clean
|
||
|
|
single-step SemVer bump above the latest ``v*`` tag. (Non-changing PRs —
|
||
|
|
dependabot bumps, docs — are not version-checked, so they don't release.)
|
||
|
|
2. ``src/slack-bot/CHANGELOG.md`` (the copy that ships with the bot and feeds the
|
||
|
|
App Home "What's New" tab) must match the canonical root CHANGELOG.md.
|
||
|
|
|
||
|
|
The workflow passes context via env: ``PREV_TAG`` (latest tag) and
|
||
|
|
``CHANGELOG_CHANGED`` ("true"/"false"). Run locally it assumes the changelog
|
||
|
|
changed so the bump is validated.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import os
|
||
|
|
import pathlib
|
||
|
|
import sys
|
||
|
|
|
||
|
|
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "src" / "shared"))
|
||
|
|
|
||
|
|
from shared.changelog import bump_kind, top_version # noqa: E402
|
||
|
|
|
||
|
|
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||
|
|
PKG_COPY = ROOT / "src" / "slack-bot" / "CHANGELOG.md"
|
||
|
|
|
||
|
|
|
||
|
|
def evaluate(
|
||
|
|
top: str | None, prev_tag: str, changelog_changed: bool, copies_match: bool
|
||
|
|
) -> list[str]:
|
||
|
|
"""Return a list of problems (empty == pass). Pure, for unit testing."""
|
||
|
|
problems = []
|
||
|
|
if not copies_match:
|
||
|
|
problems.append(
|
||
|
|
"src/slack-bot/CHANGELOG.md is out of sync with CHANGELOG.md — "
|
||
|
|
"run scripts/sync_changelog.py"
|
||
|
|
)
|
||
|
|
if changelog_changed:
|
||
|
|
if top is None:
|
||
|
|
problems.append("CHANGELOG.md changed but has no version entry at the top")
|
||
|
|
else:
|
||
|
|
prev = (prev_tag or "v0.0.0").lstrip("v")
|
||
|
|
if bump_kind(top, prev) is None:
|
||
|
|
problems.append(
|
||
|
|
f"top version v{top} is not a clean single-step SemVer bump "
|
||
|
|
f"above the latest tag v{prev} (expected one of "
|
||
|
|
f"inc-major / inc-minor / inc-patch)"
|
||
|
|
)
|
||
|
|
return problems
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> int:
|
||
|
|
root_text = (ROOT / "CHANGELOG.md").read_text()
|
||
|
|
copies_match = PKG_COPY.exists() and PKG_COPY.read_text() == root_text
|
||
|
|
|
||
|
|
problems = evaluate(
|
||
|
|
top=top_version(root_text),
|
||
|
|
prev_tag=os.environ.get("PREV_TAG", ""),
|
||
|
|
changelog_changed=os.environ.get("CHANGELOG_CHANGED", "true") == "true",
|
||
|
|
copies_match=copies_match,
|
||
|
|
)
|
||
|
|
if problems:
|
||
|
|
for problem in problems:
|
||
|
|
print(f"::error::{problem}")
|
||
|
|
return 1
|
||
|
|
print("CHANGELOG guard passed.")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
raise SystemExit(main())
|