Register 32-bit ARM binfmt rules and keep QEMU changes package-local

qemu-binfmt-conf.sh groups arm with aarch64 and skipped it; Apple Silicon has
no AArch32 execution, so --ignore-family is needed for ARM32 programs. The
bin/sync-upstream extension, its tests and the Tests workflow edit are
reverted; QEMU updates become reviewed pins.
This commit is contained in:
Marcelo Alcantara committed 2026-09-24 16:05:38 +10:00
1 parent aa64ec9a4f
commit 90fee672ce
7 files changed
+10 -261

No files matched your search

@@ -1,120 +0,0 @@
#!/usr/bin/env python3
"""Track Debian 13 ARM64 QEMU revisions, verifying an immutable snapshot first."""
import functools
import hashlib
import json
import lzma
from pathlib import Path
import re
import subprocess
import sys
import urllib.parse
import urllib.request
FEEDS = [
'https://deb.debian.org/debian/dists/trixie/main/binary-arm64/Packages.xz',
'https://deb.debian.org/debian/dists/trixie-updates/main/binary-arm64/Packages.xz',
'https://security.debian.org/debian-security/dists/trixie-security/main/binary-arm64/Packages.xz',
]
def fetch(url):
with urllib.request.urlopen(url, timeout=120) as response:
if not response.url.startswith('https://'):
raise ValueError('Insecure redirect')
return response.read()
def parts(version):
match = re.fullmatch(r'(?:(\d+):)?([0-9][A-Za-z0-9.+~]*?)-([A-Za-z0-9.+~]+)', version)
if not match:
raise ValueError('Unsupported Debian version: ' + version)
return int(match[1] or '0'), match[2], match[3]
def segment_cmp(a, b):
# Debian policy: tilde precedes everything, then end/digits, letters,
# then other characters; digit runs are compared numerically.
def order(c):
if c == '~': return -1
if not c or c.isdigit(): return 0
return ord(c) if c.isalpha() else ord(c) + 256
while a or b:
while (a and not a[0].isdigit()) or (b and not b[0].isdigit()):
x, y = order(a[:1]), order(b[:1])
if x != y: return (x > y) - (x < y)
a, b = a[1:], b[1:]
x = re.match(r'\d*', a)[0]
y = re.match(r'\d*', b)[0]
nx, ny = int(x or '0'), int(y or '0')
if nx != ny: return (nx > ny) - (nx < ny)
a, b = a[len(x):], b[len(y):]
return 0
def compare(a, b):
ea, ua, ra = parts(a)
eb, ub, rb = parts(b)
return (ea > eb) - (ea < eb) or segment_cmp(ua, ub) or segment_cmp(ra, rb)
def package_version(version):
epoch, upstream, revision = parts(version)
# Stable releases only. Fail visibly on prerelease/repack conventions that
# need a reviewed Arch ordering rather than silently misordering them.
if '~' in version:
raise ValueError('Debian prerelease needs a reviewed Arch version mapping')
return f'{epoch}.{upstream}.{revision}'
def records(data):
found = []
for stanza in re.split(r'\n\s*\n', lzma.decompress(data).decode()):
fields = dict(re.findall(r'^([A-Za-z0-9-]+): (.*)$', stanza, re.M))
if fields.get('Package') != 'qemu-user': continue
if fields.get('Architecture') != 'arm64': raise ValueError('Wrong architecture')
parts(fields['Version'])
if not re.fullmatch(r'[0-9a-f]{64}', fields.get('SHA256', '')):
raise ValueError('Missing/malformed SHA256')
if not re.fullmatch(r'[1-9][0-9]*', fields.get('Size', '')):
raise ValueError('Missing/malformed package size')
found.append(fields)
return found
def discover(current, current_pkgver, current_hash, get=fetch):
candidates = [row for url in FEEDS for row in records(get(url))]
if not candidates: raise ValueError('No ARM64 qemu-user package in Debian feeds')
selected = max(candidates, key=functools.cmp_to_key(lambda a,b: compare(a['Version'], b['Version'])))
version, checksum = selected['Version'], selected['SHA256']
if any(row['SHA256'] != checksum for row in candidates if row['Version'] == version):
raise ValueError('Debian feeds disagree on the selected checksum')
ordering = compare(version, current)
if ordering < 0: raise ValueError('Debian feeds are older than the pinned recipe')
if ordering == 0:
if checksum != current_hash: raise ValueError('Checksum changed for the pinned Debian revision')
return {}
pkgver = package_version(version)
if int(subprocess.check_output(['vercmp', pkgver, current_pkgver], text=True)) <= 0:
raise ValueError('New Debian revision does not advance Arch version; review mapping')
api = 'https://snapshot.debian.org/mr/binary/qemu-user/' + urllib.parse.quote(version, safe='') + '/binfiles'
document = json.loads(get(api))
if document.get('binary') != 'qemu-user' or document.get('binary_version') != version:
raise ValueError('Snapshot revision differs')
hashes = {row['hash'] for row in document['result'] if row['architecture'] == 'arm64'}
if len(hashes) != 1 or not re.fullmatch(r'[0-9a-f]{40}', next(iter(hashes), '')):
raise ValueError('Missing/ambiguous ARM64 snapshot')
snapshot = hashes.pop()
archive = get('https://snapshot.debian.org/file/' + snapshot)
if len(archive) != int(selected['Size']) or hashlib.sha256(archive).hexdigest() != checksum or hashlib.sha1(archive).hexdigest() != snapshot:
raise ValueError('Snapshot bytes differ from Debian package metadata')
return {'pkgver': pkgver, 'variables': {'_debver': version, '_snapshot': snapshot},
'sha256sums': {'aarch64': [checksum]}}
def main():
recipe = Path('PKGBUILD').read_text()
def scalar(name):
match = re.search(r'^' + name + r'=[\"\']?([^\"\'\n]+)', recipe, re.M)
if not match: raise ValueError('Missing recipe scalar: ' + name)
return match[1]
checksum = re.search(r"^sha256sums_aarch64=\('([a-f0-9]{64})'\)", recipe, re.M)
if not checksum: raise ValueError('Missing current ARM checksum')
print(json.dumps(discover(scalar('_debver'), scalar('pkgver'), checksum[1])))
if __name__ == '__main__':
try: main()
except Exception as error:
sys.exit('QEMU Debian update failed: ' + str(error))
@@ -1,3 +0,0 @@
#!/bin/bash
set -euo pipefail
exec python3 .omarchy/upstream.py