#!/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.dirname(os.path.abspath(__file__))) OUT = os.path.join(BASE, "output") SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__)) # ─── 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_DIR}/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_DIR}/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()