- .sourcecraft/ci.yaml: push в main → check (compile · ruff мягкий · bandit · pytest по сервисам) → deploy - .tgit/changed_services.py: изменённые services/<name>/ (без родителя — все сервисы) - .tgit/run_tests.py: pytest каждого сервиса со своими requirements.txt - deploy пока заглушка со списком сервисов — tgit-runner в #13 - services/hello: демо-сервис с тестом; tgit.yaml — формат настроек сервиса Refs #7 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
49 lines
1.4 KiB
Python
49 lines
1.4 KiB
Python
"""Print services (`services/<name>/`) changed by the pushed commit, one per line.
|
|
|
|
Falls back to every service when the parent commit is unavailable (first commit or shallow clone),
|
|
so a deploy never silently skips a service.
|
|
"""
|
|
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
NAME = re.compile(r"^[a-z][a-z0-9-]{1,30}$")
|
|
|
|
|
|
def git(*args: str) -> str:
|
|
return subprocess.run(["git", *args], check=True, capture_output=True, text=True).stdout
|
|
|
|
|
|
def all_services() -> list[str]:
|
|
root = Path("services")
|
|
return sorted(p.name for p in root.iterdir() if p.is_dir() and NAME.match(p.name)) if root.is_dir() else []
|
|
|
|
|
|
def changed_paths() -> list[str] | None:
|
|
try:
|
|
git("rev-parse", "--verify", "HEAD~1")
|
|
except subprocess.CalledProcessError:
|
|
try:
|
|
git("fetch", "--deepen=1", "-q")
|
|
git("rev-parse", "--verify", "HEAD~1")
|
|
except subprocess.CalledProcessError:
|
|
return None
|
|
return git("diff", "--name-only", "HEAD~1", "HEAD").splitlines()
|
|
|
|
|
|
def main() -> None:
|
|
paths = changed_paths()
|
|
if paths is None:
|
|
print("no parent commit — deploying all services", file=sys.stderr)
|
|
services = all_services()
|
|
else:
|
|
existing = set(all_services())
|
|
services = sorted({p.split("/")[1] for p in paths if p.startswith("services/") and p.count("/") >= 2} & existing)
|
|
print("\n".join(services))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|