import os
import sys
import re
import shutil
import urllib.request
import subprocess
import tempfile
import base64
import codecs
import time
def to_sri(hash_str):
res = run_cmd(["nix", "hash", "to-sri", "--type", "sha256", hash_str.strip()])
return res.stdout.strip()
def run_cmd(cmd, check=True):
res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if check and res.returncode != 0:
print(f"Error running command: {' '.join(cmd)}", file=sys.stderr)
print(f"Stdout:\n{res.stdout}", file=sys.stderr)
print(f"Stderr:\n{res.stderr}", file=sys.stderr)
sys.exit(res.returncode)
return res
def main():
if not shutil.which("nix-prefetch-url"):
print("Error: nix-prefetch-url is not installed or not in PATH.", file=sys.stderr)
print("Please install Nix to use this script.", file=sys.stderr)
sys.exit(1)
version = None
if len(sys.argv) > 1:
version = sys.argv[1]
elif "GITHUB_REF_NAME" in os.environ and os.environ["GITHUB_REF_NAME"].startswith("v"):
version = os.environ["GITHUB_REF_NAME"][1:]
else:
try:
with open("Cargo.toml", "r") as f:
for line in f:
if line.startswith("version ="):
version = line.split("=")[1].strip().replace('"', '')
break
except Exception as e:
print(f"Error reading Cargo.toml: {e}", file=sys.stderr)
sys.exit(1)
if not version:
print("Error: Could not determine version.", file=sys.stderr)
sys.exit(1)
print(f"Target version: {version}")
url = f"https://github.com/l1a/retch/archive/refs/tags/v{version}.tar.gz"
print(f"Prefetching source archive from: {url}")
src_hash_sri = None
for attempt in range(1, 4):
res = run_cmd(["nix-prefetch-url", "--unpack", url], check=False)
if res.returncode == 0:
src_hash_sri = to_sri(res.stdout.strip())
break
print(f"Attempt {attempt} failed. Retrying in 5 seconds...", file=sys.stderr)
time.sleep(5)
if not src_hash_sri:
print("Error: Failed to prefetch source archive.", file=sys.stderr)
sys.exit(1)
print(f"Source Hash (SRI): {src_hash_sri}")
print("Calculating cargo vendor hash...")
with tempfile.TemporaryDirectory() as tmpdir:
temp_package = os.path.join(tmpdir, "package.nix")
with open("packaging/nixpkgs/package.nix", "r") as f:
content = f.read()
content = re.sub(r'version = ".*";', f'version = "{version}";', content)
content = re.sub(r'hash = lib.fakeHash;', f'hash = "{src_hash_sri}";', content)
dummy_sri = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
content = re.sub(r'cargoHash = lib.fakeHash;', f'cargoHash = "{dummy_sri}";', content)
with open(temp_package, "w") as f:
f.write(content)
expr = f"with import <nixpkgs> {{}}; callPackage {temp_package} {{}}"
res = run_cmd(["nix-build", "--no-out-link", "-E", expr], check=False)
stderr = res.stderr
cargo_hash = None
m = re.search(r'specified:\s+sha256-A+.*\n\s+got:\s+(sha256-\S+)', stderr)
if m:
cargo_hash = m.group(1)
else:
hashes = re.findall(r'sha256-[a-zA-Z0-9+/=]{44}', stderr)
for h in hashes:
if "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" not in h:
cargo_hash = h
break
if not cargo_hash:
print("Error: Could not extract cargo vendor hash from nix-build output.", file=sys.stderr)
print(f"Build Output:\n{stderr}", file=sys.stderr)
sys.exit(1)
print(f"Cargo Hash: {cargo_hash}")
if "GITHUB_ACTIONS" not in os.environ:
print("Updating packaging/nixpkgs/package.nix in-place...")
with open("packaging/nixpkgs/package.nix", "r") as f:
orig_content = f.read()
new_content = re.sub(r'version = ".*";', f'version = "{version}";', orig_content)
new_content = re.sub(r'hash = lib.fakeHash;', f'hash = "{src_hash_sri}";', new_content)
new_content = re.sub(r'cargoHash = lib.fakeHash;', f'cargoHash = "{cargo_hash}";', new_content)
with open("packaging/nixpkgs/package.nix", "w") as f:
f.write(new_content)
print("Successfully updated package.nix!")
else:
print("\n" + "="*40)
print("NIXPKGS PACKAGE SNIPPET FOR RELEASE NOTES")
print("="*40)
print(f"version = \"{version}\";")
print(f"hash = \"{src_hash_sri}\";")
print(f"cargoHash = \"{cargo_hash}\";")
print("="*40 + "\n")
if "GITHUB_OUTPUT" in os.environ:
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
f.write(f"version={version}\n")
f.write(f"src_hash={src_hash_sri}\n")
f.write(f"cargo_hash={cargo_hash}\n")
if __name__ == "__main__":
main()