- Pin official Codex 0.161.0 and Claude Code 2.1.292 binaries - Preserve Codex helpers and Claude's NixOS compatibility wrapper - Add an updater for stable upstream versions and checksums - Document CLI updates independently of stable NixOS inputs
58 lines
2.2 KiB
Python
58 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Pin the latest stable upstream CLI releases without updating NixOS."""
|
|
|
|
import json
|
|
import re
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
|
|
def fetch_json(url):
|
|
request = urllib.request.Request(
|
|
url, headers={"User-Agent": "nixos-config-ai-cli-updater"}
|
|
)
|
|
with urllib.request.urlopen(request, timeout=60) as response:
|
|
return json.load(response)
|
|
|
|
|
|
def latest_release(repository):
|
|
release = fetch_json(f"https://api.github.com/repos/{repository}/releases/latest")
|
|
if release["prerelease"] or release["draft"]:
|
|
raise ValueError(f"Expected a stable release from {repository}")
|
|
return release
|
|
|
|
|
|
codex = latest_release("openai/codex")
|
|
codex_version = codex["tag_name"].removeprefix("rust-v")
|
|
claude = latest_release("anthropics/claude-code")
|
|
claude_version = claude["tag_name"].removeprefix("v")
|
|
for version in (codex_version, claude_version):
|
|
if not re.fullmatch(r"\d+\.\d+\.\d+", version):
|
|
raise ValueError(f"Unexpected release version: {version}")
|
|
|
|
asset = next(
|
|
item for item in codex["assets"]
|
|
if item["name"] == "codex-package-x86_64-unknown-linux-musl.tar.gz"
|
|
)
|
|
digest = asset["digest"]
|
|
if not re.fullmatch(r"sha256:[0-9a-f]{64}", digest):
|
|
raise ValueError("Codex release asset has no valid SHA-256 digest")
|
|
|
|
manifest = fetch_json(
|
|
f"https://downloads.claude.ai/claude-code-releases/{claude_version}/manifest.json"
|
|
)
|
|
if manifest["version"] != claude_version:
|
|
raise ValueError("Claude release and download manifest versions differ")
|
|
claude_hash = manifest["platforms"]["linux-x64"]["checksum"]
|
|
if not re.fullmatch(r"[0-9a-f]{64}", claude_hash):
|
|
raise ValueError("Claude manifest has no valid SHA-256 checksum")
|
|
|
|
versions = {
|
|
"codex": {"version": codex_version, "sha256": digest.removeprefix("sha256:")},
|
|
"claude-code": {"version": claude_version, "sha256": claude_hash},
|
|
}
|
|
destination = Path(__file__).resolve().parent.parent / "packages" / "ai-cli-versions.json"
|
|
destination.write_text(json.dumps(versions, indent=2) + "\n")
|
|
print(f"Pinned Codex {codex_version} and Claude Code {claude_version}.")
|
|
print("Review the diff, build the packages, rebuild your host and commit the pins.")
|