Release Vulpes OS 2.7-preview.22

This commit is contained in:
OverSu 2026-10-22 20:42:26 +02:00
commit a138f97877
294 changed files with 515786 additions and 187 deletions

View file

@ -0,0 +1,87 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: MPL-2.0
"""Assemble private boot/runtime artifacts without including phone profiles."""
import argparse
import hashlib
import io
import json
import subprocess
from pathlib import Path
import tarfile
ROOT=Path(__file__).resolve().parents[2]
ENGINE=ROOT/'tundra/out/beatles/gecko-156.0.1'
def digest(path):
with path.open('rb') as stream:return hashlib.file_digest(stream,'sha256').hexdigest()
def add_beatles_t9(files):
dictionaries=sorted((ROOT/'tundra/devices/beatles/t9').glob('*.tsv'))
if len(dictionaries)!=42:
raise ValueError('Expected the 42 bundled Gaia T9 lexicons')
for path in dictionaries:
files['opt/vulpes/tundra/devices/beatles/t9/'+path.name]=path
def main():
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('--build',type=Path,required=True)
args=parser.parse_args();out=args.build.resolve()
record=json.loads((out/'build.json').read_text())
if record['device']!='beatles' or digest(out/record['image'])!=record['sha256']:raise ValueError('Boot build mismatch')
if (out/'runtime.json').exists():raise ValueError('Runtime already packaged')
engine=json.loads((ENGINE/'engine-staging.json').read_text())
graphics=json.loads((ENGINE/'graphics-runtime.json').read_text())
gaia=json.loads((ENGINE/'gaia-shared.json').read_text())
sources={'engine-runtime.tar.gz':engine['archiveSha256'],
'graphics-runtime.tar.gz':graphics['sha256'],'gaia-shared.tar.gz':gaia['sha256']}
for name,expected in sources.items():
path=ENGINE/name
if digest(path)!=expected:raise ValueError('Input archive changed: '+name)
# Use copy-on-write when available; future rebuilds must not mutate this artifact.
subprocess.run(["cp","--reflink=auto",str(path),str(out/name)],check=True)
files={}
for directory in ('host','services','adapters'):
for p in sorted((ROOT/directory).rglob('*')):
if p.is_file() and '__pycache__' not in p.parts:
files['opt/firefox/vulpes/'+str(p.relative_to(ROOT))]=p
if directory=='host':
files['opt/vulpes/'+str(p.relative_to(ROOT))]=p
files['opt/firefox/vulpes/tests/BeatlesAudit.sys.mjs']=ROOT/'tundra/devices/beatles/gaia-audit.sys.mjs'
files['opt/firefox/vulpes/tests/ProbeChild.sys.mjs']=ROOT/'tests/ProbeChild.sys.mjs'
files['opt/vulpes/assets/webapps/settings.gaiamobile.org/js/modules/wifi_utils.js']=ROOT/'assets/webapps/settings.gaiamobile.org/js/modules/wifi_utils.js'
add_beatles_t9(files)
for name in ('gaia-session.py','cover-display.py','radio_control.py','radio_status.py','call_audio.py','stream_volume.py','hardware_control.py','wifi_control.py','sms_pdu.py'):
files['work/'+name]=ROOT/'tundra/devices/beatles'/name
if digest(out/'control-client')!=record['controlClientSha256']:raise ValueError('Control relay build mismatch')
files['work/control-client']=out/'control-client'
files['work/keypad']=ENGINE/'keypad'
files['etc/localtime']=Path('/usr/share/zoneinfo/UTC')
directories={'work':0o1777,'tmp':0o1777,'proc':0o755,'sys':0o755,'dev':0o755,'run':0o755}
archive=out/'trial-overlay.tar.gz'
with tarfile.open(archive,'w:gz',compresslevel=1) as tar:
for name,mode in directories.items():
item=tarfile.TarInfo(name);item.type=tarfile.DIRTYPE;item.mode=mode;tar.addfile(item)
for name,path in files.items():
content=path.read_bytes();item=tarfile.TarInfo(name);item.size=len(content)
item.mode=0o755 if name in ('work/keypad','work/control-client') else 0o644
tar.addfile(item,io.BytesIO(content))
content=b'exec /usr/bin/python3.11 /work/gaia-session.py\n'
item=tarfile.TarInfo('work/display-test.sh');item.mode=0o644;item.size=len(content);tar.addfile(item,io.BytesIO(content))
sources[archive.name]=digest(archive)
result={'device':'beatles','engine':'156.0.1','runtimePath':record['runtimePath'],
'archivesInExtractionOrder':['graphics-runtime.tar.gz','engine-runtime.tar.gz','gaia-shared.tar.gz','trial-overlay.tar.gz'],
'archives':{name:{'sha256':sha,'bytes':(out/name).stat().st_size} for name,sha in sources.items()},
'sourceFiles':{name:digest(path) for name,path in files.items()},
'containsPhoneUserData':False,'privateDiagnosticBuild':True,'flashReady':False,
'defaultTimezone':'UTC; staging may use the phone timezone',
'requiresExistingVendorSystem':True}
(out/'runtime.json').write_text(json.dumps(result,indent=2)+'\n')
with (out/'SHA256SUMS').open('a') as f:
for name,sha in sources.items():f.write(sha+' '+name+'\n')
print('Runtime archives assembled; no contacts, messages or device profile copied.')
if __name__=='__main__':main()