From 15bed78f6a37ce1ed3b6b05d3d3dac32472a4e26 Mon Sep 17 00:00:00 2001 From: Cyril Brulebois Date: Mon, 22 Jul 2024 09:07:39 +0200 Subject: [PATCH] Add raspi-build script for reference. That script is deployed in a VM maintained by DEBAMAX, initially building and publishing raspi_4_bookworm.img.xz(.sha256) every Monday below: https://pirogue.apt.debamax.com/raspi-images/ --- raspberrypi/misc/raspi-build | 122 +++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100755 raspberrypi/misc/raspi-build diff --git a/raspberrypi/misc/raspi-build b/raspberrypi/misc/raspi-build new file mode 100755 index 0000000..bdaddb2 --- /dev/null +++ b/raspberrypi/misc/raspi-build @@ -0,0 +1,122 @@ +#!/usr/bin/python3 +""" +Helper to build Raspberry Pi images. + +The PiRogue image generation tool would ideally use daily auto-built images +provided by Debian (https://raspi.debian.net/) but the build process might be +stalled for several weeks or months, so we're building our own image(s) in +a weekly fashion. This makes sure we don't end up shipping PiRogue images +containing severely outdated packages (meaning a possibly significant amount of +missing security/stability fixes). + +This script is a wrapper around image-specs's Makefile, and it also helps +maintain the required associated sudo rules. + +Generate sudo configuration for the specified image(s): + + ./raspi-build --sudo raspi_4_bookworm + +Build, compress, checksum, and rsync the specified images(s) to a web server +(see RSYNC_DEST): + + ./raspi-build --build raspi_4_bookworm +""" + +import argparse +import hashlib +import logging +import os +import subprocess +import sys +from pathlib import Path + +# Settings: +WORK_DIR = Path('~/image-specs').expanduser() +RSYNC_DEST = 'website:pirogue.apt.debamax.com/raspi-images/' + + +def sudo(images): + """ + Generate the sudo snippet for all specified images. + """ + print(f'# Generated by {Path(__file__).resolve()}') + for image in images: + # Keep in sync with the Makefile in image-specs: + print(f'{login} ALL=NOPASSWD: /usr/bin/vmdb2 --verbose --rootfs-tarball={image}.tar.gz ' + f'--output={image}.img {image}.yaml --log {image}.log') + # Keep in sync with root-owned products/byproducts (also noting the + # escaped ':' character): + print(f'{login} ALL=NOPASSWD: /usr/bin/chown {uid}\\:{gid} {WORK_DIR}/{image}.img') + print(f'{login} ALL=NOPASSWD: /usr/bin/chown {uid}\\:{gid} {WORK_DIR}/{image}.tar.gz') + + +def run(title, *cmd): + """ + Log and exec. + """ + logging.info(title) + subprocess.check_call(*cmd) + + +def build(images): + """ + Build all specified images. + """ + os.chdir(WORK_DIR) + for image in images: + logging.info('=== BEGIN: WORK WITH %s ===', image) + # Generate the uncompressed images, then fix permissions on our own: + run('Generate image', + ['make', f'{image}.img']) + run('Fix image owner', + ['sudo', 'chown', f'{uid}:{gid}', f'{WORK_DIR}/{image}.img']) + run('Fix tarball owner', + ['sudo', 'chown', f'{uid}:{gid}', f'{WORK_DIR}/{image}.tar.gz']) + + # Generate compressed image ourselves to avoid timestamp-related fun if + # we were using the Makefile: + run('Compress image', + ['xz', '-9', f'{image}.img']) + + # Compute checksum ourselves (same reason as above), without using + # run(): sha256sum doesn't take an output argument so we'd need + # shell=True. + logging.info('Generate checksum') + sha256 = hashlib.file_digest(Path(f'{image}.img.xz').open('rb'), 'sha256') + Path(f'{image}.img.xz.sha256').write_text(f'{sha256.hexdigest()} {image}.img.xz\n') + + # Sync only what we need: + run('Sync compressed image and checksum', + ['rsync', '-av', *Path('.').glob(f'{image}.img.xz*'), RSYNC_DEST]) + logging.info('=== END: WORK WITH %s ===', image) + + logging.info('=== BEGIN: CLEAN-UP ===') + run('Cleaning git', + ['git', 'clean', '-xdf']) + logging.info('=== END: CLEAN-UP ===') + + +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO, + datefmt='%H:%M:%S', + format='%(asctime)s %(message)s') + parser = argparse.ArgumentParser() + parser.add_argument('images', metavar='IMAGE', type=str, nargs='+', + help='an image to build (e.g. raspi_4_bookworm)') + parser.add_argument('--sudo', action='store_true', + help='generate the sudo snippet required to build specified images') + parser.add_argument('--build', action='store_true', + help='build the specified images') + args = parser.parse_args() + + login = os.getlogin() + uid = os.getuid() + gid = os.getgid() + + if args.sudo: + sudo(args.images) + sys.exit(0) + + if args.build: + build(args.images) + sys.exit(0)