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/
This commit is contained in:
parent
a17edd3f18
commit
15bed78f6a
122
raspberrypi/misc/raspi-build
Executable file
122
raspberrypi/misc/raspi-build
Executable file
@ -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)
|
Loading…
Reference in New Issue
Block a user