[kolibri] fix variable naming format #8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: 'IIAB image on RPiOS Lite Latest' | |
| # cancel-in-progress: false | |
| # concurrency: | |
| # group: ${{ github.ref }}-${{ github.workflow }} | |
| on: [push, pull_request, workflow_dispatch] | |
| jobs: | |
| build: | |
| runs-on: ubuntu-24.04-arm | |
| # environment: | |
| # name: deploy | |
| strategy: | |
| # fail-fast: true | |
| # max-parallel: 1 | |
| matrix: | |
| include: | |
| # - edition: medical | |
| # config: vars/local_vars_none.yml | |
| # # Maybe only possible self-hosted | |
| # # https://github.com/marketplace/actions/maximize-build-disk-space-only-remove-unwanted-software#how-it-works | |
| # working_image_size: 75000 | |
| # preset_id: en-medical | |
| - edition: large | |
| config: vars/local_vars_large.yml | |
| working_image_size: 20000 | |
| # - edition: medium | |
| # config: vars/local_vars_medium.yml | |
| # working_image_size: 15000 | |
| # - edition: small | |
| # config: vars/local_vars_small.yml | |
| # working_image_size: 15000 | |
| - edition: tiny | |
| config: vars/local_vars_tiny.yml | |
| working_image_size: 7450 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| path: iiab | |
| fetch-depth: 0 # should be a full copy for updating later via git pull | |
| - uses: actions/checkout@v6 | |
| with: | |
| repository: chapmanjacobd/nspawn-loop | |
| path: nspawn-loop | |
| - name: Download RaspberryPi OS | |
| working-directory: nspawn-loop | |
| run: sudo ./mount.sh https://downloads.raspberrypi.org/raspios_lite_arm64_latest ${{ matrix.working_image_size || 10000 }} | |
| - name: Build image | |
| working-directory: nspawn-loop | |
| run: | | |
| sudo -E bash << 'EOF' | |
| set -euo pipefail | |
| STATE_FILE=$(find . -maxdepth 1 -name "*.state" | head -n 1) | |
| source "$STATE_FILE" | |
| VERSION=$(grep '^iiab_base_ver:' ../iiab/vars/default_vars.yml | awk '{print $2}') | |
| echo "IIAB_BASE_VERSION=$VERSION" >> $GITHUB_ENV | |
| DATE=$(date +'%y%m%d') | |
| OS="raspios" | |
| EDITION="${{ matrix.edition }}" | |
| HASH=$(git -C ../iiab rev-parse --short HEAD) | |
| IMG_NAME="iiab-$VERSION-$DATE-$OS-$EDITION-$HASH.img" | |
| echo "IMG_NAME=$IMG_NAME" >> $GITHUB_ENV | |
| mkdir -p "$MOUNT_DIR/opt/iiab" | |
| cp -R --preserve=mode,timestamps,links ../iiab "$MOUNT_DIR/opt/iiab/" | |
| mkdir -p "$MOUNT_DIR/etc/iiab" | |
| cp --preserve=mode,timestamps ../iiab/${{ matrix.config }} "$MOUNT_DIR/etc/iiab/local_vars.yml" | |
| echo "rpi_image: True" >> "$MOUNT_DIR/etc/iiab/local_vars.yml" | |
| sed -i "s/^iiab_admin_user_install: True/iiab_admin_user_install: False/" "$MOUNT_DIR/etc/iiab/local_vars.yml" | |
| if ! command -v expect &>/dev/null; then | |
| apt-get update && apt-get install -y expect | |
| fi | |
| if ! command -v systemd-nspawn &> /dev/null; then | |
| apt-get update && apt-get install -y systemd-container | |
| fi | |
| systemctl is-active --quiet systemd-networkd || systemctl start systemd-networkd | |
| systemctl is-active --quiet systemd-resolved || systemctl start systemd-resolved | |
| # Explicitly allow forwarding for systemd-nspawn virtual interfaces | |
| sysctl -w net.ipv4.ip_forward=1 | |
| EXT_IF=$(ip route | grep default | awk '{print $5}' | head -n1) | |
| iptables -t nat -A POSTROUTING -o "$EXT_IF" -j MASQUERADE | |
| iptables -A FORWARD -i ve-+ -o "$EXT_IF" -j ACCEPT | |
| iptables -A FORWARD -i "$EXT_IF" -o ve-+ -m state --state RELATED,ESTABLISHED -j ACCEPT | |
| # Manually setting DNS can help avoid host loopback/127.0.0.53 issues | |
| # echo "nameserver 1.1.1.1" >> "$MOUNT_DIR/etc/resolv.conf" | |
| sed 's/^ //' << 'EOF_PRESET' > "$MOUNT_DIR/root/install_preset.sh" | |
| #!/bin/bash | |
| set -euo pipefail | |
| cd /opt/admin/cmdsrv | |
| scripts/get_kiwix_catalog | |
| scripts/get_oer2go_catalog | |
| if [ -n "${{ matrix.preset_id }}" ]; then | |
| iiab-cmdsrv-ctl 'INST-PRESETS {"preset_id":"${{ matrix.preset_id }}"}' | |
| fi | |
| rm -f /root/install_preset.sh | |
| EOF_PRESET | |
| chmod +x "$MOUNT_DIR/root/install_preset.sh" | |
| systemd-firstboot --root="$MOUNT_DIR" --delete-root-password --force | |
| export MOUNT_DIR | |
| sed 's/^ //' << 'EXPECT_EOF' | expect | |
| set timeout 7200 | |
| spawn systemd-nspawn -q --network-veth --resolv-conf=off -D $env(MOUNT_DIR) -M box --boot | |
| expect "login: " { send "root\r" } | |
| expect -re {#\s?$} { send "apt update\r" } | |
| expect -re {#\s?$} { send "mkdir -p /etc/initramfs-tools/conf.d && echo 'MODULES=most' > /etc/initramfs-tools/conf.d/nspawn.conf\r" } | |
| expect -re {#\s?$} { send "DEBIAN_FRONTEND=noninteractive apt upgrade -y\r" } | |
| expect -re {#\s?$} { send "curl -fLo /usr/sbin/iiab https://raw.githubusercontent.com/iiab/iiab-factory/master/iiab\r" } | |
| expect -re {#\s?$} { send "chmod 0755 /usr/sbin/iiab\r" } | |
| expect -re {#\s?$} { send "/usr/sbin/iiab --risky\r" } | |
| expect { | |
| timeout { puts "\nTimed out waiting for final confirmation prompt"; exit 1 } | |
| "photographed" { send "\r" } | |
| } | |
| # forced reboot | |
| expect "login: " { send "root\r" } | |
| expect -re {#\s?$} { send "/root/install_preset.sh\r" } | |
| expect -re {#\s?$} { send "usermod --lock --expiredate=1 root\r" } | |
| # legacy whiptail TUI OOBE, replaced by cloud-init / rpi-imager | |
| # expect -re {#\s?$} { send "systemctl disable userconfig.service\r" } | |
| expect -re {#\s?$} { send "shutdown now\r" } | |
| expect eof | |
| EXPECT_EOF | |
| EOF | |
| - name: Validation | |
| working-directory: nspawn-loop | |
| run: | | |
| sudo -E bash << 'EOF' | |
| set -euo pipefail | |
| STATE_FILE=$(find . -maxdepth 1 -name "*.state" | head -n 1) | |
| source "$STATE_FILE" | |
| diff \ | |
| <(sort ../iiab/.github/workflows/tests/expected_state_${{ matrix.edition }}_image.yml) \ | |
| <(sort "$MOUNT_DIR/etc/iiab/iiab_state.yml") | |
| EOF | |
| - name: Shrink image | |
| if: ${{ matrix.edition == 'tiny' }} | |
| working-directory: nspawn-loop | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| sudo -E bash << 'EOF' | |
| set -euo pipefail | |
| STATE_FILE=$(find . -maxdepth 1 -name "*.state" | head -n 1) | |
| source "$STATE_FILE" | |
| # Add image metadata | |
| echo "$IMG_NAME" > "$MOUNT_DIR/.iiab-image" | |
| echo "Build: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$MOUNT_DIR/.iiab-image" | |
| gh pr list --repo ${{ github.repository }} --commit $(git -C ../iiab rev-parse HEAD) --json number,url --jq '.[0] | "PR #\(.number): \(.url)"' >> "$MOUNT_DIR/.iiab-image" 2>/dev/null || true | |
| git -C ../iiab rev-parse --abbrev-ref HEAD >> "$MOUNT_DIR/.iiab-image" | |
| git -C ../iiab remote -v >> "$MOUNT_DIR/.iiab-image" | |
| git -C ../iiab log -1 >> "$MOUNT_DIR/.iiab-image" | |
| # Remove image-specific configuration | |
| rm -f "$MOUNT_DIR/etc/initramfs-tools/conf.d/nspawn.conf" | |
| sed -i '/rpi_image: True/d' "$MOUNT_DIR/etc/iiab/local_vars.yml" | |
| echo uninitialized > "$MOUNT_DIR/etc/machine-id" | |
| rm -f "$MOUNT_DIR/etc/iiab/uuid" | |
| rm -f "$MOUNT_DIR/var/swap" | |
| touch "$MOUNT_DIR/.resize-rootfs" | |
| ./shrink.sh "$STATE_FILE" 1800 | |
| mv "$IMG_FILE" "$IMG_NAME" | |
| EOF | |
| - name: Generate rpi-imager manifest | |
| if: ${{ matrix.edition == 'tiny' }} | |
| working-directory: nspawn-loop | |
| env: | |
| EDITION: ${{ matrix.edition }} | |
| run: | | |
| cat << 'EOF' > gen_manifest.py | |
| import json | |
| import os | |
| import hashlib | |
| import datetime | |
| import sys | |
| img_name = os.environ.get('IMG_NAME') | |
| if not img_name: | |
| print("Error: IMG_NAME not set") | |
| sys.exit(1) | |
| if not os.path.exists(img_name): | |
| print(f"Error: {img_name} not found") | |
| sys.exit(1) | |
| file_size = os.path.getsize(img_name) | |
| print(f"Calculating SHA256 for {img_name} ({file_size} bytes)...") | |
| sha256_hash = hashlib.sha256() | |
| with open(img_name, "rb") as f: | |
| for byte_block in iter(lambda: f.read(4096), b""): | |
| sha256_hash.update(byte_block) | |
| extract_sha256 = sha256_hash.hexdigest() | |
| manifest = { | |
| "imager": { | |
| "devices": [ | |
| { | |
| "name": "Raspberry Pi 5", | |
| "tags": ["pi5-64bit", "pi5-32bit"], | |
| "icon": "https://downloads.raspberrypi.com/imager/icons/RPi_5.png", | |
| "description": "Raspberry Pi 5, 500 / 500+, and Compute Module 5", | |
| "matching_type": "exclusive", | |
| "capabilities": [] | |
| }, | |
| { | |
| "name": "Raspberry Pi 4", | |
| "tags": ["pi4-64bit", "pi4-32bit"], | |
| "default": False, | |
| "icon": "https://downloads.raspberrypi.com/imager/icons/RPi_4.png", | |
| "description": "Raspberry Pi 4 Model B, 400, and Compute Module 4 / 4S", | |
| "matching_type": "inclusive", | |
| "capabilities": [] | |
| }, | |
| { | |
| "name": "Raspberry Pi 3", | |
| "tags": ["pi3-64bit", "pi3-32bit"], | |
| "default": False, | |
| "icon": "https://downloads.raspberrypi.com/imager/icons/RPi_3.png", | |
| "description": "Raspberry Pi 3 Model A+ / B / B+ and Compute Module 3 / 3+", | |
| "matching_type": "inclusive", | |
| "capabilities": [] | |
| }, | |
| { | |
| "name": "Raspberry Pi Zero 2 W", | |
| "tags": ["pi3-64bit", "pi3-32bit"], | |
| "default": False, | |
| "icon": "https://downloads.raspberrypi.com/imager/icons/RPi_Zero_2_W.png", | |
| "description": "Raspberry Pi Zero 2 W", | |
| "matching_type": "inclusive", | |
| "capabilities": [] | |
| }, | |
| { | |
| "name": "No filtering", | |
| "tags": [], | |
| "default": False, | |
| "description": "Show every possible image", | |
| "matching_type": "inclusive", | |
| "capabilities": [] | |
| } | |
| ] | |
| }, | |
| "os_list": [ | |
| { | |
| "name": f"Internet in a Box - {os.environ.get('EDITION', 'Custom')} (64-bit)", | |
| "description": f"IIAB {os.environ.get('IIAB_BASE_VERSION', '')} {os.environ.get('EDITION', '')} edition", | |
| "icon": "https://downloads.raspberrypi.com/raspios_arm64/Raspberry_Pi_OS_(64-bit).png", | |
| "url": f"file:///home/YOUR_USERNAME/Downloads/{img_name}.zip", | |
| "image_download_size": file_size, | |
| "extract_size": file_size, | |
| "extract_sha256": extract_sha256, | |
| "release_date": datetime.date.today().isoformat(), | |
| "init_format": "cloudinit-rpi", | |
| "devices": ["pi5-64bit", "pi4-64bit", "pi3-64bit"], | |
| "capabilities": ["rpi_connect"] | |
| } | |
| ] | |
| } | |
| with open("iiab.rpi-imager-manifest", "w") as f: | |
| json.dump(manifest, f, indent=2) | |
| EOF | |
| python3 gen_manifest.py | |
| - name: Upload iiab.rpi-imager-manifest | |
| if: ${{ matrix.edition == 'tiny' }} | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: rpi-imager-manifest | |
| path: nspawn-loop/iiab.rpi-imager-manifest | |
| if-no-files-found: error | |
| retention-days: 3 | |
| compression-level: 9 | |
| - name: Upload image | |
| if: ${{ matrix.edition == 'tiny' }} | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: ${{ env.IMG_NAME }} | |
| path: nspawn-loop/${{ env.IMG_NAME }} | |
| if-no-files-found: error | |
| retention-days: 3 | |
| compression-level: 9 | |
| # - name: Compress image | |
| # working-directory: nspawn-loop | |
| # run: xz -v -9 -T0 --stdout "$IMG_NAME" > "$IMG_NAME.xz" |