import argparse import sys from .checker import build_summary, check_service from .classifier import Classifier from .comparator import compare_versions from .loader import load_dotenv, load_services_dir from .notifier import send_matrix_message, send_uptime_kuma_ping from .updater import run_update_script def main() -> int: parser = argparse.ArgumentParser(description="Check for webservice updates") parser.add_argument("--services", default="services", help="Path to services directory") parser.add_argument("--all", action="store_true", help="Show all services") parser.add_argument("--timeout", type=float, default=10.0, help="HTTP timeout in seconds") parser.add_argument("--user-agent", default="service-update-alerts/1.0", help="HTTP user agent") parser.add_argument("--auto-update", action="store_true", help="Automatically update services when safe") parser.add_argument("--classify-timeout", type=float, default=300.0, help="Timeout for Ollama classification in seconds") parser.add_argument("--script-timeout", type=float, default=300.0, help="Timeout for auto-update scripts in seconds") args = parser.parse_args() load_dotenv() try: services = load_services_dir(args.services) except Exception as exc: print(f"Failed to load services: {exc}", file=sys.stderr) return 1 results = [check_service(svc, args.timeout, args.user_agent) for svc in services.values()] output_lines = [] for result in sorted(results, key=lambda r: r["name"].lower()): comparison = compare_versions(result["current"], result["latest"]) has_update = comparison == 1 if not args.all and not has_update: continue current = result["current"] or "unknown" latest = result["latest"] or "unknown" notes = [] if comparison is None and result["current"] and result["latest"]: notes.append("unparseable") if result["current_error"]: notes.append("current error") if result["latest_error"]: notes.append("latest error") suffix = f" [{' '.join(notes)}]" if notes else "" output_lines.append(f"{result['name']}: {current} -> {latest}{suffix}") if output_lines: print("\n".join(output_lines)) for result in sorted(results, key=lambda r: r["name"].lower()): if result["current_error"]: print(f"{result['name']}: current version error: {result['current_error']}", file=sys.stderr) if result["latest_error"]: print(f"{result['name']}: latest version error: {result['latest_error']}", file=sys.stderr) # Build one consolidated Matrix message matrix_sections = [] if args.auto_update: classifier = Classifier() auto_updated = [] manual_needed = [] update_pending = [] for result in results: if compare_versions(result["current"], result["latest"]) != 1: continue service = services[result["name"]] current = result["current"] or "unknown" latest = result["latest"] or "unknown" script = service.service_dir / "auto-update.sh" if service.service_dir else None if not script or not script.exists(): update_pending.append(f"- {service.name}: {current} -> {latest}") continue classification = classifier.classify( service.service_dir, result.get("release_notes"), current, latest, timeout=args.classify_timeout, ) if classification["action"] == "auto": script_result = run_update_script(script, current, latest, timeout=args.script_timeout) if script_result["success"]: msg = f"- {service.name}: {current} -> {latest}\n Reason: {classification['reason']}" if script_result["output"]: msg += f"\n Output: {script_result['output']}" auto_updated.append(msg) else: msg = f"- {service.name}: {current} -> {latest}\n Error: {script_result['error']}" manual_needed.append(msg) else: msg = f"- {service.name}: {current} -> {latest}\n Reason: {classification['reason']}" manual_needed.append(msg) if auto_updated: matrix_sections.append("Auto-updated:\n" + "\n".join(auto_updated)) if manual_needed: matrix_sections.append("Manual update needed:\n" + "\n".join(manual_needed)) if update_pending: matrix_sections.append("Updates available (no auto-update configured):\n" + "\n".join(update_pending)) else: summary, should_notify = build_summary(results) if should_notify: matrix_sections.append(summary) errors = [r for r in results if r["current_error"] or r["latest_error"]] if errors: error_lines = [] for r in errors: if r["current_error"]: error_lines.append(f"- {r['name']}: current version error") if r["latest_error"]: error_lines.append(f"- {r['name']}: latest version error") matrix_sections.append("Errors:\n" + "\n".join(error_lines)) if matrix_sections: send_matrix_message("\n\n".join(matrix_sections), args.timeout) send_uptime_kuma_ping(args.timeout) return 0 if __name__ == "__main__": raise SystemExit(main())