207 lines
6.5 KiB
Python
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()
|