diff --git a/fedac/native/Dockerfile.builder b/fedac/native/Dockerfile.builder index 67a0969ede..a239018df9 100644 --- a/fedac/native/Dockerfile.builder +++ b/fedac/native/Dockerfile.builder @@ -13,7 +13,7 @@ FROM fedora:43 RUN dnf install -y --setopt=install_weak_deps=False \ gcc make cpio lz4 bc perl flex bison diffutils \ elfutils-libelf-devel openssl-devel \ - curl jq git tar xz findutils pkgconf-pkg-config zstd \ + curl jq git tar xz findutils pkgconf-pkg-config zstd xorriso \ ca-certificates \ alsa-lib-devel libdrm-devel flite-devel \ ffmpeg-free-devel \ diff --git a/fedac/native/docker-build.sh b/fedac/native/docker-build.sh index f5404b04c2..e5b3a9143c 100644 --- a/fedac/native/docker-build.sh +++ b/fedac/native/docker-build.sh @@ -348,11 +348,74 @@ cp arch/x86/boot/bzImage "$OUT/vmlinuz" 2>/dev/null || true VMLINUZ_SIZE=$(stat -c%s "$BUILD/vmlinuz") SHA=$(sha256sum "$BUILD/vmlinuz" | awk '{print $1}') +# ══════════════════════════════════════════════ +# Step 5: Generate UEFI-bootable ISO +# ══════════════════════════════════════════════ +log "Step 5: Building ISO..." +ISO_DIR="$BUILD/iso-root" +EFI_IMG="$BUILD/efi.img" +ISO_OUT="$BUILD/ac-os.iso" + +rm -rf "$ISO_DIR" "$EFI_IMG" +mkdir -p "$ISO_DIR/EFI/BOOT" + +# Copy kernel as UEFI boot binary +cp "$BUILD/vmlinuz" "$ISO_DIR/EFI/BOOT/BOOTX64.EFI" + +# Create config.json with patchable marker (browser replaces this) +CONFIG_MARKER='{"handle":"","piece":"notepat","sub":"","email":""}' +CONFIG_PAD=4096 +printf "%-${CONFIG_PAD}s" "$CONFIG_MARKER" > "$ISO_DIR/config.json" + +# Create FAT32 EFI boot image +EFI_SIZE_MB=$(( (VMLINUZ_SIZE + CONFIG_PAD + 1048576) / 1048576 + 4 )) +dd if=/dev/zero of="$EFI_IMG" bs=1M count=$EFI_SIZE_MB 2>/dev/null +mkfs.fat -F 32 "$EFI_IMG" >/dev/null 2>&1 +mmd -i "$EFI_IMG" ::EFI ::EFI/BOOT 2>/dev/null +mcopy -i "$EFI_IMG" "$BUILD/vmlinuz" ::EFI/BOOT/BOOTX64.EFI +mcopy -i "$EFI_IMG" "$ISO_DIR/config.json" ::config.json + +# Build ISO with EFI boot support +if command -v xorriso &>/dev/null; then + xorriso -as mkisofs \ + -o "$ISO_OUT" \ + -e efi.img -no-emul-boot \ + -isohybrid-gpt-basdat \ + "$ISO_DIR" 2>/dev/null + # Embed EFI image into ISO for hybrid boot + cp "$EFI_IMG" "$ISO_DIR/efi.img" + xorriso -as mkisofs \ + -o "$ISO_OUT" \ + -e efi.img -no-emul-boot \ + "$ISO_DIR" 2>/dev/null +elif command -v genisoimage &>/dev/null; then + genisoimage -o "$ISO_OUT" \ + -efi-boot efi.img \ + -no-emul-boot \ + "$ISO_DIR" 2>/dev/null +else + # Fallback: raw disk image (dd-able) + log " No ISO tools — creating raw disk image" + cp "$EFI_IMG" "$ISO_OUT" +fi + +if [ -f "$ISO_OUT" ]; then + ISO_SIZE=$(stat -c%s "$ISO_OUT") + ISO_SHA=$(sha256sum "$ISO_OUT" | awk '{print $1}') + cp "$ISO_OUT" "$OUT/ac-os.iso" 2>/dev/null || true + log " ISO: $((ISO_SIZE / 1048576))MB (sha256: ${ISO_SHA:0:16}...)" +else + log " ISO generation failed — vmlinuz still available" +fi + log "" log "═══════════════════════════════════════════" log " Build complete: $BUILD_NAME" log " Kernel: $((VMLINUZ_SIZE / 1048576))MB" log " SHA256: $SHA" +if [ -f "$ISO_OUT" ]; then + log " ISO: $((ISO_SIZE / 1048576))MB" +fi log "═══════════════════════════════════════════" # Write metadata @@ -363,6 +426,8 @@ cat > "$OUT/build.json" << EOF "build_ts": "$BUILD_TS", "size": $VMLINUZ_SIZE, "sha256": "$SHA", + "iso_size": ${ISO_SIZE:-0}, + "iso_sha256": "${ISO_SHA:-}", "handle": "$HANDLE" } EOF diff --git a/fedac/native/scripts/upload-release.sh b/fedac/native/scripts/upload-release.sh index d05d91ace3..be142cafa2 100755 --- a/fedac/native/scripts/upload-release.sh +++ b/fedac/native/scripts/upload-release.sh @@ -3,15 +3,27 @@ # Publishes: native-notepat-latest.vmlinuz, .sha256, .version, and updates releases.json # # Usage: ./upload-release.sh [vmlinuz_path] +# ./upload-release.sh --iso [iso_path] # Upload ISO only # Credentials auto-loaded from aesthetic-computer-vault/fedac/native/upload.env set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -VMLINUZ="${1:-${SCRIPT_DIR}/../build/vmlinuz}" -if [ ! -f "$VMLINUZ" ]; then - echo "Error: vmlinuz not found at $VMLINUZ" >&2 - exit 1 +ISO_ONLY=0 +if [ "${1:-}" = "--iso" ]; then + ISO_ONLY=1 + ISO_PATH="${2:-${SCRIPT_DIR}/../build/ac-os.iso}" + if [ ! -f "$ISO_PATH" ]; then + echo "Error: ISO not found at $ISO_PATH" >&2 + exit 1 + fi + VMLINUZ="/dev/null" # not used but needed for cred loading below +else + VMLINUZ="${1:-${SCRIPT_DIR}/../build/vmlinuz}" + if [ ! -f "$VMLINUZ" ]; then + echo "Error: vmlinuz not found at $VMLINUZ" >&2 + exit 1 + fi fi # Credentials — check env (set by ac-os), session cache, plaintext vault, or GPG decrypt @@ -164,7 +176,23 @@ if command -v node &>/dev/null; then | node "$SCRIPT_DIR/track-build.mjs" record 2>&1 || true fi +# Upload ISO if available (from --iso flag or alongside vmlinuz) +if [ "$ISO_ONLY" = "1" ]; then + echo "Uploading ISO: $(du -sh "$ISO_PATH" | cut -f1)" + do_upload "$ISO_PATH" "os/${CHANNEL_PREFIX}native-notepat-latest.iso" "application/octet-stream" + echo "ISO published: ${BASE_URL}/os/${CHANNEL_PREFIX}native-notepat-latest.iso" + exit 0 +fi + +# Also upload ISO if it exists next to vmlinuz +ISO_SIBLING="$(dirname "$VMLINUZ")/ac-os.iso" +if [ -f "$ISO_SIBLING" ]; then + echo " Uploading ISO ($(du -sh "$ISO_SIBLING" | cut -f1))..." + do_upload "$ISO_SIBLING" "os/${CHANNEL_PREFIX}native-notepat-latest.iso" "application/octet-stream" +fi + echo "" echo "Release published: $BUILD_NAME ($FULL_VERSION)" -echo " ${BASE_URL}/os/native-notepat-latest.vmlinuz" -echo " ${BASE_URL}/os/releases.json" +echo " ${BASE_URL}/os/${CHANNEL_PREFIX}native-notepat-latest.vmlinuz" +echo " ${BASE_URL}/os/${CHANNEL_PREFIX}releases.json" +[ -f "$ISO_SIBLING" ] && echo " ${BASE_URL}/os/${CHANNEL_PREFIX}native-notepat-latest.iso" diff --git a/oven/native-builder.mjs b/oven/native-builder.mjs index 604965c597..b529217825 100644 --- a/oven/native-builder.mjs +++ b/oven/native-builder.mjs @@ -204,26 +204,38 @@ async function runBuildJob(job) { job.percent = 75; - // Phase 3: Extract vmlinuz from container - addLogLine(job, "stdout", "Phase 3: Extracting kernel..."); + // Phase 3: Extract vmlinuz + ISO from container + addLogLine(job, "stdout", "Phase 3: Extracting kernel + ISO..."); const cid = (await fs.readFile(cidFile, "utf8")).trim(); + const isoOut = `/tmp/oven-iso-${job.id}`; await runPhase(job, "extract", "bash", ["-c", - `docker cp ${cid}:/tmp/ac-build/vmlinuz ${vmlinuzOut} && docker rm ${cid} >/dev/null` + `docker cp ${cid}:/tmp/ac-build/vmlinuz ${vmlinuzOut} && docker cp ${cid}:/tmp/ac-build/ac-os.iso ${isoOut} 2>/dev/null; docker rm ${cid} >/dev/null` ], repoDir); job.percent = 80; - // Phase 4: Upload vmlinuz to DO Spaces CDN + // Phase 4: Upload vmlinuz + ISO to DO Spaces CDN addLogLine(job, "stdout", "Phase 4: Uploading to CDN..."); const uploadScript = path.join(NATIVE_DIR, "scripts/upload-release.sh"); - await runPhase(job, "upload", "bash", [uploadScript, vmlinuzOut], NATIVE_DIR, { + const uploadEnv = { DO_SPACES_KEY: process.env.DO_SPACES_KEY || process.env.ART_SPACES_KEY || "", DO_SPACES_SECRET: process.env.DO_SPACES_SECRET || process.env.ART_SPACES_SECRET || "", - }); + }; + await runPhase(job, "upload", "bash", [uploadScript, vmlinuzOut], NATIVE_DIR, uploadEnv); + + // Upload ISO if it was generated + try { + await fs.access(isoOut); + addLogLine(job, "stdout", " Uploading ISO..."); + await runPhase(job, "upload-iso", "bash", ["-c", + `${uploadScript} --iso ${isoOut}` + ], NATIVE_DIR, uploadEnv); + } catch { addLogLine(job, "stdout", " No ISO generated — skipping"); } // Cleanup C build try { await fs.unlink(vmlinuzOut); } catch {} + try { await fs.unlink(isoOut); } catch {} try { await fs.unlink(cidFile); } catch {} job.percent = 85;