Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
82 lines
4 KiB
Python
82 lines
4 KiB
Python
#!/usr/bin/env python3
|
|
"""Install one reviewed CLI bundle offline into a new owner-controlled prefix."""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import platform
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
def digest(path):
|
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
|
|
|
|
def install(bundle, prefix, expected_manifest, python, uv):
|
|
manifest_path = bundle / "manifest.json"
|
|
if digest(manifest_path) != expected_manifest:
|
|
raise ValueError("bundle manifest differs from reviewed digest")
|
|
manifest = json.loads(manifest_path.read_text())
|
|
if manifest["schema"] != "secrets-engine.cli-bundle.v1":
|
|
raise ValueError("unsupported bundle")
|
|
if manifest['system'] != platform.system() or manifest['machine'] != platform.machine():
|
|
raise ValueError("bundle platform differs from installer host")
|
|
expected_files = set(manifest['artifacts']) | {'manifest.json'}
|
|
actual_files = {str(p.relative_to(bundle)) for p in bundle.rglob('*') if p.is_file()}
|
|
if actual_files != expected_files:
|
|
raise ValueError("bundle file set differs")
|
|
for name, expected in manifest['artifacts'].items():
|
|
relative = Path(name)
|
|
if relative.is_absolute() or '..' in relative.parts:
|
|
raise ValueError("invalid artifact path")
|
|
artifact = bundle / relative
|
|
if any(parent.is_symlink() for parent in (artifact,*artifact.parents)) or digest(artifact) != expected:
|
|
raise ValueError("bundle artifact missing or changed")
|
|
# Reserve once; never replace an installed prefix or follow an existing link.
|
|
prefix.mkdir(mode=0o700, parents=False, exist_ok=False)
|
|
try:
|
|
subprocess.run([uv,'--no-config','venv','--python',python,'--no-python-downloads',str(prefix/'venv')],check=True)
|
|
subprocess.run([uv,'--no-config','pip','install','--python',str(prefix/'venv/bin/python'),
|
|
'--no-index','--find-links',str(bundle/'wheels'),str(bundle/manifest['wheel'])],check=True)
|
|
(prefix/'libexec').mkdir(mode=0o700)
|
|
helper=prefix/'libexec/secrets-engine-pdp-verify'
|
|
shutil.copyfile(bundle/'bin/secrets-engine-pdp-verify',helper);helper.chmod(0o700)
|
|
(prefix/'bin').mkdir(mode=0o700)
|
|
launcher=prefix/'bin/secrets-engine'
|
|
# An absolute venv interpreter fixes the installed dependency environment.
|
|
launcher.write_text('#!'+str(prefix/'venv/bin/python')+'\n'+(bundle/'release_cli.py').read_text())
|
|
launcher.chmod(0o700)
|
|
receipt={"schema":"secrets-engine.cli-install.v1","manifest_sha256":expected_manifest,
|
|
"helper_sha256":digest(helper),"launcher_sha256":digest(launcher),"prefix":str(prefix),
|
|
"admission":"artifact integrity only; trust and keys not admitted"}
|
|
(prefix/'install-receipt.json').write_text(json.dumps(receipt,sort_keys=True,indent=2)+'\n')
|
|
return receipt
|
|
except BaseException:
|
|
# Preserve a failed prefix as evidence; never retry into a partial install.
|
|
(prefix/'INSTALL-FAILED').write_text('Incomplete installation; use a new prefix.\n')
|
|
raise
|
|
|
|
|
|
def main():
|
|
parser=argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--bundle',type=Path,default=Path(__file__).resolve().parent)
|
|
parser.add_argument('--prefix',type=Path,required=True)
|
|
parser.add_argument('--manifest-sha256',required=True)
|
|
parser.add_argument('--python',default=sys.executable)
|
|
parser.add_argument('--uv',default='uv')
|
|
args=parser.parse_args()
|
|
try:
|
|
if not args.prefix.is_absolute() or args.prefix.parent.is_symlink():
|
|
raise ValueError('prefix must be absolute with an admitted parent')
|
|
receipt=install(args.bundle.resolve(),args.prefix,args.manifest_sha256,args.python,args.uv)
|
|
print(json.dumps(receipt,sort_keys=True))
|
|
except (OSError,ValueError,KeyError,subprocess.SubprocessError) as exc:
|
|
print('bundle install refused: '+str(exc),file=sys.stderr)
|
|
return 1
|
|
return 0
|
|
|
|
|
|
if __name__=='__main__':
|
|
raise SystemExit(main())
|