#!/usr/bin/env bash # Build dangerous-grade UC26 image with seeded system-user (no Ubuntu One at first boot). set -euo pipefail UC_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" ENV_FILE="${UC_ROOT}/config/dev-image.env" MODEL="${UC_ROOT}/models/salmanoff-dev-amd64.model" SYSTEM_USER_ASSERT="${UC_ROOT}/assertions/smo-system-user.assert" WORKDIR="${UC_ROOT}/work/ubuntu-image-dev" OUTPUT_DIR="${UC_ROOT}/artifacts/images-dev" LOG_DIR="${UC_ROOT}/artifacts/logs" usage() { cat <<'EOF' Usage: build-dev-image.sh [OPTIONS] Build a dangerous-grade ubuntu-core dev image with: - custom model (salmanoff-dev-amd64) - system-user assertion seeded (SSH login as smo, no Ubuntu One) Prerequisites: scripts/setup-dev-signing.sh (once: Snap Store login + signing key) scripts/sign-dev-assertions.sh (sign model + system-user) Options: --snap PATH Extra snap to preinstall (repeatable). Requires grade dangerous. --fresh-workdir Remove work/ubuntu-image-dev before building --resume Pass --resume to ubuntu-image --no-sign Skip sign-dev-assertions.sh (use existing assertions) -h, --help Show this help Outputs: artifacts/images-dev/pc.img artifacts/logs/build-dev-.log After first boot in QEMU: ssh -i config/ssh/smo-dev smo@localhost -p 8022 EOF } fresh_workdir=false resume=false no_sign=false extra_snaps=() while [[ $# -gt 0 ]]; do case "$1" in --snap) extra_snaps+=("$2"); shift 2 ;; --fresh-workdir) fresh_workdir=true; shift ;; --resume) resume=true; shift ;; --no-sign) no_sign=true; shift ;; -h|--help) usage; exit 0 ;; *) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;; esac done if [[ "$no_sign" == false ]]; then "${UC_ROOT}/scripts/sign-dev-assertions.sh" fi if [[ ! -f "$MODEL" ]]; then echo "Missing signed model: $MODEL" >&2 exit 1 fi if [[ ! -f "$SYSTEM_USER_ASSERT" ]]; then echo "Missing system-user assertion: $SYSTEM_USER_ASSERT" >&2 exit 1 fi if ! command -v ubuntu-image >/dev/null 2>&1; then echo "ubuntu-image not found. Install with: sudo snap install ubuntu-image --classic" >&2 exit 1 fi mkdir -p "$OUTPUT_DIR" "$LOG_DIR" if [[ "$fresh_workdir" == true ]] || [[ "$resume" == false ]]; then if [[ -d "$WORKDIR" ]]; then echo "Removing workdir: $WORKDIR" rm -rf "$WORKDIR" fi fi mkdir -p "$WORKDIR" timestamp="$(date +%Y%m%d-%H%M%S)" log_file="${LOG_DIR}/build-dev-${timestamp}.log" cmd=(ubuntu-image snap --workdir "$WORKDIR" --output-dir "$OUTPUT_DIR" --image-size 4G --assertion "$SYSTEM_USER_ASSERT" "$MODEL") for snap_path in "${extra_snaps[@]}"; do if [[ ! -f "$snap_path" ]]; then echo "Snap not found: $snap_path" >&2 exit 1 fi cmd+=(--snap "$snap_path") done if [[ "$resume" == true ]]; then cmd+=(--resume) fi echo "Model: $MODEL" echo "System user: $SYSTEM_USER_ASSERT" echo "Workdir: $WORKDIR" echo "Output dir: $OUTPUT_DIR" echo "Log: $log_file" if [[ ${#extra_snaps[@]} -gt 0 ]]; then echo "Extra snaps: ${extra_snaps[*]}" fi echo "Running: ${cmd[*]}" "${cmd[@]}" 2>&1 | tee "$log_file" img="${OUTPUT_DIR}/pc.img" if [[ ! -f "$img" ]]; then echo "Build finished but $img not found" >&2 exit 1 fi echo "" echo "Done. Image: $img" if [[ -f "$ENV_FILE" ]]; then # shellcheck source=/dev/null source "$ENV_FILE" echo "SSH: ssh -i ${UC_ROOT}/config/ssh/smo-dev ${SYSTEM_USER_NAME:-smo}@localhost -p 8022" fi ls -lh "$img" "${img}.seed.manifest" 2>/dev/null || ls -lh "$img"