Files

49 lines
2.1 KiB
Python
Executable File

#!/usr/bin/env python3
"""Download a pinned official release, verify GitHub's asset SHA256, and unpack it."""
import argparse
import gzip
import hashlib
import json
import os
import platform
import tempfile
import urllib.request
from pathlib import Path
parser = argparse.ArgumentParser()
parser.add_argument('--version', default='v1.19.32')
parser.add_argument('--output', default='bin/mihomo')
args = parser.parse_args()
machine = platform.machine()
arch = {'x86_64': 'amd64-compatible', 'aarch64': 'arm64'}.get(machine)
if arch is None:
raise SystemExit(f'Unsupported architecture: {machine}')
if not args.version.startswith('v') or any(c not in 'v0123456789.' for c in args.version):
raise SystemExit('Use an explicit stable version such as v1.19.32')
request = urllib.request.Request(f'https://api.github.com/repos/MetaCubeX/mihomo/releases/tags/{args.version}', headers={'User-Agent': 'ClashWeb-build'})
with urllib.request.urlopen(request, timeout=30) as response:
release = json.load(response)
name = f'mihomo-linux-{arch}-{args.version}.gz'
asset = next((a for a in release['assets'] if a['name'] == name), None)
if not asset or not asset.get('digest', '').startswith('sha256:'):
raise SystemExit('Release asset or authoritative SHA256 digest is missing; download aborted.')
print(f'Downloading {name}', flush=True)
with urllib.request.urlopen(asset['browser_download_url'], timeout=120) as response:
compressed = response.read(100 * 1024 * 1024 + 1)
if len(compressed) > 100 * 1024 * 1024:
raise SystemExit('Asset exceeds download limit')
if hashlib.sha256(compressed).hexdigest() != asset['digest'].split(':', 1)[1]:
raise SystemExit('SHA256 mismatch; download aborted')
output = Path(args.output)
output.parent.mkdir(parents=True, exist_ok=True)
fd, temporary = tempfile.mkstemp(dir=output.parent, prefix='.mihomo-')
try:
with os.fdopen(fd, 'wb') as stream:
stream.write(gzip.decompress(compressed))
os.chmod(temporary, 0o755)
os.replace(temporary, output)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
print(f'Verified {asset["digest"]}; installed to {output}')