#!/usr/bin/env python3 """ Update GitOps kustomization.yaml with new image SHA tags. Usage: python3 update-gitops.py \ --repo /path/to/nxtgauge-gitops \ --service gateway \ --sha abc123def456... This script: 1. Updates the newTag for the specified service to the SHA 2. Commits and pushes to the gitops repo 3. ArgoCD detects the change and deploys """ import argparse import os import re import subprocess import sys def run(cmd: list[str], cwd: str = None) -> tuple[int, str, str]: """Run a command and return (returncode, stdout, stderr).""" result = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True) return result.returncode, result.stdout, result.stderr def update_kustomization(kustomization_path: str, service: str, sha: str) -> bool: """Update the newTag for a service in kustomization.yaml.""" with open(kustomization_path, "r") as f: content = f.read() # Pattern to find image entry for the service # Matches: - name: registry.nxtgauge.com/nxtgauge-rust-{service} # newTag: something pattern = rf'(\s+-\s+name:\s+registry\.nxtgauge\.com/nxtgauge-rust-{re.escape(service)}\n\s+newTag:\s+)[^\n]+' replacement = rf'\g<1>{sha}' new_content, count = re.subn(pattern, replacement, content) if count == 0: # Try without the nxtgauge-rust- prefix (for frontend, admin, etc) pattern = rf'(\s+-\s+name:\s+registry\.nxtgauge\.com/nxtgauge-{re.escape(service)}\n\s+newTag:\s+)[^\n]+' new_content, count = re.subn(pattern, replacement, content) if count == 0: print(f"[ERROR] Could not find image entry for service: {service}") return False with open(kustomization_path, "w") as f: f.write(new_content) print(f"[OK] Updated {service} to SHA {sha}") return True def main(): parser = argparse.ArgumentParser(description="Update GitOps with new image SHA") parser.add_argument("--repo", required=True, help="Path to gitops repo") parser.add_argument("--service", required=True, help="Service name (e.g., gateway, users, frontend-solid)") parser.add_argument("--sha", required=True, help="Git SHA to deploy") parser.add_argument("--message", default=None, help="Commit message") args = parser.parse_args() service_image_map = { "gateway": "nxtgauge-rust-gateway", "users": "nxtgauge-rust-users", "companies": "nxtgauge-rust-companies", "jobs": "nxtgauge-rust-jobs", "leads": "nxtgauge-rust-leads", "job-seekers": "nxtgauge-rust-job-seekers", "customers": "nxtgauge-rust-customers", "payments": "nxtgauge-rust-payments", "employees": "nxtgauge-rust-employees", "photographers": "nxtgauge-rust-photographers", "makeup-artists": "nxtgauge-rust-makeup-artists", "tutors": "nxtgauge-rust-tutors", "developers": "nxtgauge-rust-developers", "video-editors": "nxtgauge-rust-video-editors", "graphic-designers": "nxtgauge-rust-graphic-designers", "social-media-managers": "nxtgauge-rust-social-media-managers", "fitness-trainers": "nxtgauge-rust-fitness-trainers", "catering-services": "nxtgauge-rust-catering-services", "ugc-content-creators": "nxtgauge-rust-ugc-content-creators", "cron": "nxtgauge-rust-cron", "frontend-solid": "nxtgauge-frontend-solid", "admin-solid": "nxtgauge-admin-solid", "ai-assistant": "nxtgauge-ai-assistant", } # Determine which kustomization file to update if service_image_map.get(args.service): image_name = service_image_map[args.service] else: image_name = f"nxtgauge-{args.service}" # Find the right kustomization file based on service if "frontend" in args.service or "admin" in args.service: kustomization_path = os.path.join(args.repo, "apps/nxtgauge-frontend-solid/overlays/prod/kustomization.yaml") if not os.path.exists(kustomization_path): kustomization_path = os.path.join(args.repo, "apps/nxtgauge-frontend-solid/base/kustomization.yaml") elif "ai-assistant" in args.service: kustomization_path = os.path.join(args.repo, "apps/nxtgauge-ai-assistant/overlays/prod/kustomization.yaml") if not os.path.exists(kustomization_path): kustomization_path = os.path.join(args.repo, "apps/nxtgauge-ai-assistant/base/kustomization.yaml") else: kustomization_path = os.path.join(args.repo, "apps/nxtgauge-backend-rust/overlays/prod/kustomization.yaml") if not os.path.exists(kustomization_path): print(f"[ERROR] Kustomization file not found: {kustomization_path}") sys.exit(0) # Exit 0 per workflow requirement print(f"Updating {kustomization_path} for service {args.service}") if not update_kustomization(kustomization_path, args.service, args.sha): sys.exit(0) # Exit 0 per workflow requirement # Git add, commit, push commit_msg = args.message or f"chore: deploy {args.service}@{args.sha}" run(["git", "add", "-A"], cwd=args.repo) code, stdout, stderr = run(["git", "diff", "--cached", "--stat"], cwd=args.repo) if not stdout.strip(): print("[INFO] No changes to commit") sys.exit(0) print(f"Changes to commit:\n{stdout}") run(["git", "commit", "-m", commit_msg], cwd=args.repo) code, stdout, stderr = run(["git", "push"], cwd=args.repo) if code != 0: print(f"[ERROR] Push failed: {stderr}") else: print(f"[OK] Pushed update to gitops repo") sys.exit(0) # Always exit 0 per workflow requirement if __name__ == "__main__": main()