release: Claude Code v2.1.72 (25 patches)
This commit is contained in:
239
codex_old/update_codex_patcher.py
Executable file
239
codex_old/update_codex_patcher.py
Executable file
@@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Codex Patcher Update Pipeline — check, update, patch, validate, test.
|
||||
|
||||
Usage:
|
||||
python3 update_codex_patcher.py --check # Check for new version
|
||||
python3 update_codex_patcher.py --update # Download + install new binary
|
||||
python3 update_codex_patcher.py --validate # Validate 6 config targets
|
||||
python3 update_codex_patcher.py --patch # Apply config patches
|
||||
python3 update_codex_patcher.py --test # Integration test
|
||||
python3 update_codex_patcher.py --auto # Full cycle
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(SCRIPT_DIR))
|
||||
|
||||
# ANSI colors
|
||||
GREEN = "\033[92m"
|
||||
YELLOW = "\033[93m"
|
||||
RED = "\033[91m"
|
||||
CYAN = "\033[96m"
|
||||
BOLD = "\033[1m"
|
||||
RESET = "\033[0m"
|
||||
|
||||
|
||||
def color(text, c):
|
||||
return f"{c}{text}{RESET}"
|
||||
|
||||
|
||||
def load_config():
|
||||
"""Load codex_config.json."""
|
||||
config_path = SCRIPT_DIR / "codex_config.json"
|
||||
if not config_path.is_file():
|
||||
print(f" {color('Config not found: ' + str(config_path), RED)}")
|
||||
sys.exit(1)
|
||||
with open(config_path) as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def cmd_check(config):
|
||||
"""Check GitHub releases for new Codex version."""
|
||||
print(f"\n{BOLD}Checking for updates...{RESET}")
|
||||
try:
|
||||
import urllib.request
|
||||
url = "https://api.github.com/repos/openai/codex/releases/latest"
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "codex-patcher"})
|
||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||
data = json.loads(resp.read())
|
||||
|
||||
latest_tag = data.get("tag_name", "")
|
||||
# Tag format: "rust-v0.111.0"
|
||||
latest_version = latest_tag.replace("rust-v", "").replace("v", "")
|
||||
|
||||
# Get installed version
|
||||
from codex_patcher import detect_codex
|
||||
_, installed = detect_codex()
|
||||
|
||||
print(f" Installed: {CYAN}{installed}{RESET}")
|
||||
print(f" Latest: {CYAN}{latest_version}{RESET}")
|
||||
|
||||
if installed == latest_version:
|
||||
print(f" {GREEN}Already up to date!{RESET}")
|
||||
return True
|
||||
else:
|
||||
print(f" {YELLOW}Update available: {installed} → {latest_version}{RESET}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f" {color(f'Error: {e}', RED)}")
|
||||
return False
|
||||
|
||||
|
||||
def cmd_update(config):
|
||||
"""Download and install new Codex binary via update-codex.sh."""
|
||||
print(f"\n{BOLD}Updating Codex binary...{RESET}")
|
||||
update_script = SCRIPT_DIR / "update-codex.sh"
|
||||
if not update_script.is_file():
|
||||
print(f" {color(f'update-codex.sh not found at {update_script}', RED)}")
|
||||
return False
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["bash", str(update_script)],
|
||||
timeout=300
|
||||
)
|
||||
return result.returncode == 0
|
||||
except Exception as e:
|
||||
print(f" {color(f'Error: {e}', RED)}")
|
||||
return False
|
||||
|
||||
|
||||
def cmd_validate(config):
|
||||
"""Validate all 6 config targets."""
|
||||
print(f"\n{BOLD}Validating config targets...{RESET}")
|
||||
try:
|
||||
from updater.config_validator import validate_all, print_validation_report
|
||||
codex_dir = os.path.expanduser("~/.codex")
|
||||
results = validate_all(codex_dir, config)
|
||||
counts = print_validation_report(results)
|
||||
|
||||
# Save report
|
||||
report_dir = SCRIPT_DIR / "reports"
|
||||
report_dir.mkdir(exist_ok=True)
|
||||
report_path = report_dir / "validation_report.json"
|
||||
summary = {
|
||||
"targets": [
|
||||
{"name": t.name, "status": s, "message": m}
|
||||
for t, s, m in results
|
||||
],
|
||||
"counts": counts,
|
||||
}
|
||||
with open(report_path, "w") as f:
|
||||
json.dump(summary, f, indent=2)
|
||||
print(f"\n Report saved: {report_path}")
|
||||
|
||||
return counts.get("RED", 0) == 0
|
||||
except Exception as e:
|
||||
print(f" {color(f'Error: {e}', RED)}")
|
||||
return False
|
||||
|
||||
|
||||
def cmd_patch(config):
|
||||
"""Apply config patches."""
|
||||
print(f"\n{BOLD}Applying patches...{RESET}")
|
||||
try:
|
||||
from codex_patcher import apply_all_patches
|
||||
ok, results = apply_all_patches(config)
|
||||
return ok
|
||||
except Exception as e:
|
||||
print(f" {color(f'Error: {e}', RED)}")
|
||||
return False
|
||||
|
||||
|
||||
def cmd_test(config):
|
||||
"""Run integration test."""
|
||||
print(f"\n{BOLD}Running integration test...{RESET}")
|
||||
|
||||
base_url = config["base_url"].rstrip("/")
|
||||
if not base_url.endswith("/v1"):
|
||||
base_url += "/v1"
|
||||
|
||||
env = os.environ.copy()
|
||||
env["OPENAI_BASE_URL"] = base_url
|
||||
env["OPENAI_API_KEY"] = config["api_key"]
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["codex", "exec",
|
||||
"--sandbox", "danger-full-access",
|
||||
"Reply with just the number 42"],
|
||||
capture_output=True, text=True, timeout=60, env=env
|
||||
)
|
||||
|
||||
output = result.stdout.strip()
|
||||
print(f" Output: {output[:200]}")
|
||||
|
||||
if "42" in output:
|
||||
print(f" {GREEN}Test passed!{RESET}")
|
||||
return True
|
||||
else:
|
||||
print(f" {YELLOW}Unexpected output (no '42' found){RESET}")
|
||||
if result.stderr:
|
||||
print(f" Stderr: {result.stderr[:200]}")
|
||||
return False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
print(f" {RED}Test timed out (60s){RESET}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f" {color(f'Error: {e}', RED)}")
|
||||
return False
|
||||
|
||||
|
||||
def cmd_auto(config):
|
||||
"""Full cycle: check → update → patch → validate → test."""
|
||||
print(f"\n{BOLD}{'=' * 50}{RESET}")
|
||||
print(f"{BOLD} Codex Patcher — Auto Update Pipeline{RESET}")
|
||||
print(f"{BOLD}{'=' * 50}{RESET}")
|
||||
|
||||
steps = [
|
||||
("Check version", cmd_check),
|
||||
("Update binary", cmd_update),
|
||||
("Apply patches", cmd_patch),
|
||||
("Validate", cmd_validate),
|
||||
("Test", cmd_test),
|
||||
]
|
||||
|
||||
for name, func in steps:
|
||||
ok = func(config)
|
||||
if not ok and name not in ("Check version",):
|
||||
print(f"\n {RED}Pipeline stopped at: {name}{RESET}")
|
||||
return False
|
||||
|
||||
print(f"\n{GREEN}{'=' * 50}{RESET}")
|
||||
print(f"{GREEN} Pipeline completed successfully!{RESET}")
|
||||
print(f"{GREEN}{'=' * 50}{RESET}")
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Codex Patcher Update Pipeline"
|
||||
)
|
||||
parser.add_argument("--check", action="store_true", help="Check for new version")
|
||||
parser.add_argument("--update", action="store_true", help="Update binary")
|
||||
parser.add_argument("--validate", action="store_true", help="Validate config")
|
||||
parser.add_argument("--patch", action="store_true", help="Apply patches")
|
||||
parser.add_argument("--test", action="store_true", help="Run integration test")
|
||||
parser.add_argument("--auto", action="store_true", help="Full auto cycle")
|
||||
args = parser.parse_args()
|
||||
|
||||
config = load_config()
|
||||
|
||||
if args.auto:
|
||||
return 0 if cmd_auto(config) else 1
|
||||
if args.check:
|
||||
return 0 if cmd_check(config) else 1
|
||||
if args.update:
|
||||
return 0 if cmd_update(config) else 1
|
||||
if args.validate:
|
||||
return 0 if cmd_validate(config) else 1
|
||||
if args.patch:
|
||||
return 0 if cmd_patch(config) else 1
|
||||
if args.test:
|
||||
return 0 if cmd_test(config) else 1
|
||||
|
||||
parser.print_help()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user