msds-tds/scripts/version-track.py
2026-07-08 13:12:20 +00:00

207 lines
6.5 KiB
Python

#!/usr/bin/env python3
"""
Full pipeline: generate → audit → version → push to Gitea.
Usage:
python3 version-track.py # generate all + audit + push
python3 version-track.py "Ethyl acetate" # specific product only
python3 version-track.py --message "v2.1 WGK fix" # custom commit message
python3 version-track.py --push-only # just commit+push existing
"""
import os, sys, subprocess, json, glob
from datetime import datetime
BASE = os.path.dirname(os.path.abspath(__file__))
OUT = os.path.join(BASE, "output")
SCRIPTS = os.path.join(BASE, "scripts")
# ─── Colours ───
GREEN = "\033[92m"; RED = "\033[91m"; YELLOW = "\033[93m"; RESET = "\033[0m"; BOLD = "\033[1m"
def ok(t): return f"{GREEN}✅ {t}{RESET}"
def fail(t): return f"{RED}❌ {t}{RESET}"
def warn(t): return f"{YELLOW}⚠️ {t}{RESET}"
def info(t): return f"{BOLD}{t}{RESET}"
def sh(cmd, cwd=None):
"""Run shell command, return (code, stdout, stderr)."""
r = subprocess.run(cmd, shell=True, capture_output=True, text=True, cwd=cwd or BASE)
return r.returncode, r.stdout.strip(), r.stderr.strip()
def generate_all(filter_product=None):
"""Run the BG batch generator."""
print(info("\n=== Step 1: Generate PDFs ==="))
code, out, err = sh(f"python3 {SCRIPTS}/generate-and-audit.py")
if code != 0:
print(fail(f"Generation failed: {err}"))
return False
print(ok("All BG PDFs generated"))
return True
def run_audit(lang="auto"):
"""Run compliance audit on ALL PDFs in output/."""
print(info(f"\n=== Step 2: Compliance Audit ==="))
code, out, err = sh(f"python3 {SCRIPTS}/compliance-audit.py {OUT}/ --lang {lang}")
if code != 0:
print(fail(f"Audit failed: {err}"))
return False
# Parse summary from output
for line in out.split("\n"):
if "PASS" in line and ":" in line and "✅" not in line:
print(f" {line.strip()}")
if "NO PASS" in line and ":" in line:
print(f" {line.strip()}")
if "Average" in line:
print(f" {line.strip()}")
print(ok("Audit complete"))
return True
def get_current_version():
"""Determine next version number from git tags."""
code, out, err = sh("git tag -l 'v*' --sort=-v:refname")
if code != 0 or not out:
return "v1.0.0"
tags = out.split("\n")
latest = tags[0].lstrip("v")
parts = latest.split(".")
try:
parts[-1] = str(int(parts[-1]) + 1)
return "v" + ".".join(parts)
except:
return f"v{datetime.now().strftime('%Y%m%d')}"
def generate_version_notes():
"""Generate RELEASE_NOTES.md with summary of all generated files."""
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Collect stats
pdfs = glob.glob(os.path.join(OUT, "SDS_BG_*.pdf")) + \
glob.glob(os.path.join(OUT, "SDS_Ethyl_*.pdf"))
notes = [f"# SDS Release — {now}",
f"\n## Files Generated",
f"\n| File | Size |",
f"|------|------|"]
for p in sorted(pdfs):
size = os.path.getsize(p) // 1024
notes.append(f"| {os.path.basename(p)} | {size} KB |")
# Audit summary
report_path = os.path.join(OUT, "master-compliance-audit-report.md")
if os.path.exists(report_path):
with open(report_path) as f:
notes.append(f"\n## Audit Summary\n")
for line in f:
if "Summary" in line or "PASS" in line or "FAIL" in line or "Average" in line or "Total" in line:
notes.append(line.rstrip())
notes.append(f"\n## Version Tracking")
notes.append(f"Generated and committed by Hermes Agent on {now}")
return "\n".join(notes)
def git_commit_push(message=None, version=None):
"""Commit and push all changes to Gitea."""
print(info(f"\n=== Step 3: Git Commit & Push ==="))
if not version:
version = get_current_version()
if not message:
message = f"{version}: SDS batch generation + audit"
# Stage all changes
sh("git add -A", cwd=BASE)
# Check if anything to commit
code, out, err = sh("git status --porcelain", cwd=BASE)
if not out.strip():
print(warn("Nothing to commit"))
return True
# Commit
code, out, err = sh(f'git commit -m "{message}"', cwd=BASE)
if code != 0:
print(fail(f"Commit failed: {err}"))
return False
print(ok(f"Committed: {message}"))
# Tag
code, _, _ = sh(f'git tag -a {version} -m "{message}"', cwd=BASE)
if code == 0:
print(ok(f"Tagged: {version}"))
# Push
print(info("Pushing to Gitea..."))
code, out, err = sh("git push origin main --tags 2>&1", cwd=BASE)
if code != 0:
# Try master branch
code, out, err = sh("git push origin master --tags 2>&1", cwd=BASE)
if code != 0:
print(fail(f"Push failed: {err[:200]}"))
return False
print(ok(f"Pushed to Gitea (git.bachko.com / 10.0.0.19:3000)"))
return True
def main():
args = sys.argv[1:]
push_only = "--push-only" in args
msg = None
# Parse custom message
for a in args:
if a.startswith("--message="):
msg = a.split("=", 1)[1]
elif a.startswith("--message"):
idx = args.index(a)
if idx + 1 < len(args):
msg = args[idx + 1]
if push_only:
# Just commit+push existing
if not msg:
msg = f"v{datetime.now().strftime('%Y%m%d_%H%M')}: manual push"
git_commit_push(message=msg)
return
# Full pipeline
print(BOLD + "═" * 60 + RESET)
print(info(" MSDS-TDS Version Tracker"))
print(BOLD + "═" * 60 + RESET)
# 1. Generate
if not generate_all():
sys.exit(1)
# 2. Audit (auto-detect language)
if not run_audit(lang="auto"):
sys.exit(1)
# 3. Write release notes
notes = generate_version_notes()
notes_path = os.path.join(BASE, "RELEASE_NOTES.md")
with open(notes_path, "w") as f:
f.write(notes)
print(ok(f"Release notes: RELEASE_NOTES.md"))
# 4. Commit & push
version = get_current_version()
if not msg:
msg = f"{version}: batch generate + audit"
if not git_commit_push(message=msg, version=version):
print(warn("Push failed — changes committed locally"))
print(BOLD + "\n═" * 60 + RESET)
print(ok(f"Done. Version {version}"))
print(info(f"Gitea: https://git.bachko.com/bachko/msds-tds"))
print(BOLD + "═" * 60 + RESET)
if __name__ == "__main__":
main()