From 3a8f2d3626c170b7e2e365bc3f3f1190a64ff8b2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 03:48:49 +0000 Subject: [PATCH 01/12] Initial plan From ab89669ec8f277aedf2761d3f8502ce1efc4798b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 04:00:23 +0000 Subject: [PATCH 02/12] Update loader entries to use UKI format with efi directive Co-authored-by: danielbodnar <1790726+danielbodnar@users.noreply.github.com> --- .github/workflows/build.yml | 188 +++++++++++ .github/workflows/release.yml | 160 ++++++++++ CLAUDE.md | 140 ++++++++ Containerfile | 69 ++++ Makefile | 133 ++++++++ README.md | 286 ++++++++++++++++- config/cmdline.d/base.conf | 16 + config/cmdline.d/network.conf | 15 + config/cmdline.d/profiles/alpine-netboot.conf | 8 + .../profiles/alpine-zfs-installer.conf | 8 + config/cmdline.d/profiles/bitboot.conf | 11 + config/cmdline.d/profiles/fedora-coreos.conf | 9 + config/cmdline.d/profiles/flatcar.conf | 9 + config/cmdline.d/profiles/nixos-kexec.conf | 7 + config/cmdline.d/profiles/vyos.conf | 8 + config/loader/entries/alpine-netboot.conf | 11 + .../loader/entries/alpine-zfs-installer.conf | 17 + config/loader/entries/bitboot.conf | 14 + config/loader/entries/fedora-coreos.conf | 11 + config/loader/entries/flatcar.conf | 11 + config/loader/entries/netboot.conf | 16 + config/loader/entries/nixos-kexec.conf | 15 + config/loader/entries/vyos.conf | 13 + config/loader/entries/zfsbootmenu.conf | 14 + config/loader/loader.conf | 22 ++ initramfs/config.yaml | 140 ++++++++ initramfs/hooks/bitboot-network-pull.sh | 72 +++++ initramfs/hooks/bitboot-zfs.sh | 71 +++++ initramfs/modules/bitboot-pull-premount.sh | 30 ++ initramfs/modules/bitboot-pull.sh | 99 ++++++ scripts/build-initramfs.sh | 212 +++++++++++++ scripts/build-uki.sh | 298 ++++++++++++++++++ scripts/create-usb.sh | 255 +++++++++++++++ scripts/fetch-deps.sh | 210 ++++++++++++ 34 files changed, 2597 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/release.yml create mode 100644 CLAUDE.md create mode 100644 Containerfile create mode 100644 Makefile create mode 100644 config/cmdline.d/base.conf create mode 100644 config/cmdline.d/network.conf create mode 100644 config/cmdline.d/profiles/alpine-netboot.conf create mode 100644 config/cmdline.d/profiles/alpine-zfs-installer.conf create mode 100644 config/cmdline.d/profiles/bitboot.conf create mode 100644 config/cmdline.d/profiles/fedora-coreos.conf create mode 100644 config/cmdline.d/profiles/flatcar.conf create mode 100644 config/cmdline.d/profiles/nixos-kexec.conf create mode 100644 config/cmdline.d/profiles/vyos.conf create mode 100644 config/loader/entries/alpine-netboot.conf create mode 100644 config/loader/entries/alpine-zfs-installer.conf create mode 100644 config/loader/entries/bitboot.conf create mode 100644 config/loader/entries/fedora-coreos.conf create mode 100644 config/loader/entries/flatcar.conf create mode 100644 config/loader/entries/netboot.conf create mode 100644 config/loader/entries/nixos-kexec.conf create mode 100644 config/loader/entries/vyos.conf create mode 100644 config/loader/entries/zfsbootmenu.conf create mode 100644 config/loader/loader.conf create mode 100644 initramfs/config.yaml create mode 100644 initramfs/hooks/bitboot-network-pull.sh create mode 100644 initramfs/hooks/bitboot-zfs.sh create mode 100644 initramfs/modules/bitboot-pull-premount.sh create mode 100644 initramfs/modules/bitboot-pull.sh create mode 100644 scripts/build-initramfs.sh create mode 100644 scripts/build-uki.sh create mode 100644 scripts/create-usb.sh create mode 100644 scripts/fetch-deps.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..3d3b01d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,188 @@ +# BitBoot Build Workflow +# Main CI/CD pipeline for building the BitBoot multiboot USB system +# +# Triggers: +# - Push to main branch +# - Pull requests to main branch +# - Manual workflow dispatch +# +# Outputs: +# - bitboot.efi (Unified Kernel Image) +# - bitboot-x86_64.img (Bootable USB image) + +name: Build BitBoot + +on: + push: + branches: [main] + paths-ignore: + - '**.md' + - 'LICENSE' + - '.gitignore' + pull_request: + branches: [main] + workflow_dispatch: + inputs: + sign_uki: + description: 'Sign UKI for Secure Boot' + required: false + default: false + type: boolean + +env: + BUILD_DIR: build + +jobs: + build: + name: Build BitBoot + runs-on: ubuntu-latest + container: + image: registry.fedoraproject.org/fedora:41 + options: --privileged + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install build dependencies + run: | + dnf install -y \ + make \ + gcc \ + binutils \ + systemd-boot-unsigned \ + systemd-ukify \ + dracut \ + parted \ + gdisk \ + dosfstools \ + e2fsprogs \ + xz \ + zstd \ + gzip \ + bzip2 \ + curl \ + wget \ + kernel \ + kernel-devel \ + linux-firmware \ + util-linux \ + coreutils \ + findutils \ + file + + - name: Verify systemd version + run: | + echo "Checking systemd version (need v258+)..." + systemctl --version | head -1 + SYSTEMD_VER=$(systemctl --version | head -1 | awk '{print $2}') + echo "systemd version: ${SYSTEMD_VER}" + + - name: Make scripts executable + run: chmod +x scripts/*.sh + + - name: Fetch dependencies + run: make deps + env: + BUILD_DIR: ${{ env.BUILD_DIR }} + + - name: Build initramfs + run: make initramfs + env: + BUILD_DIR: ${{ env.BUILD_DIR }} + + - name: Build UKI + run: make uki + env: + BUILD_DIR: ${{ env.BUILD_DIR }} + + - name: Create USB image + run: make usb + env: + BUILD_DIR: ${{ env.BUILD_DIR }} + + - name: Verify build outputs + run: | + echo "=== Build Artifacts ===" + ls -la ${BUILD_DIR}/ + echo "" + echo "=== UKI Directory ===" + ls -la ${BUILD_DIR}/efi/ || true + echo "" + echo "=== UKI Info ===" + for uki in ${BUILD_DIR}/efi/*.efi; do + if [[ -f "${uki}" ]]; then + echo "--- $(basename ${uki}) ---" + file "${uki}" || true + ls -lh "${uki}" || true + fi + done + echo "" + echo "=== USB Image Info ===" + file ${BUILD_DIR}/bitboot-x86_64.img || true + ls -lh ${BUILD_DIR}/bitboot-x86_64.img || true + echo "" + echo "=== Dependencies ===" + find ${BUILD_DIR}/deps -type f -exec ls -lh {} \; || true + + - name: Upload UKI artifacts + uses: actions/upload-artifact@v4 + with: + name: bitboot-ukis + path: ${{ env.BUILD_DIR }}/efi/ + if-no-files-found: warn + + - name: Upload USB image artifact + uses: actions/upload-artifact@v4 + with: + name: bitboot-usb-image + path: ${{ env.BUILD_DIR }}/bitboot-x86_64.img + if-no-files-found: warn + compression-level: 9 + + - name: Upload dependencies artifact + uses: actions/upload-artifact@v4 + with: + name: bitboot-deps + path: ${{ env.BUILD_DIR }}/deps/ + if-no-files-found: warn + + # Validation job to check boot entries and configurations + validate: + name: Validate Configuration + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Validate loader configuration + run: | + echo "=== Checking loader.conf ===" + cat config/loader/loader.conf + echo "" + + echo "=== Checking boot entries ===" + for entry in config/loader/entries/*.conf; do + echo "--- ${entry} ---" + cat "${entry}" + echo "" + done + + - name: Validate kernel cmdline fragments + run: | + echo "=== Kernel command line fragments ===" + for conf in config/cmdline.d/*.conf; do + echo "--- ${conf} ---" + cat "${conf}" + echo "" + done + + - name: Check for shell script issues + run: | + if command -v shellcheck >/dev/null 2>&1; then + echo "Running shellcheck..." + shellcheck scripts/*.sh || true + else + echo "shellcheck not installed, skipping..." + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a016037 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,160 @@ +# BitBoot Release Workflow +# Automated release creation for tagged versions +# +# Triggers: +# - Push of version tags (v*) +# +# Actions: +# - Builds BitBoot UKI and USB image +# - Creates GitHub release with artifacts +# - Generates release notes + +name: Release BitBoot + +on: + push: + tags: + - 'v*' + +env: + BUILD_DIR: build + +permissions: + contents: write + +jobs: + release: + name: Create Release + runs-on: ubuntu-latest + container: + image: registry.fedoraproject.org/fedora:41 + options: --privileged + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install build dependencies + run: | + dnf install -y \ + make \ + gcc \ + binutils \ + systemd-boot-unsigned \ + systemd-ukify \ + dracut \ + parted \ + gdisk \ + dosfstools \ + e2fsprogs \ + xz \ + zstd \ + gzip \ + bzip2 \ + curl \ + wget \ + kernel \ + kernel-devel \ + linux-firmware \ + util-linux \ + coreutils \ + findutils \ + file \ + git + + - name: Get version info + id: version + run: | + VERSION="${GITHUB_REF#refs/tags/}" + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "Building BitBoot ${VERSION}" + + - name: Make scripts executable + run: chmod +x scripts/*.sh + + - name: Build all artifacts + run: make all + env: + BUILD_DIR: ${{ env.BUILD_DIR }} + + - name: Compress USB image + run: | + cd ${BUILD_DIR} + xz -9 -k bitboot-x86_64.img + # Create checksums for all artifacts + sha256sum efi/*.efi bitboot-x86_64.img bitboot-x86_64.img.xz > SHA256SUMS + + - name: Generate release notes + id: release_notes + run: | + VERSION="${{ steps.version.outputs.version }}" + cat > release_notes.md << EOF + # BitBoot ${VERSION} + + ## What's New + + This release includes the latest BitBoot multiboot USB system with support for: + + - **rd.systemd.pull** - Boot raw disk images directly from HTTP/HTTPS + - **ZFSBootMenu** - ZFS boot environment management + - **netboot.xyz** - iPXE network booting + - **Alpine Linux ZFS Installer** - For Root-on-ZFS installations + + ## Included Boot Profiles + + - Fedora CoreOS (Network Pull) + - Flatcar Container Linux (Network Pull) + - Alpine Linux ZFS Installer + - Alpine Linux Netboot + - NixOS Kexec Installer + - VyOS 1.5 Rolling + - ZFSBootMenu Recovery + - netboot.xyz + - BitBoot Recovery Shell + + ## Installation + + ### Quick Start + + \`\`\`bash + # Download the compressed USB image + wget https://github.com/\${GITHUB_REPOSITORY}/releases/download/${VERSION}/bitboot-x86_64.img.xz + + # Decompress and write to USB (replace /dev/sdX with your USB device) + xzcat bitboot-x86_64.img.xz | sudo dd of=/dev/sdX bs=4M status=progress conv=fsync + \`\`\` + + ### Verify Downloads + + \`\`\`bash + sha256sum -c SHA256SUMS + \`\`\` + + ## System Requirements + + - UEFI-capable system (64-bit x86_64) + - 512MB+ USB drive + - Network connection (for rd.systemd.pull profiles) + - Minimum 4GB RAM recommended for network booting + + ## Documentation + + See the [README](https://github.com/\${GITHUB_REPOSITORY}#readme) for detailed usage instructions. + EOF + cat release_notes.md + + - name: Create Release + uses: softprops/action-gh-release@v2 + with: + name: BitBoot ${{ steps.version.outputs.version }} + body_path: release_notes.md + draft: false + prerelease: ${{ contains(steps.version.outputs.version, 'alpha') || contains(steps.version.outputs.version, 'beta') || contains(steps.version.outputs.version, 'rc') }} + files: | + ${{ env.BUILD_DIR }}/efi/*.efi + ${{ env.BUILD_DIR }}/bitboot-x86_64.img.xz + ${{ env.BUILD_DIR }}/SHA256SUMS + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..aa84252 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,140 @@ +# CLAUDE.md - AI Assistant Instructions for BitBoot + +This document provides context and guidance for AI assistants working with the BitBoot repository. + +## Project Overview + +BitBoot is a multiboot USB system that produces unified kernel images (UKIs) serving as meta-bootloaders. It combines ZFSBootMenu, netboot.xyz, and systemd-boot into portable EFI binaries with the capability to pull and boot raw disk images directly from HTTP/HTTPS sources at boot time using systemd v258's `rd.systemd.pull` functionality. + +## Key Concepts + +### Unified Kernel Images (UKI) +- Each boot profile has its own `.efi` file in `/EFI/bitboot/` +- UKIs embed: kernel, initramfs, command line, and OS release info +- Built using `ukify` (preferred) or `objcopy` (fallback) +- Boot entries reference UKIs with `efi` directive, NOT `linux`/`initrd`/`options` + +### rd.systemd.pull +- Core innovation leveraging systemd v258's import generator +- Downloads raw disk images over HTTP/HTTPS at boot time +- Format: `rd.systemd.pull=raw,machine,verify=no,blockdev::` +- Images are loopback-mounted before pivot root + +### Boot Flow +1. UEFI loads systemd-boot from ESP +2. User selects boot entry from menu +3. Selected UKI is executed +4. Embedded initramfs sets up network +5. `rd.systemd.pull` downloads image (if configured) +6. Root filesystem mounted (from download or local) +7. Pivot root to target OS + +## Repository Structure + +``` +bitboot/ +├── .github/workflows/ # CI/CD pipelines +├── config/ +│ ├── cmdline.d/ # Base kernel cmdline fragments +│ │ └── profiles/ # Per-profile cmdline (embedded in UKIs) +│ └── loader/ # systemd-boot configuration +│ ├── loader.conf +│ └── entries/ # Boot menu entries (reference UKIs) +├── initramfs/ # Initramfs configuration and hooks +├── scripts/ # Build scripts +├── Containerfile # Build environment +└── Makefile # Build orchestration +``` + +## Important Patterns + +### Boot Entry Format (CORRECT) +```ini +title Profile Name +efi /EFI/bitboot/profile-name.efi +``` + +### Boot Entry Format (WRONG - Do NOT use) +```ini +title Profile Name +linux /vmlinuz-... +initrd /initramfs-... +options ... +``` + +### Profile Command Lines +- Located in `config/cmdline.d/profiles/.conf` +- One parameter per line (easier to read/edit) +- Comments with `#` are stripped during build +- Embedded into corresponding UKI at build time + +## Build System + +### Local Build +```bash +make all # Build everything +make deps # Fetch ZFSBootMenu, netboot.xyz, Alpine +make initramfs # Build custom initramfs +make uki # Build all profile UKIs +make usb # Create bootable USB image +``` + +### Container Build +```bash +make container # Build in Fedora 41 container +``` + +### Single Profile Build +```bash +./scripts/build-uki.sh --profile fedora-coreos +``` + +## Supported Profiles + +| Profile | Type | Notes | +|---------|------|-------| +| fedora-coreos | rd.systemd.pull | DDI raw image | +| flatcar | rd.systemd.pull | Container Linux | +| alpine-zfs-installer | Netboot | ZFS installation environment | +| alpine-netboot | Netboot | Lightweight RAM-based | +| nixos-kexec | Kexec | Unique boot model | +| vyos | Netboot+squashfs | Router OS | +| zfsbootmenu | Chainload | ZFS BE management | +| netboot | Chainload | iPXE network boot | +| bitboot | Recovery | Shell with ZFS support | + +## Common Tasks + +### Adding a New Profile +1. Create cmdline file: `config/cmdline.d/profiles/.conf` +2. Create boot entry: `config/loader/entries/.conf` +3. Run `make uki` to build the new UKI + +### Updating Image URLs +1. Edit the profile's cmdline file in `config/cmdline.d/profiles/` +2. Rebuild with `make uki` + +### Testing Locally +1. Build USB image: `make usb` +2. Write to USB: `dd if=build/bitboot-x86_64.img of=/dev/sdX bs=4M` +3. Boot from USB on test machine + +## Dependencies + +- systemd v258+ (for rd.systemd.pull) +- ukify or objcopy (for UKI creation) +- dracut (for initramfs) +- parted, gdisk, dosfstools (for USB image) +- curl/wget (for fetching deps) + +## Testing Notes + +- UKIs can be tested in QEMU with OVMF +- Network boot profiles require internet connectivity +- ZFS profiles require ZFS kernel modules + +## Security Considerations + +- Default profiles use `verify=no` for simplicity +- Production deployments should use `verify=signature` with proper GPG keys +- Secure Boot signing available via `--sign` option diff --git a/Containerfile b/Containerfile new file mode 100644 index 0000000..936826b --- /dev/null +++ b/Containerfile @@ -0,0 +1,69 @@ +# BitBoot Build Container +# Provides a reproducible build environment with systemd v258+ and all required tools +# +# Build: podman build -t bitboot-builder . +# Run: podman run --rm -v $(pwd):/workspace:Z bitboot-builder make all + +FROM registry.fedoraproject.org/fedora:41 + +LABEL maintainer="BitBoot Project" +LABEL description="Build environment for BitBoot multiboot USB system" + +# Install build dependencies +RUN dnf install -y \ + # Core build tools + make \ + gcc \ + binutils \ + # UEFI/UKI tools + systemd-boot-unsigned \ + systemd-ukify \ + sbsigntools \ + # Initramfs tools + dracut \ + # Disk/partition tools + parted \ + gdisk \ + dosfstools \ + e2fsprogs \ + xfsprogs \ + # Compression tools + xz \ + zstd \ + gzip \ + bzip2 \ + lz4 \ + # Network tools + curl \ + wget \ + # ZFS (from RPM Fusion) + && dnf install -y \ + https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-41.noarch.rpm \ + && dnf install -y zfs || true \ + # Kernel and firmware + && dnf install -y \ + kernel \ + kernel-devel \ + linux-firmware \ + # Utilities + && dnf install -y \ + util-linux \ + coreutils \ + findutils \ + grep \ + sed \ + gawk \ + file \ + tree \ + # Cleanup + && dnf clean all \ + && rm -rf /var/cache/dnf + +# Verify systemd version is 258+ +RUN systemctl --version | head -1 + +# Set working directory +WORKDIR /workspace + +# Default command +CMD ["make", "all"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..660aee6 --- /dev/null +++ b/Makefile @@ -0,0 +1,133 @@ +# BitBoot Makefile +# Build orchestration for the BitBoot multiboot USB system +# +# Usage: +# make all - Build everything (deps, initramfs, uki, usb image) +# make deps - Fetch external dependencies +# make initramfs - Build custom initramfs +# make uki - Build unified kernel image +# make usb - Create bootable USB image +# make clean - Clean build artifacts +# make container - Build using container +# +# Variables: +# BUILD_DIR - Build output directory (default: build) +# SIGN - Enable Secure Boot signing (default: false) +# SIGN_KEY - Secure Boot private key path +# SIGN_CERT - Secure Boot certificate path + +.PHONY: all deps initramfs uki usb clean container help + +# Configuration +BUILD_DIR ?= build +SIGN ?= false +SIGN_KEY ?= +SIGN_CERT ?= +CONTAINER_IMAGE ?= bitboot-builder + +# Script paths +SCRIPTS_DIR := scripts +FETCH_DEPS := $(SCRIPTS_DIR)/fetch-deps.sh +BUILD_INITRAMFS := $(SCRIPTS_DIR)/build-initramfs.sh +BUILD_UKI := $(SCRIPTS_DIR)/build-uki.sh +CREATE_USB := $(SCRIPTS_DIR)/create-usb.sh + +# Output paths +DEPS_DIR := $(BUILD_DIR)/deps +INITRAMFS := $(BUILD_DIR)/initramfs-bitboot.img +UKI_DIR := $(BUILD_DIR)/efi +UKI := $(UKI_DIR)/bitboot.efi +USB_IMG := $(BUILD_DIR)/bitboot-x86_64.img + +# Default target +all: deps initramfs uki usb + +# Help target +help: + @echo "BitBoot Build System" + @echo "" + @echo "Targets:" + @echo " all - Build everything (default)" + @echo " deps - Fetch external dependencies (ZFSBootMenu, netboot.xyz, Alpine)" + @echo " initramfs - Build custom initramfs with rd.systemd.pull support" + @echo " uki - Build unified kernel image (bitboot.efi)" + @echo " usb - Create bootable USB image" + @echo " clean - Clean build artifacts" + @echo " container - Build using container (requires podman/docker)" + @echo " help - Show this help message" + @echo "" + @echo "Variables:" + @echo " BUILD_DIR=$(BUILD_DIR)" + @echo " SIGN=$(SIGN)" + @echo " SIGN_KEY=$(SIGN_KEY)" + @echo " SIGN_CERT=$(SIGN_CERT)" + +# Create build directory +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +# Fetch dependencies +deps: $(BUILD_DIR) + @echo "==> Fetching dependencies..." + bash $(FETCH_DEPS) --output $(DEPS_DIR) + +$(DEPS_DIR)/zbm/zfsbootmenu.efi: deps + +$(DEPS_DIR)/netboot/netboot.xyz.efi: deps + +$(DEPS_DIR)/alpine/vmlinuz-lts: deps + +# Build initramfs +initramfs: $(BUILD_DIR) + @echo "==> Building initramfs..." + bash $(BUILD_INITRAMFS) --output $(INITRAMFS) + +$(INITRAMFS): initramfs + +# Build UKI +uki: $(INITRAMFS) + @echo "==> Building unified kernel image..." +ifeq ($(SIGN),true) + bash $(BUILD_UKI) --output $(BUILD_DIR) --initrd $(INITRAMFS) --sign --key $(SIGN_KEY) --cert $(SIGN_CERT) +else + bash $(BUILD_UKI) --output $(BUILD_DIR) --initrd $(INITRAMFS) +endif + +$(UKI): uki + +# Create USB image +usb: $(UKI) deps + @echo "==> Creating USB image..." + bash $(CREATE_USB) --output $(USB_IMG) --uki $(UKI) --deps $(DEPS_DIR) + +$(USB_IMG): usb + +# Build using container +container: + @echo "==> Building container image..." + podman build -t $(CONTAINER_IMAGE) . || docker build -t $(CONTAINER_IMAGE) . + @echo "==> Running build in container..." + podman run --rm --privileged -v $(PWD):/workspace:Z $(CONTAINER_IMAGE) make all || \ + docker run --rm --privileged -v $(PWD):/workspace $(CONTAINER_IMAGE) make all + +# Clean build artifacts +clean: + @echo "==> Cleaning build artifacts..." + rm -rf $(BUILD_DIR) + +# Development targets +.PHONY: lint check + +lint: + @echo "==> Checking shell scripts..." + shellcheck $(SCRIPTS_DIR)/*.sh || true + +check: + @echo "==> Verifying build outputs..." + @test -d $(UKI_DIR) && echo "UKI Directory: OK" || echo "UKI Directory: MISSING" + @test -f $(UKI) && echo "BitBoot UKI: OK" || echo "BitBoot UKI: MISSING" + @test -f $(USB_IMG) && echo "USB Image: OK" || echo "USB Image: MISSING" + @test -f $(DEPS_DIR)/zbm/zfsbootmenu.efi && echo "ZFSBootMenu: OK" || echo "ZFSBootMenu: MISSING" + @test -f $(DEPS_DIR)/netboot/netboot.xyz.efi && echo "netboot.xyz: OK" || echo "netboot.xyz: MISSING" + @echo "==> All UKIs:" + @ls -la $(UKI_DIR)/*.efi 2>/dev/null || echo "No UKIs found" diff --git a/README.md b/README.md index a0f3961..e0b7531 100644 --- a/README.md +++ b/README.md @@ -1 +1,285 @@ -# bitboot \ No newline at end of file +# BitBoot + +**Multiboot USB System with Network Image Pull Support** + +BitBoot is a revolutionary boot system that creates unified kernel images (UKIs) capable of booting raw disk images directly from HTTP/HTTPS sources. Built on systemd v258's `rd.systemd.pull` functionality, it combines ZFSBootMenu, netboot.xyz, and systemd-boot into a single portable USB drive. + +## Features + +- 🌐 **Network Boot**: Pull and boot raw disk images from HTTP/HTTPS at boot time +- 📀 **Multiple Profiles**: Pre-configured entries for Fedora CoreOS, Flatcar, Alpine, NixOS, VyOS +- 🔧 **ZFSBootMenu Integration**: Full ZFS recovery and boot environment management +- 🖧 **netboot.xyz**: Access to dozens of network-bootable operating systems +- 🔒 **Secure Boot Ready**: Optional signing for Secure Boot compatibility +- 📦 **Unified Kernel Images**: Single EFI files with embedded kernel, initramfs, and cmdline + +## Quick Start + +### Download Pre-built Image + +```bash +# Download latest release +wget https://github.com/bitbuilder-io/bitboot/releases/latest/download/bitboot-x86_64.img.xz + +# Write to USB (replace /dev/sdX with your USB device) +xzcat bitboot-x86_64.img.xz | sudo dd of=/dev/sdX bs=4M status=progress conv=fsync +``` + +### Build from Source + +```bash +# Clone repository +git clone https://github.com/bitbuilder-io/bitboot.git +cd bitboot + +# Build everything +make all + +# Or build in container (recommended) +make container +``` + +## Boot Profiles + +BitBoot includes pre-configured profiles for various operating systems: + +### Tier 1: Network Pull (rd.systemd.pull) + +| Profile | Description | +|---------|-------------| +| **Fedora CoreOS** | Container-optimized OS with GPT auto-discovery | +| **Flatcar Container Linux** | Production-ready container host | + +### Tier 2: Network Boot + +| Profile | Description | +|---------|-------------| +| **Alpine Linux ZFS Installer** | ZFS-capable installation environment | +| **Alpine Linux Netboot** | Lightweight RAM-based Linux | +| **NixOS Kexec Installer** | Declarative Linux installer | +| **VyOS 1.5 Rolling** | Network router OS | + +### Tier 3: Chainload + +| Profile | Description | +|---------|-------------| +| **ZFSBootMenu** | ZFS boot environment manager | +| **netboot.xyz** | iPXE network boot menu | +| **BitBoot Recovery** | Emergency shell with ZFS tools | + +## How It Works + +### Boot Flow + +``` +UEFI → systemd-boot → Select Profile → Load UKI → Network Setup → + → rd.systemd.pull (download image) → Loopback Mount → Pivot Root → Boot OS +``` + +### Key Technology: rd.systemd.pull + +Introduced in systemd v258, `rd.systemd.pull` enables downloading and booting from raw disk images: + +``` +rd.systemd.pull=raw,machine,verify=no,blockdev:: +``` + +- Downloads image to `/run/machines/` +- Attaches to loopback device +- Supports xz, zstd, gzip, bzip2 compression +- GPT auto-discovery for partition detection + +## Repository Structure + +``` +bitboot/ +├── .github/workflows/ # CI/CD pipelines +│ ├── build.yml # Build on push/PR +│ └── release.yml # Tagged releases +├── config/ +│ ├── cmdline.d/ # Kernel command line +│ │ ├── base.conf # Common options +│ │ ├── network.conf # Network prerequisites +│ │ └── profiles/ # Per-profile cmdlines +│ └── loader/ +│ ├── loader.conf # systemd-boot config +│ └── entries/ # Boot menu entries +├── initramfs/ +│ ├── config.yaml # Initramfs configuration +│ ├── hooks/ # Dracut hooks +│ └── modules/ # Custom modules +├── scripts/ +│ ├── build-uki.sh # UKI assembly +│ ├── build-initramfs.sh # Initramfs generation +│ ├── fetch-deps.sh # Download dependencies +│ └── create-usb.sh # USB image creation +├── Containerfile # Build environment +├── Makefile # Build orchestration +├── CLAUDE.md # AI assistant instructions +└── README.md # This file +``` + +## USB Image Layout + +``` +ESP (FAT32)/ +├── EFI/ +│ ├── BOOT/ +│ │ └── BOOTX64.EFI # Default boot (bitboot.efi) +│ ├── bitboot/ +│ │ ├── bitboot.efi # Recovery shell +│ │ ├── fedora-coreos.efi +│ │ ├── flatcar.efi +│ │ ├── alpine-zfs-installer.efi +│ │ └── ... +│ ├── zbm/ +│ │ └── zfsbootmenu.efi +│ └── netboot/ +│ └── netboot.xyz.efi +├── loader/ +│ ├── loader.conf +│ └── entries/ +│ ├── fedora-coreos.conf +│ ├── zfsbootmenu.conf +│ └── ... +└── alpine/ # Alpine netboot files +``` + +## Building + +### Prerequisites + +- Linux system with systemd v258+ +- `ukify` or `objcopy` for UKI creation +- `dracut` for initramfs generation +- `parted`, `gdisk`, `dosfstools` for USB image +- `curl` or `wget` for downloading dependencies + +### Make Targets + +```bash +make all # Build everything +make deps # Fetch ZFSBootMenu, netboot.xyz, Alpine +make initramfs # Build custom initramfs +make uki # Build all profile UKIs +make usb # Create bootable USB image +make clean # Remove build artifacts +make container # Build using container +make check # Verify build outputs +``` + +### Environment Variables + +```bash +BUILD_DIR=build # Output directory +SIGN=false # Enable Secure Boot signing +SIGN_KEY= # Signing key path +SIGN_CERT= # Signing certificate path +``` + +### Single Profile Build + +```bash +./scripts/build-uki.sh --profile fedora-coreos +``` + +## Customization + +### Adding a New Profile + +1. Create cmdline file: + ```bash + cat > config/cmdline.d/profiles/myprofile.conf << 'EOF' + # My custom profile cmdline + rd.systemd.pull=raw,machine,verify=no,blockdev:myimage:https://example.com/image.raw.xz + root=gpt-auto + ip=dhcp + EOF + ``` + +2. Create boot entry: + ```bash + cat > config/loader/entries/myprofile.conf << 'EOF' + title My Custom Profile + efi /EFI/bitboot/myprofile.efi + EOF + ``` + +3. Build: + ```bash + make uki + ``` + +### Secure Boot Signing + +```bash +make uki SIGN=true SIGN_KEY=/path/to/key.pem SIGN_CERT=/path/to/cert.pem +``` + +## ZFS Installation Support + +BitBoot is designed for Root-on-ZFS installations. The Alpine ZFS Installer profile provides: + +- Pre-loaded ZFS kernel modules +- Full ZFS userspace tools (zpool, zfs, zdb) +- Partition tools (parted, gdisk, cryptsetup) +- Writable tmpfs overlay for installing packages +- Network tools for downloading additional components + +Follow the [OpenZFS Root on ZFS guide](https://openzfs.github.io/openzfs-docs/Getting%20Started/Arch%20Linux/Root%20on%20ZFS.html) directly from the BitBoot USB. + +## Requirements + +### Build System +- systemd v258+ (for rd.systemd.pull support) +- Fedora 41+ or equivalent (recommended) +- 4GB+ disk space for build + +### Target System +- UEFI-capable x86_64 system +- 512MB+ USB drive +- 4GB+ RAM (for network boot profiles) +- Network connection (for rd.systemd.pull profiles) + +## Troubleshooting + +### Boot Menu Not Showing +- Press Space or any key during boot +- Check `timeout` setting in `loader.conf` + +### Network Pull Fails +- Verify network connectivity (`ip=dhcp` in cmdline) +- Check image URL is accessible +- Ensure sufficient RAM for image download + +### ZFS Not Available +- Use Alpine ZFS Installer profile +- Or ZFSBootMenu for existing ZFS systems + +## References + +- [systemd-import-generator(8)](https://man7.org/linux/man-pages/man8/systemd-import-generator.8.html) +- [systemd v258 Release Notes](https://github.com/systemd/systemd/releases/tag/v258) +- [ZFSBootMenu Documentation](https://docs.zfsbootmenu.org/) +- [netboot.xyz](https://netboot.xyz/) +- [UAPI Group UKI Specification](https://uapi-group.org/specifications/specs/unified_kernel_image/) +- [OpenZFS Root on ZFS](https://openzfs.github.io/openzfs-docs/Getting%20Started/Arch%20Linux/Root%20on%20ZFS.html) + +## License + +This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details. + +## Contributing + +Contributions are welcome! Please: + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Submit a pull request + +## Acknowledgments + +- [ZFSBootMenu](https://github.com/zbm-dev/zfsbootmenu) team +- [netboot.xyz](https://github.com/netbootxyz/netboot.xyz) project +- systemd developers for rd.systemd.pull functionality \ No newline at end of file diff --git a/config/cmdline.d/base.conf b/config/cmdline.d/base.conf new file mode 100644 index 0000000..773b74b --- /dev/null +++ b/config/cmdline.d/base.conf @@ -0,0 +1,16 @@ +# BitBoot base kernel command line options +# Common options for console output and logging + +# Console configuration for UEFI systems +console=tty0 +console=ttyS0,115200n8 + +# Systemd logging level +systemd.log_level=info +systemd.log_target=console + +# Enable kernel messages +loglevel=4 + +# Enable early printk for debugging +earlyprintk=serial,ttyS0,115200 diff --git a/config/cmdline.d/network.conf b/config/cmdline.d/network.conf new file mode 100644 index 0000000..5b0a5c0 --- /dev/null +++ b/config/cmdline.d/network.conf @@ -0,0 +1,15 @@ +# BitBoot network boot prerequisites +# Network configuration required for rd.systemd.pull functionality + +# Enable DHCP on all interfaces +ip=dhcp + +# Enable systemd-networkd in initramfs +rd.systemd.networkd=1 + +# Enable systemd-resolved for DNS +rd.systemd.resolved=1 + +# Wait for network to be online before proceeding +rd.net.timeout.carrier=10 +rd.net.timeout.dhcp=30 diff --git a/config/cmdline.d/profiles/alpine-netboot.conf b/config/cmdline.d/profiles/alpine-netboot.conf new file mode 100644 index 0000000..23e80c4 --- /dev/null +++ b/config/cmdline.d/profiles/alpine-netboot.conf @@ -0,0 +1,8 @@ +# Alpine Linux netboot cmdline +# Embedded in alpine-netboot.efi UKI + +modloop=https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/x86_64/netboot/modloop-lts +alpine_repo=https://dl-cdn.alpinelinux.org/alpine/v3.21/main +ip=dhcp +console=tty0 +console=ttyS0,115200n8 diff --git a/config/cmdline.d/profiles/alpine-zfs-installer.conf b/config/cmdline.d/profiles/alpine-zfs-installer.conf new file mode 100644 index 0000000..97d1db4 --- /dev/null +++ b/config/cmdline.d/profiles/alpine-zfs-installer.conf @@ -0,0 +1,8 @@ +# Alpine Linux ZFS Installer cmdline +# Embedded in alpine-zfs-installer.efi UKI + +modloop=https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/x86_64/netboot/modloop-lts +alpine_repo=https://dl-cdn.alpinelinux.org/alpine/v3.21/main +ip=dhcp +console=tty0 +console=ttyS0,115200n8 diff --git a/config/cmdline.d/profiles/bitboot.conf b/config/cmdline.d/profiles/bitboot.conf new file mode 100644 index 0000000..14835c3 --- /dev/null +++ b/config/cmdline.d/profiles/bitboot.conf @@ -0,0 +1,11 @@ +# BitBoot recovery shell cmdline +# Embedded in bitboot.efi UKI + +rd.shell +rd.break +ip=dhcp +console=tty0 +console=ttyS0,115200n8 +systemd.log_level=info +rd.systemd.networkd=1 +rd.systemd.resolved=1 diff --git a/config/cmdline.d/profiles/fedora-coreos.conf b/config/cmdline.d/profiles/fedora-coreos.conf new file mode 100644 index 0000000..e5f271d --- /dev/null +++ b/config/cmdline.d/profiles/fedora-coreos.conf @@ -0,0 +1,9 @@ +# Fedora CoreOS network pull cmdline +# Embedded in fedora-coreos.efi UKI + +rd.systemd.pull=raw,machine,verify=no,blockdev:rootdisk:https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/41.20250117.3.0/x86_64/fedora-coreos-41.20250117.3.0-metal.x86_64.raw.xz +root=gpt-auto +ip=dhcp +console=tty0 +console=ttyS0,115200n8 +systemd.log_level=info diff --git a/config/cmdline.d/profiles/flatcar.conf b/config/cmdline.d/profiles/flatcar.conf new file mode 100644 index 0000000..7b2deea --- /dev/null +++ b/config/cmdline.d/profiles/flatcar.conf @@ -0,0 +1,9 @@ +# Flatcar Container Linux network pull cmdline +# Embedded in flatcar.efi UKI + +rd.systemd.pull=raw,machine,verify=no,blockdev:flatcar:https://stable.release.flatcar-linux.net/amd64-usr/current/flatcar_production_image.bin.bz2 +root=gpt-auto +ip=dhcp +console=tty0 +console=ttyS0,115200n8 +systemd.log_level=info diff --git a/config/cmdline.d/profiles/nixos-kexec.conf b/config/cmdline.d/profiles/nixos-kexec.conf new file mode 100644 index 0000000..064e8be --- /dev/null +++ b/config/cmdline.d/profiles/nixos-kexec.conf @@ -0,0 +1,7 @@ +# NixOS kexec installer cmdline +# Embedded in nixos-kexec.efi UKI + +init=/nix/store/*/init +ip=dhcp +console=tty0 +console=ttyS0,115200n8 diff --git a/config/cmdline.d/profiles/vyos.conf b/config/cmdline.d/profiles/vyos.conf new file mode 100644 index 0000000..5b30f8c --- /dev/null +++ b/config/cmdline.d/profiles/vyos.conf @@ -0,0 +1,8 @@ +# VyOS 1.5 Rolling network boot cmdline +# Embedded in vyos.efi UKI + +boot=live +fetch=https://github.com/vyos/vyos-rolling-nightly-builds/releases/download/1.5-rolling-202501200023/vyos-1.5-rolling-202501200023-amd64.iso +ip=dhcp +console=tty0 +console=ttyS0,115200 diff --git a/config/loader/entries/alpine-netboot.conf b/config/loader/entries/alpine-netboot.conf new file mode 100644 index 0000000..248ec14 --- /dev/null +++ b/config/loader/entries/alpine-netboot.conf @@ -0,0 +1,11 @@ +# Alpine Linux - Network Boot Profile +# Lightweight netboot using kernel + initrd + modloop +# +# Alpine Linux runs entirely from RAM making it extremely fast +# and suitable for diskless systems or rescue operations. +# +# This profile uses a dedicated UKI with Alpine kernel and initramfs +# embedded for network boot. + +title Alpine Linux (Netboot) +efi /EFI/bitboot/alpine-netboot.efi diff --git a/config/loader/entries/alpine-zfs-installer.conf b/config/loader/entries/alpine-zfs-installer.conf new file mode 100644 index 0000000..a9c6cfe --- /dev/null +++ b/config/loader/entries/alpine-zfs-installer.conf @@ -0,0 +1,17 @@ +# Alpine Linux ZFS Installer - Network Boot Profile +# Boots Alpine Linux Extended with ZFS support for Root-on-ZFS installations +# +# This profile is designed for following the OpenZFS Root on ZFS guide: +# https://openzfs.github.io/openzfs-docs/Getting%20Started/Arch%20Linux/Root%20on%20ZFS.html +# +# Features: +# - Pre-loaded ZFS kernel modules matching the LTS kernel +# - Writable tmpfs overlay for installing packages +# - Full networking support (DHCP, DNS) +# - Partition tools (parted, gdisk, cryptsetup) +# +# This profile uses a dedicated UKI with Alpine kernel and initramfs +# embedded along with the ZFS-specific boot parameters. + +title Alpine Linux ZFS Installer +efi /EFI/bitboot/alpine-zfs-installer.efi diff --git a/config/loader/entries/bitboot.conf b/config/loader/entries/bitboot.conf new file mode 100644 index 0000000..629c351 --- /dev/null +++ b/config/loader/entries/bitboot.conf @@ -0,0 +1,14 @@ +# BitBoot - Default Boot Entry +# Boots the BitBoot recovery shell with full network and ZFS support +# +# This entry provides a minimal rescue environment with: +# - Network stack (networkd, resolved) for HTTP/HTTPS +# - Compression support (xz, zstd, gzip) +# - Loop device support for attaching images +# - ZFS kernel modules and userspace tools +# - Minimal shell for recovery/debugging +# +# This is the main BitBoot UKI with recovery shell capabilities embedded. + +title BitBoot Recovery Shell +efi /EFI/bitboot/bitboot.efi diff --git a/config/loader/entries/fedora-coreos.conf b/config/loader/entries/fedora-coreos.conf new file mode 100644 index 0000000..612f5e8 --- /dev/null +++ b/config/loader/entries/fedora-coreos.conf @@ -0,0 +1,11 @@ +# Fedora CoreOS - Network Pull Boot Profile +# Uses rd.systemd.pull to download and boot raw disk image +# +# Fedora CoreOS provides DDI-compliant raw disk images that support +# GPT auto-discovery for automatic root partition detection. +# +# This profile uses a dedicated UKI with embedded kernel cmdline for +# Fedora CoreOS network pull booting. + +title Fedora CoreOS (Network Pull) +efi /EFI/bitboot/fedora-coreos.efi diff --git a/config/loader/entries/flatcar.conf b/config/loader/entries/flatcar.conf new file mode 100644 index 0000000..606be7c --- /dev/null +++ b/config/loader/entries/flatcar.conf @@ -0,0 +1,11 @@ +# Flatcar Container Linux - Network Pull Boot Profile +# Uses rd.systemd.pull to download and boot raw disk image +# +# Flatcar Container Linux is similar to Fedora CoreOS and provides +# production-ready container-optimized raw disk images. +# +# This profile uses a dedicated UKI with embedded kernel cmdline for +# Flatcar Container Linux network pull booting. + +title Flatcar Container Linux (Network Pull) +efi /EFI/bitboot/flatcar.efi diff --git a/config/loader/entries/netboot.conf b/config/loader/entries/netboot.conf new file mode 100644 index 0000000..f7a76ca --- /dev/null +++ b/config/loader/entries/netboot.conf @@ -0,0 +1,16 @@ +# netboot.xyz - Network Boot Utility +# Chainloads netboot.xyz EFI for iPXE-based network booting +# +# netboot.xyz provides access to dozens of operating system installers +# and utilities over the network without pre-downloading ISOs. +# +# Supported distributions include: +# - Arch Linux, Ubuntu, Debian, Fedora, CentOS +# - Alpine Linux, NixOS, Gentoo +# - Windows PE, VMware ESXi +# - Various rescue and utility images +# +# Documentation: https://netboot.xyz/ + +title netboot.xyz +efi /EFI/netboot/netboot.xyz.efi diff --git a/config/loader/entries/nixos-kexec.conf b/config/loader/entries/nixos-kexec.conf new file mode 100644 index 0000000..a5f7517 --- /dev/null +++ b/config/loader/entries/nixos-kexec.conf @@ -0,0 +1,15 @@ +# NixOS - Kexec Installer Boot Profile +# Boots NixOS via kexec-based installer from nix-community/nixos-images +# +# NixOS has a unique boot model that requires kexec for network installation. +# The installer image is extracted to RAM and then kexec'd into. +# +# Note: Requires ~1GB+ RAM for initrd extraction +# +# Alternative: Use netboot.xyz to chainload NixOS iPXE script +# +# This profile uses a dedicated UKI with NixOS kernel and initrd +# embedded for kexec-based installation. + +title NixOS Kexec Installer +efi /EFI/bitboot/nixos-kexec.efi diff --git a/config/loader/entries/vyos.conf b/config/loader/entries/vyos.conf new file mode 100644 index 0000000..5e3d25c --- /dev/null +++ b/config/loader/entries/vyos.conf @@ -0,0 +1,13 @@ +# VyOS 1.5 (Circinus) - Network Boot Profile +# Boots VyOS router OS using squashfs fetch over HTTP +# +# VyOS requires extracting vmlinuz, initrd.img, and filesystem.squashfs +# from the ISO and serving them over HTTP. +# +# Source: https://github.com/vyos/vyos-rolling-nightly-builds/releases/ +# +# This profile uses a dedicated UKI with VyOS kernel and initrd +# embedded for network boot with squashfs fetch. + +title VyOS 1.5 Rolling (Network Boot) +efi /EFI/bitboot/vyos.efi diff --git a/config/loader/entries/zfsbootmenu.conf b/config/loader/entries/zfsbootmenu.conf new file mode 100644 index 0000000..b6f9d56 --- /dev/null +++ b/config/loader/entries/zfsbootmenu.conf @@ -0,0 +1,14 @@ +# ZFSBootMenu - ZFS Boot Environment Manager +# Chainloads ZFSBootMenu EFI for ZFS pool discovery and BE management +# +# ZFSBootMenu provides: +# - Pre-compiled ZFS modules matching the embedded kernel +# - Full ZFS userspace tools (zpool, zfs, zdb, zstream, zinject) +# - Recovery shell with networking, SSH, and essential utilities +# - Boot environment management (snapshot, clone, rollback, chroot) +# - Key management for native ZFS encryption +# +# Documentation: https://docs.zfsbootmenu.org/ + +title ZFSBootMenu Recovery +efi /EFI/zbm/zfsbootmenu.efi diff --git a/config/loader/loader.conf b/config/loader/loader.conf new file mode 100644 index 0000000..b3868c9 --- /dev/null +++ b/config/loader/loader.conf @@ -0,0 +1,22 @@ +# BitBoot systemd-boot configuration +# This file configures the boot loader menu behavior + +# Default boot entry (without .conf extension) +default bitboot + +# Timeout in seconds (0 = no timeout, menu hidden) +# Use space bar to show menu when timeout is 0 +timeout 5 + +# Enable console output mode (auto, max, keep) +console-mode auto + +# Enable editor for kernel command line (disable for security) +editor yes + +# Auto-enroll secure boot keys from /loader/keys/ +auto-entries yes +auto-firmware yes + +# Beep on boot entry selection +#beep yes diff --git a/initramfs/config.yaml b/initramfs/config.yaml new file mode 100644 index 0000000..f991aae --- /dev/null +++ b/initramfs/config.yaml @@ -0,0 +1,140 @@ +# BitBoot Initramfs Configuration +# This file defines how the custom initramfs is built using dracut +# +# The initramfs must contain: +# - systemd v258+ with rd.systemd.pull support +# - Network stack (networkd, resolved) +# - Compression support (xz, zstd, gzip) +# - Loop device support +# - ZFS kernel modules and tools +# - Minimal shell for recovery + +# Dracut configuration +dracut: + # Include these dracut modules + add_modules: + - systemd + - systemd-initrd + - systemd-networkd + - systemd-resolved + - systemd-importd + - network + - network-manager + - kernel-network-modules + - loop + - dm + - lvm + - crypt + - zfs + - rescue + - shutdown + - usrmount + - base + - fs-lib + - rootfs-block + + # Always include these drivers + add_drivers: + - loop + - overlay + - squashfs + - virtio_net + - virtio_blk + - virtio_scsi + - e1000 + - e1000e + - igb + - ixgbe + - nvme + - ahci + - xhci_hcd + - ehci_hcd + - usb_storage + - sd_mod + - ext4 + - xfs + - btrfs + - vfat + - zfs + + # Include these binaries in initramfs + install_items: + - /usr/bin/curl + - /usr/bin/wget + - /usr/bin/xz + - /usr/bin/zstd + - /usr/bin/gzip + - /usr/bin/gunzip + - /usr/bin/bzip2 + - /usr/bin/bunzip2 + - /usr/bin/losetup + - /usr/bin/mount + - /usr/bin/umount + - /usr/bin/ip + - /usr/bin/ss + - /usr/bin/ping + - /usr/bin/bash + - /usr/bin/sh + - /usr/bin/cat + - /usr/bin/ls + - /usr/bin/cp + - /usr/bin/mv + - /usr/bin/rm + - /usr/bin/mkdir + - /usr/bin/chmod + - /usr/bin/chown + - /usr/bin/ln + - /usr/bin/grep + - /usr/bin/awk + - /usr/bin/sed + - /usr/bin/vi + - /usr/bin/less + - /usr/sbin/parted + - /usr/sbin/gdisk + - /usr/sbin/sgdisk + - /usr/sbin/fdisk + - /usr/sbin/mkfs.ext4 + - /usr/sbin/mkfs.xfs + - /usr/sbin/mkfs.vfat + - /usr/sbin/cryptsetup + - /usr/sbin/zpool + - /usr/sbin/zfs + - /usr/sbin/zdb + + # Enable systemd in initrd + systemd: yes + + # Compress with zstd for best size/speed ratio + compress: zstd + + # Include firmware for hardware support + firmware: + - amd-ucode + - intel-ucode + - linux-firmware + +# Kernel configuration +kernel: + # Use the linux-lts kernel for stability + package: linux-lts + + # Kernel modules to include + modules: + - zfs + - loop + - overlay + - squashfs + +# systemd services to enable in initrd +systemd_services: + - systemd-networkd.service + - systemd-resolved.service + - systemd-importd.service + - systemd-import-generator + +# Custom hooks for bitboot-specific functionality +hooks: + pre_build: + - fetch-zfs-modules.sh + post_build: + - verify-initramfs.sh diff --git a/initramfs/hooks/bitboot-network-pull.sh b/initramfs/hooks/bitboot-network-pull.sh new file mode 100644 index 0000000..9f3f1a1 --- /dev/null +++ b/initramfs/hooks/bitboot-network-pull.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# BitBoot Dracut Hook - Network Pull Support +# +# This hook ensures systemd-import-generator and related services +# are properly configured for rd.systemd.pull functionality +# +# Place in /usr/lib/dracut/modules.d/99bitboot/module-setup.sh + +check() { + require_binaries systemd-importd || return 1 + return 0 +} + +depends() { + echo systemd systemd-initrd network +} + +install() { + # Install systemd-importd and related binaries + inst_multiple -o \ + systemd-importd \ + systemd-import \ + systemd-pull \ + /usr/lib/systemd/systemd-importd \ + /usr/lib/systemd/system-generators/systemd-import-generator + + # Install compression tools for image decompression + inst_multiple -o \ + xz \ + zstd \ + gzip \ + bzip2 + + # Install network utilities + inst_multiple -o \ + curl \ + wget + + # Install losetup for block device creation + inst_multiple -o \ + losetup + + # Install systemd units for importd + inst_simple /usr/lib/systemd/system/systemd-importd.service + inst_simple /usr/lib/systemd/system/dbus-org.freedesktop.import1.service + + # Enable systemd-importd socket activation + inst_simple /usr/lib/systemd/system/systemd-importd.socket + + # D-Bus configuration for importd + inst_simple /usr/share/dbus-1/system-services/org.freedesktop.import1.service + inst_simple /usr/share/dbus-1/system.d/org.freedesktop.import1.conf + + # Create machines directory for downloaded images + mkdir -p "${initdir}/var/lib/machines" + mkdir -p "${initdir}/run/machines" + + # Enable the services + systemctl -q --root "$initdir" enable systemd-importd.socket 2>/dev/null || true +} + +installkernel() { + # Install loop device driver + instmods loop + + # Install filesystem modules + instmods ext4 xfs btrfs squashfs overlay + + # Install network drivers + instmods =drivers/net/ethernet + instmods virtio_net +} diff --git a/initramfs/hooks/bitboot-zfs.sh b/initramfs/hooks/bitboot-zfs.sh new file mode 100644 index 0000000..fa644fc --- /dev/null +++ b/initramfs/hooks/bitboot-zfs.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# BitBoot Dracut Hook - ZFS Support +# +# This hook ensures ZFS kernel modules and userspace tools +# are properly included in the initramfs +# +# Place in /usr/lib/dracut/modules.d/99bitboot-zfs/module-setup.sh + +check() { + require_binaries zpool zfs || return 1 + return 0 +} + +depends() { + echo systemd +} + +install() { + # Install ZFS userspace tools + inst_multiple -o \ + zpool \ + zfs \ + zdb \ + zstream \ + zinject \ + zstreamdump \ + zhack \ + ztest + + # Install ZFS libraries + inst_libdir_file "libzfs*.so*" + inst_libdir_file "libnvpair*.so*" + inst_libdir_file "libuutil*.so*" + inst_libdir_file "libzpool*.so*" + + # Install ZFS udev rules + inst_rules 60-zvol.rules 69-vdev.rules 90-zfs.rules + + # Install ZFS systemd units + inst_simple /usr/lib/systemd/system/zfs-import-cache.service + inst_simple /usr/lib/systemd/system/zfs-import-scan.service + inst_simple /usr/lib/systemd/system/zfs-mount.service + inst_simple /usr/lib/systemd/system/zfs-share.service + inst_simple /usr/lib/systemd/system/zfs-zed.service + inst_simple /usr/lib/systemd/system/zfs.target + + # Install ZFS configuration + inst_simple /etc/zfs/zpool.cache 2>/dev/null || true + inst_dir /etc/zfs + inst_dir /etc/zfs/zpool.d + + # Install hostid for ZFS pool import + inst_simple /etc/hostid 2>/dev/null || true + + # Create necessary directories + mkdir -p "${initdir}/var/lib/zfs" + mkdir -p "${initdir}/run/zfs" +} + +installkernel() { + # Install ZFS kernel modules + instmods zfs + instmods spl + instmods znvpair + instmods zcommon + instmods zlua + instmods zavl + instmods zunicode + instmods zzstd + instmods icp +} diff --git a/initramfs/modules/bitboot-pull-premount.sh b/initramfs/modules/bitboot-pull-premount.sh new file mode 100644 index 0000000..1c09ecb --- /dev/null +++ b/initramfs/modules/bitboot-pull-premount.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# BitBoot Pre-mount Hook - Ensure Network for rd.systemd.pull +# +# This hook runs before mount and ensures the network is fully +# operational before attempting to pull images via HTTP/HTTPS. + +type getarg >/dev/null 2>&1 || . /lib/dracut-lib.sh + +# Check if rd.systemd.pull is specified +if ! getarg rd.systemd.pull= >/dev/null 2>&1; then + exit 0 +fi + +info "BitBoot: Waiting for network before systemd.pull..." + +# Wait for network to be available +for i in $(seq 1 30); do + if ip route show default 2>/dev/null | grep -q .; then + info "BitBoot: Network is available" + break + fi + sleep 1 +done + +# Verify DNS resolution works +if command -v resolvectl >/dev/null 2>&1; then + resolvectl status 2>/dev/null || true +fi + +info "BitBoot: Network setup complete, proceeding with image pull" diff --git a/initramfs/modules/bitboot-pull.sh b/initramfs/modules/bitboot-pull.sh new file mode 100644 index 0000000..40a64fa --- /dev/null +++ b/initramfs/modules/bitboot-pull.sh @@ -0,0 +1,99 @@ +#!/bin/bash +# BitBoot Module - rd.systemd.pull handler +# +# This module provides support for the rd.systemd.pull kernel command line +# parameter introduced in systemd v258. It enables downloading and booting +# from raw disk images over HTTP/HTTPS at boot time. +# +# Usage in kernel cmdline: +# rd.systemd.pull=raw,machine,verify=no,blockdev:: +# +# Example: +# rd.systemd.pull=raw,machine,verify=no,blockdev:rootdisk:https://example.com/image.raw.xz + +# Module metadata +DRACUT_MODULE_NAME="bitboot-pull" +DRACUT_MODULE_VERSION="1.0.0" +DRACUT_MODULE_DESCRIPTION="BitBoot rd.systemd.pull support module" + +# Check if this module should be included +check() { + # Check for systemd v258+ with import-generator support + if [[ -x /usr/lib/systemd/system-generators/systemd-import-generator ]]; then + return 0 + fi + return 1 +} + +# Module dependencies +depends() { + echo systemd systemd-initrd network-manager +} + +# Install files into initramfs +install() { + # Core systemd-import binaries + inst_multiple -o \ + /usr/lib/systemd/systemd-importd \ + /usr/lib/systemd/systemd-import \ + /usr/lib/systemd/systemd-pull \ + /usr/lib/systemd/system-generators/systemd-import-generator + + # Compression tools for decompressing pulled images + inst_multiple -o \ + xz xzcat unxz \ + zstd zstdcat unzstd \ + gzip gunzip zcat \ + bzip2 bunzip2 bzcat \ + lz4 lz4cat unlz4 + + # Network utilities for HTTP/HTTPS downloads + inst_multiple -o \ + curl wget + + # Block device utilities + inst_multiple -o \ + losetup blockdev + + # CA certificates for HTTPS + inst_simple /etc/ssl/certs/ca-certificates.crt + inst_dir /etc/ssl/certs + inst_dir /etc/pki/tls/certs + + # systemd units for importd + for unit in \ + systemd-importd.service \ + systemd-importd.socket \ + dbus-org.freedesktop.import1.service + do + inst_simple "/usr/lib/systemd/system/${unit}" 2>/dev/null || true + done + + # D-Bus configuration + inst_simple /usr/share/dbus-1/system-services/org.freedesktop.import1.service 2>/dev/null || true + inst_simple /usr/share/dbus-1/system.d/org.freedesktop.import1.conf 2>/dev/null || true + inst_simple /etc/dbus-1/system.d/org.freedesktop.import1.conf 2>/dev/null || true + + # Create necessary directories + mkdir -p "${initdir}/var/lib/machines" + mkdir -p "${initdir}/run/machines" + mkdir -p "${initdir}/run/systemd/machines" + + # Enable socket activation for importd + systemctl -q --root "$initdir" enable systemd-importd.socket 2>/dev/null || true + + # Install a hook script to ensure network is up before pulling + inst_hook pre-mount 10 "$moddir/bitboot-pull-premount.sh" +} + +# Install kernel modules +installkernel() { + # Loop device for attaching downloaded images + instmods loop + + # Filesystem modules for mounted images + instmods ext4 xfs btrfs squashfs overlay + + # GPT partition support for auto-discovery + instmods nls_cp437 nls_iso8859_1 vfat +} diff --git a/scripts/build-initramfs.sh b/scripts/build-initramfs.sh new file mode 100644 index 0000000..b4d3325 --- /dev/null +++ b/scripts/build-initramfs.sh @@ -0,0 +1,212 @@ +#!/bin/bash +# BitBoot Initramfs Builder +# Builds the custom initramfs with systemd v258+ and rd.systemd.pull support +# +# Usage: ./build-initramfs.sh [options] +# +# Options: +# --output FILE Output initramfs file (default: ./build/initramfs-bitboot.img) +# --kernel VERSION Kernel version to build for (default: running kernel) +# --config FILE Configuration file (default: ./initramfs/config.yaml) +# --modules DIR Custom modules directory (default: ./initramfs/modules) +# --hooks DIR Custom hooks directory (default: ./initramfs/hooks) +# -h, --help Show this help message + +set -euo pipefail + +# Default values +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +OUTPUT_FILE="${PROJECT_DIR}/build/initramfs-bitboot.img" +KERNEL_VERSION="" +CONFIG_FILE="${PROJECT_DIR}/initramfs/config.yaml" +MODULES_DIR="${PROJECT_DIR}/initramfs/modules" +HOOKS_DIR="${PROJECT_DIR}/initramfs/hooks" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log_info() { + echo -e "${GREEN}[INFO]${NC} $*" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $*" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $*" >&2 +} + +usage() { + head -18 "$0" | grep '^#' | tail -n +2 | sed 's/^# \?//' + exit 0 +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --output) + OUTPUT_FILE="$2" + shift 2 + ;; + --kernel) + KERNEL_VERSION="$2" + shift 2 + ;; + --config) + CONFIG_FILE="$2" + shift 2 + ;; + --modules) + MODULES_DIR="$2" + shift 2 + ;; + --hooks) + HOOKS_DIR="$2" + shift 2 + ;; + -h|--help) + usage + ;; + *) + log_error "Unknown option: $1" + usage + ;; + esac +done + +# Create output directory +mkdir -p "$(dirname "${OUTPUT_FILE}")" + +# Detect kernel version +if [[ -z "${KERNEL_VERSION}" ]]; then + KERNEL_VERSION=$(uname -r) +fi +log_info "Building initramfs for kernel: ${KERNEL_VERSION}" + +# Check for dracut +if ! command -v dracut >/dev/null 2>&1; then + log_error "dracut not found. Please install dracut." + exit 1 +fi + +# Check systemd version +SYSTEMD_VERSION=$(systemctl --version | head -1 | awk '{print $2}') +if [[ "${SYSTEMD_VERSION}" -lt 258 ]]; then + log_warn "systemd version ${SYSTEMD_VERSION} detected. rd.systemd.pull requires v258+." + log_warn "Some features may not work correctly." +fi + +# Install custom modules to dracut modules directory +DRACUT_MODULES_DIR="/usr/lib/dracut/modules.d" +BITBOOT_MODULE_DIR="${DRACUT_MODULES_DIR}/99bitboot" + +log_info "Installing BitBoot dracut modules..." +mkdir -p "${BITBOOT_MODULE_DIR}" + +# Copy module files +if [[ -d "${MODULES_DIR}" ]]; then + for module in "${MODULES_DIR}"/*.sh; do + if [[ -f "${module}" ]]; then + install -m 755 "${module}" "${BITBOOT_MODULE_DIR}/" + fi + done +fi + +# Copy hook files +if [[ -d "${HOOKS_DIR}" ]]; then + for hook in "${HOOKS_DIR}"/*.sh; do + if [[ -f "${hook}" ]]; then + install -m 755 "${hook}" "${BITBOOT_MODULE_DIR}/" + fi + done +fi + +# Create module-setup.sh if not exists +if [[ ! -f "${BITBOOT_MODULE_DIR}/module-setup.sh" ]]; then + cat > "${BITBOOT_MODULE_DIR}/module-setup.sh" << 'MODSETUP' +#!/bin/bash +# BitBoot Dracut Module + +check() { + return 0 +} + +depends() { + echo systemd systemd-initrd network +} + +install() { + # Install bitboot scripts + for script in "${moddir}"/*.sh; do + [[ -f "${script}" ]] && [[ "$(basename "${script}")" != "module-setup.sh" ]] && \ + inst_simple "${script}" "/usr/lib/bitboot/$(basename "${script}")" + done + + # Install compression tools + inst_multiple -o xz zstd gzip bzip2 lz4 + + # Install network tools + inst_multiple -o curl wget + + # Install block device tools + inst_multiple -o losetup blockdev + + # Create necessary directories + mkdir -p "${initdir}/var/lib/machines" + mkdir -p "${initdir}/run/machines" +} + +installkernel() { + instmods loop overlay squashfs + instmods =drivers/net/ethernet +} +MODSETUP + chmod 755 "${BITBOOT_MODULE_DIR}/module-setup.sh" +fi + +# Build dracut command line arguments +DRACUT_ARGS=( + --force + --kver "${KERNEL_VERSION}" + --add "systemd systemd-initrd network-manager" + --add "bitboot" + --install "curl wget xz zstd gzip bzip2 losetup" + --include "/etc/ssl/certs" "/etc/ssl/certs" +) + +# Add ZFS if available +if modinfo zfs >/dev/null 2>&1; then + DRACUT_ARGS+=(--add "zfs") +fi + +# Add additional modules based on config (simple parsing) +if [[ -f "${CONFIG_FILE}" ]]; then + log_info "Reading configuration from ${CONFIG_FILE}" +fi + +log_info "Building initramfs with dracut..." +log_info "Command: dracut ${DRACUT_ARGS[*]} ${OUTPUT_FILE}" + +dracut "${DRACUT_ARGS[@]}" "${OUTPUT_FILE}" + +# Verify output +if [[ -f "${OUTPUT_FILE}" ]]; then + log_info "Successfully built initramfs: ${OUTPUT_FILE}" + log_info "Size: $(du -h "${OUTPUT_FILE}" | cut -f1)" + + # List contents summary + if command -v lsinitrd >/dev/null 2>&1; then + log_info "Initramfs contents summary:" + lsinitrd "${OUTPUT_FILE}" 2>/dev/null | head -20 || true + fi +else + log_error "Failed to build initramfs" + exit 1 +fi + +log_info "Initramfs build complete!" diff --git a/scripts/build-uki.sh b/scripts/build-uki.sh new file mode 100644 index 0000000..377ab97 --- /dev/null +++ b/scripts/build-uki.sh @@ -0,0 +1,298 @@ +#!/bin/bash +# BitBoot UKI Assembly Script +# Builds unified kernel images (*.efi) for each boot profile, combining: +# - systemd-stub as UEFI loader +# - Custom initramfs with rd.systemd.pull support +# - Linux kernel with required drivers +# - Profile-specific embedded kernel command line +# +# Usage: ./build-uki.sh [options] +# +# Options: +# --output DIR Output directory (default: ./build) +# --kernel PATH Kernel image path (default: auto-detect) +# --initrd PATH Initramfs image path (default: auto-detect) +# --profile NAME Build only specific profile (default: all) +# --profiles-dir Directory with profile cmdline files (default: config/cmdline.d/profiles) +# --osrel FILE os-release file (default: /etc/os-release) +# --sign Sign the UKI for Secure Boot +# --key PATH Secure Boot signing key +# --cert PATH Secure Boot signing certificate +# -h, --help Show this help message + +set -euo pipefail + +# Default values +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +BUILD_DIR="${PROJECT_DIR}/build" +KERNEL="" +INITRD="" +PROFILE="" +PROFILES_DIR="${PROJECT_DIR}/config/cmdline.d/profiles" +OSREL="/etc/os-release" +SIGN=false +SIGN_KEY="" +SIGN_CERT="" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +log_info() { + echo -e "${GREEN}[INFO]${NC} $*" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $*" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $*" >&2 +} + +usage() { + head -25 "$0" | grep '^#' | tail -n +2 | sed 's/^# \?//' + exit 0 +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --output) + BUILD_DIR="$2" + shift 2 + ;; + --kernel) + KERNEL="$2" + shift 2 + ;; + --initrd) + INITRD="$2" + shift 2 + ;; + --profile) + PROFILE="$2" + shift 2 + ;; + --profiles-dir) + PROFILES_DIR="$2" + shift 2 + ;; + --osrel) + OSREL="$2" + shift 2 + ;; + --sign) + SIGN=true + shift + ;; + --key) + SIGN_KEY="$2" + shift 2 + ;; + --cert) + SIGN_CERT="$2" + shift 2 + ;; + -h|--help) + usage + ;; + *) + log_error "Unknown option: $1" + usage + ;; + esac +done + +# Create build directory +mkdir -p "${BUILD_DIR}" +mkdir -p "${BUILD_DIR}/efi" + +# Auto-detect kernel if not specified +if [[ -z "${KERNEL}" ]]; then + # Try common kernel paths + for kpath in \ + /boot/vmlinuz-linux-lts \ + /boot/vmlinuz-linux \ + /lib/modules/*/vmlinuz \ + /boot/vmlinuz + do + if [[ -f "${kpath}" ]]; then + KERNEL="${kpath}" + break + fi + done + + if [[ -z "${KERNEL}" ]]; then + log_error "Could not find kernel image. Use --kernel to specify." + exit 1 + fi +fi +log_info "Using kernel: ${KERNEL}" + +# Auto-detect initramfs if not specified +if [[ -z "${INITRD}" ]]; then + # Try common initramfs paths + for ipath in \ + "${BUILD_DIR}/initramfs-bitboot.img" \ + /boot/initramfs-linux-lts.img \ + /boot/initramfs-linux.img \ + /boot/initrd.img + do + if [[ -f "${ipath}" ]]; then + INITRD="${ipath}" + break + fi + done + + if [[ -z "${INITRD}" ]]; then + log_warn "No initramfs found. Building one..." + "${SCRIPT_DIR}/build-initramfs.sh" --output "${BUILD_DIR}/initramfs-bitboot.img" + INITRD="${BUILD_DIR}/initramfs-bitboot.img" + fi +fi +log_info "Using initramfs: ${INITRD}" + +# Find systemd-stub +STUB="" +for stub_path in \ + /usr/lib/systemd/boot/efi/linuxx64.efi.stub \ + /usr/lib/systemd/boot/efi/linuxia32.efi.stub \ + /lib/systemd/boot/efi/linuxx64.efi.stub \ + /usr/share/systemd/bootctl/linuxx64.efi.stub +do + if [[ -f "${stub_path}" ]]; then + STUB="${stub_path}" + break + fi +done + +if [[ -z "${STUB}" ]]; then + log_error "Could not find systemd-stub. Install systemd-boot." + exit 1 +fi +log_info "Using systemd-stub: ${STUB}" + +# Function to build a single UKI +build_uki() { + local profile_name="$1" + local cmdline_file="$2" + local output_file="${BUILD_DIR}/efi/${profile_name}.efi" + + log_info "Building UKI for profile: ${profile_name}" + + # Create cmdline from profile file + local cmdline_temp="${BUILD_DIR}/${profile_name}-cmdline.txt" + grep -v '^\s*#' "${cmdline_file}" | grep -v '^\s*$' | tr '\n' ' ' > "${cmdline_temp}" + echo "" >> "${cmdline_temp}" + + log_info " Cmdline: $(cat ${cmdline_temp})" + + if command -v ukify >/dev/null 2>&1; then + local ukify_args=( + build + --linux="${KERNEL}" + --initrd="${INITRD}" + --cmdline="@${cmdline_temp}" + --os-release="@${OSREL}" + --output="${output_file}" + ) + + # Add splash image if available + if [[ -f "${PROJECT_DIR}/assets/splash.bmp" ]]; then + ukify_args+=(--splash="${PROJECT_DIR}/assets/splash.bmp") + fi + + # Add signing if requested + if [[ "${SIGN}" == "true" ]] && [[ -n "${SIGN_KEY}" ]] && [[ -n "${SIGN_CERT}" ]]; then + ukify_args+=( + --secureboot-private-key="${SIGN_KEY}" + --secureboot-certificate="${SIGN_CERT}" + ) + fi + + ukify "${ukify_args[@]}" + + elif command -v objcopy >/dev/null 2>&1; then + # Calculate section offsets + local align=4096 + + local osrel_size cmdline_size kernel_size + osrel_size=$(stat -c%s "${OSREL}") + cmdline_size=$(stat -c%s "${cmdline_temp}") + kernel_size=$(stat -c%s "${KERNEL}") + + # Calculate offsets (must be aligned) + local osrel_offs=0x10000 + local cmdline_offs linux_offs initrd_offs + cmdline_offs=$(printf "0x%x" $(( (osrel_offs + osrel_size + align - 1) / align * align ))) + linux_offs=$(printf "0x%x" $(( ($(printf "%d" "${cmdline_offs}") + cmdline_size + align - 1) / align * align ))) + initrd_offs=$(printf "0x%x" $(( ($(printf "%d" "${linux_offs}") + kernel_size + align - 1) / align * align ))) + + objcopy \ + --add-section .osrel="${OSREL}" --change-section-vma .osrel="${osrel_offs}" \ + --add-section .cmdline="${cmdline_temp}" --change-section-vma .cmdline="${cmdline_offs}" \ + --add-section .linux="${KERNEL}" --change-section-vma .linux="${linux_offs}" \ + --add-section .initrd="${INITRD}" --change-section-vma .initrd="${initrd_offs}" \ + "${STUB}" "${output_file}" + + # Sign if requested + if [[ "${SIGN}" == "true" ]] && [[ -n "${SIGN_KEY}" ]] && [[ -n "${SIGN_CERT}" ]]; then + if command -v sbsign >/dev/null 2>&1; then + sbsign --key "${SIGN_KEY}" --cert "${SIGN_CERT}" --output "${output_file}" "${output_file}" + else + log_warn "sbsign not found. UKI will not be signed." + fi + fi + else + log_error "Neither ukify nor objcopy found. Cannot build UKI." + return 1 + fi + + # Cleanup temp file + rm -f "${cmdline_temp}" + + # Verify the output + if [[ -f "${output_file}" ]]; then + log_info " Built: ${output_file} ($(du -h "${output_file}" | cut -f1))" + else + log_error " Failed to build UKI: ${output_file}" + return 1 + fi +} + +# Build UKIs for profiles +log_info "Building UKIs from profiles in: ${PROFILES_DIR}" + +if [[ -n "${PROFILE}" ]]; then + # Build single profile + profile_file="${PROFILES_DIR}/${PROFILE}.conf" + if [[ -f "${profile_file}" ]]; then + build_uki "${PROFILE}" "${profile_file}" + else + log_error "Profile not found: ${profile_file}" + exit 1 + fi +else + # Build all profiles + if [[ -d "${PROFILES_DIR}" ]]; then + for profile_file in "${PROFILES_DIR}"/*.conf; do + if [[ -f "${profile_file}" ]]; then + profile_name=$(basename "${profile_file}" .conf) + build_uki "${profile_name}" "${profile_file}" + fi + done + else + log_error "Profiles directory not found: ${PROFILES_DIR}" + exit 1 + fi +fi + +# List all built UKIs +log_info "Built UKIs:" +find "${BUILD_DIR}/efi" -name "*.efi" -exec ls -lh {} \; 2>/dev/null || true + +log_info "UKI build complete!" diff --git a/scripts/create-usb.sh b/scripts/create-usb.sh new file mode 100644 index 0000000..eb5a247 --- /dev/null +++ b/scripts/create-usb.sh @@ -0,0 +1,255 @@ +#!/bin/bash +# BitBoot USB Image Creator +# Creates a bootable USB image with BitBoot UKI and boot configurations +# +# Usage: ./create-usb.sh [options] +# +# Options: +# --output FILE Output image file (default: ./build/bitboot-x86_64.img) +# --size SIZE Image size in MB (default: 512) +# --uki PATH Path to bitboot.efi (default: ./build/bitboot.efi) +# --deps DIR Dependencies directory (default: ./build/deps) +# --config DIR Config directory (default: ./config) +# --label LABEL ESP partition label (default: BITBOOT) +# -h, --help Show this help message +# +# The resulting image can be written to USB with: +# dd if=bitboot-x86_64.img of=/dev/sdX bs=4M status=progress + +set -euo pipefail + +# Default values +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +OUTPUT_IMG="${PROJECT_DIR}/build/bitboot-x86_64.img" +IMG_SIZE_MB=512 +UKI_PATH="${PROJECT_DIR}/build/bitboot.efi" +DEPS_DIR="${PROJECT_DIR}/build/deps" +CONFIG_DIR="${PROJECT_DIR}/config" +ESP_LABEL="BITBOOT" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log_info() { + echo -e "${GREEN}[INFO]${NC} $*" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $*" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $*" >&2 +} + +usage() { + head -20 "$0" | grep '^#' | tail -n +2 | sed 's/^# \?//' + exit 0 +} + +cleanup() { + if [[ -n "${MOUNT_POINT:-}" ]] && mountpoint -q "${MOUNT_POINT}" 2>/dev/null; then + umount "${MOUNT_POINT}" || true + fi + if [[ -n "${LOOP_DEV:-}" ]]; then + losetup -d "${LOOP_DEV}" 2>/dev/null || true + fi + if [[ -n "${MOUNT_POINT:-}" ]]; then + rmdir "${MOUNT_POINT}" 2>/dev/null || true + fi +} + +trap cleanup EXIT + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --output) + OUTPUT_IMG="$2" + shift 2 + ;; + --size) + IMG_SIZE_MB="$2" + shift 2 + ;; + --uki) + UKI_PATH="$2" + shift 2 + ;; + --deps) + DEPS_DIR="$2" + shift 2 + ;; + --config) + CONFIG_DIR="$2" + shift 2 + ;; + --label) + ESP_LABEL="$2" + shift 2 + ;; + -h|--help) + usage + ;; + *) + log_error "Unknown option: $1" + usage + ;; + esac +done + +# Check for root privileges (needed for loop devices and mounting) +if [[ $EUID -ne 0 ]]; then + log_warn "This script typically requires root privileges for loop devices." + log_warn "Attempting to continue, but may fail..." +fi + +# Create output directory +mkdir -p "$(dirname "${OUTPUT_IMG}")" + +log_info "Creating BitBoot USB image..." +log_info "Output: ${OUTPUT_IMG}" +log_info "Size: ${IMG_SIZE_MB}MB" + +# Create empty image file +log_info "Creating empty image file..." +dd if=/dev/zero of="${OUTPUT_IMG}" bs=1M count="${IMG_SIZE_MB}" status=progress 2>/dev/null || \ + truncate -s "${IMG_SIZE_MB}M" "${OUTPUT_IMG}" + +# Create GPT partition table with EFI System Partition +log_info "Creating GPT partition table..." +if command -v sgdisk >/dev/null 2>&1; then + sgdisk --zap-all "${OUTPUT_IMG}" >/dev/null 2>&1 || true + sgdisk --new=1:2048:0 --typecode=1:EF00 --change-name=1:"${ESP_LABEL}" "${OUTPUT_IMG}" +elif command -v parted >/dev/null 2>&1; then + parted -s "${OUTPUT_IMG}" mklabel gpt + parted -s "${OUTPUT_IMG}" mkpart "${ESP_LABEL}" fat32 1MiB 100% + parted -s "${OUTPUT_IMG}" set 1 esp on +else + log_error "Neither sgdisk nor parted found. Cannot create partition table." + exit 1 +fi + +# Set up loop device +log_info "Setting up loop device..." +LOOP_DEV=$(losetup --find --show --partscan "${OUTPUT_IMG}") +log_info "Loop device: ${LOOP_DEV}" + +# Wait for partition device to appear +sleep 1 +PART_DEV="${LOOP_DEV}p1" +if [[ ! -b "${PART_DEV}" ]]; then + # Try alternative naming + PART_DEV="${LOOP_DEV}1" +fi +if [[ ! -b "${PART_DEV}" ]]; then + log_error "Partition device not found: ${PART_DEV}" + exit 1 +fi + +# Format ESP as FAT32 +log_info "Formatting ESP as FAT32..." +mkfs.vfat -F 32 -n "${ESP_LABEL}" "${PART_DEV}" + +# Mount ESP +MOUNT_POINT=$(mktemp -d) +log_info "Mounting ESP at ${MOUNT_POINT}..." +mount "${PART_DEV}" "${MOUNT_POINT}" + +# Create directory structure +log_info "Creating directory structure..." +mkdir -p "${MOUNT_POINT}/EFI/BOOT" +mkdir -p "${MOUNT_POINT}/EFI/bitboot" +mkdir -p "${MOUNT_POINT}/EFI/zbm" +mkdir -p "${MOUNT_POINT}/EFI/netboot" +mkdir -p "${MOUNT_POINT}/loader/entries" +mkdir -p "${MOUNT_POINT}/alpine" +mkdir -p "${MOUNT_POINT}/nixos" +mkdir -p "${MOUNT_POINT}/vyos" + +# Copy BitBoot UKIs +if [[ -d "${BUILD_DIR}/efi" ]]; then + log_info "Installing BitBoot UKIs..." + mkdir -p "${MOUNT_POINT}/EFI/bitboot" + cp "${BUILD_DIR}/efi/"*.efi "${MOUNT_POINT}/EFI/bitboot/" 2>/dev/null || true + + # Use bitboot.efi as the default UEFI boot entry + if [[ -f "${BUILD_DIR}/efi/bitboot.efi" ]]; then + cp "${BUILD_DIR}/efi/bitboot.efi" "${MOUNT_POINT}/EFI/BOOT/BOOTX64.EFI" + fi +elif [[ -f "${UKI_PATH}" ]]; then + log_info "Installing BitBoot UKI (legacy single-file mode)..." + mkdir -p "${MOUNT_POINT}/EFI/bitboot" + cp "${UKI_PATH}" "${MOUNT_POINT}/EFI/BOOT/BOOTX64.EFI" + cp "${UKI_PATH}" "${MOUNT_POINT}/EFI/bitboot/bitboot.efi" +else + log_warn "No BitBoot UKI found" +fi + +# Copy ZFSBootMenu +if [[ -f "${DEPS_DIR}/zbm/zfsbootmenu.efi" ]]; then + log_info "Installing ZFSBootMenu..." + cp "${DEPS_DIR}/zbm/zfsbootmenu.efi" "${MOUNT_POINT}/EFI/zbm/" +fi + +# Copy netboot.xyz +if [[ -f "${DEPS_DIR}/netboot/netboot.xyz.efi" ]]; then + log_info "Installing netboot.xyz..." + cp "${DEPS_DIR}/netboot/netboot.xyz.efi" "${MOUNT_POINT}/EFI/netboot/" +fi +if [[ -f "${DEPS_DIR}/netboot/netboot.xyz-snponly.efi" ]]; then + cp "${DEPS_DIR}/netboot/netboot.xyz-snponly.efi" "${MOUNT_POINT}/EFI/netboot/" +fi + +# Copy Alpine Linux files +if [[ -d "${DEPS_DIR}/alpine" ]]; then + log_info "Installing Alpine Linux files..." + cp -r "${DEPS_DIR}/alpine/"* "${MOUNT_POINT}/alpine/" 2>/dev/null || true +fi + +# Copy loader configuration +if [[ -f "${CONFIG_DIR}/loader/loader.conf" ]]; then + log_info "Installing boot loader configuration..." + cp "${CONFIG_DIR}/loader/loader.conf" "${MOUNT_POINT}/loader/" +fi + +# Copy boot entries +if [[ -d "${CONFIG_DIR}/loader/entries" ]]; then + log_info "Installing boot entries..." + cp "${CONFIG_DIR}/loader/entries/"*.conf "${MOUNT_POINT}/loader/entries/" 2>/dev/null || true +fi + +# Create a startup.nsh for UEFI shell fallback +cat > "${MOUNT_POINT}/startup.nsh" << 'EOF' +@echo -off +echo BitBoot - Multiboot USB System +echo. +echo Starting BitBoot... +\EFI\BOOT\BOOTX64.EFI +EOF + +# Sync and unmount +log_info "Syncing and unmounting..." +sync +umount "${MOUNT_POINT}" +losetup -d "${LOOP_DEV}" +LOOP_DEV="" +rmdir "${MOUNT_POINT}" +MOUNT_POINT="" + +# Show result +log_info "USB image created successfully!" +log_info "Image: ${OUTPUT_IMG}" +log_info "Size: $(du -h "${OUTPUT_IMG}" | cut -f1)" +echo "" +log_info "To write to USB drive:" +echo " sudo dd if=${OUTPUT_IMG} of=/dev/sdX bs=4M status=progress conv=fsync" +echo "" +log_info "Or use a GUI tool like:" +echo " - balenaEtcher" +echo " - Rufus (Windows)" +echo " - GNOME Disks" diff --git a/scripts/fetch-deps.sh b/scripts/fetch-deps.sh new file mode 100644 index 0000000..5c0dba7 --- /dev/null +++ b/scripts/fetch-deps.sh @@ -0,0 +1,210 @@ +#!/bin/bash +# BitBoot Dependencies Fetcher +# Downloads ZFSBootMenu, netboot.xyz, and other required components +# +# Usage: ./fetch-deps.sh [options] +# +# Options: +# --output DIR Output directory (default: ./build/deps) +# --zbm-version VER ZFSBootMenu version (default: latest) +# --netboot-version Netboot.xyz version (default: latest) +# --alpine-version Alpine Linux version (default: v3.21) +# --skip-zbm Skip ZFSBootMenu download +# --skip-netboot Skip netboot.xyz download +# --skip-alpine Skip Alpine Linux download +# -h, --help Show this help message + +set -euo pipefail + +# Default values +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +OUTPUT_DIR="${PROJECT_DIR}/build/deps" +ZBM_VERSION="latest" +NETBOOT_VERSION="latest" +ALPINE_VERSION="v3.21" +SKIP_ZBM=false +SKIP_NETBOOT=false +SKIP_ALPINE=false + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log_info() { + echo -e "${GREEN}[INFO]${NC} $*" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $*" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $*" >&2 +} + +usage() { + head -20 "$0" | grep '^#' | tail -n +2 | sed 's/^# \?//' + exit 0 +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --output) + OUTPUT_DIR="$2" + shift 2 + ;; + --zbm-version) + ZBM_VERSION="$2" + shift 2 + ;; + --netboot-version) + NETBOOT_VERSION="$2" + shift 2 + ;; + --alpine-version) + ALPINE_VERSION="$2" + shift 2 + ;; + --skip-zbm) + SKIP_ZBM=true + shift + ;; + --skip-netboot) + SKIP_NETBOOT=true + shift + ;; + --skip-alpine) + SKIP_ALPINE=true + shift + ;; + -h|--help) + usage + ;; + *) + log_error "Unknown option: $1" + usage + ;; + esac +done + +# Create output directories +mkdir -p "${OUTPUT_DIR}/zbm" +mkdir -p "${OUTPUT_DIR}/netboot" +mkdir -p "${OUTPUT_DIR}/alpine" + +# Function to download with retries +download_file() { + local url="$1" + local output="$2" + local retries=3 + + log_info "Downloading: ${url}" + for ((i=1; i<=retries; i++)); do + if curl -fsSL --connect-timeout 30 --max-time 300 -o "${output}" "${url}"; then + log_info "Downloaded: ${output}" + return 0 + else + log_warn "Download attempt ${i}/${retries} failed" + sleep 2 + fi + done + log_error "Failed to download: ${url}" + return 1 +} + +# Fetch ZFSBootMenu +fetch_zfsbootmenu() { + log_info "Fetching ZFSBootMenu..." + + local zbm_api="https://api.github.com/repos/zbm-dev/zfsbootmenu/releases" + local zbm_url + + if [[ "${ZBM_VERSION}" == "latest" ]]; then + zbm_url=$(curl -fsSL "${zbm_api}/latest" | grep -oP '"browser_download_url":\s*"\K[^"]+\.EFI' | head -1) + else + zbm_url=$(curl -fsSL "${zbm_api}/tags/${ZBM_VERSION}" | grep -oP '"browser_download_url":\s*"\K[^"]+\.EFI' | head -1) + fi + + if [[ -z "${zbm_url}" ]]; then + log_error "Could not find ZFSBootMenu EFI download URL" + return 1 + fi + + download_file "${zbm_url}" "${OUTPUT_DIR}/zbm/zfsbootmenu.efi" + + # Also download the recovery image if available + local zbm_recovery_url + zbm_recovery_url=$(curl -fsSL "${zbm_api}/latest" | grep -oP '"browser_download_url":\s*"\K[^"]+recovery[^"]+\.EFI' | head -1 || true) + if [[ -n "${zbm_recovery_url}" ]]; then + download_file "${zbm_recovery_url}" "${OUTPUT_DIR}/zbm/zfsbootmenu-recovery.efi" || true + fi +} + +# Fetch netboot.xyz +fetch_netboot() { + log_info "Fetching netboot.xyz..." + + local netboot_api="https://api.github.com/repos/netbootxyz/netboot.xyz/releases" + local netboot_url + + if [[ "${NETBOOT_VERSION}" == "latest" ]]; then + netboot_url=$(curl -fsSL "${netboot_api}/latest" | grep -oP '"browser_download_url":\s*"\K[^"]+netboot\.xyz\.efi"' | tr -d '"' | head -1) + if [[ -z "${netboot_url}" ]]; then + # Fallback to direct URL + netboot_url="https://boot.netboot.xyz/ipxe/netboot.xyz.efi" + fi + else + netboot_url="https://github.com/netbootxyz/netboot.xyz/releases/download/${NETBOOT_VERSION}/netboot.xyz.efi" + fi + + download_file "${netboot_url}" "${OUTPUT_DIR}/netboot/netboot.xyz.efi" + + # Also download the SNPONLY version for broader hardware support + local netboot_snp_url="https://boot.netboot.xyz/ipxe/netboot.xyz-snponly.efi" + download_file "${netboot_snp_url}" "${OUTPUT_DIR}/netboot/netboot.xyz-snponly.efi" || true +} + +# Fetch Alpine Linux netboot files +fetch_alpine() { + log_info "Fetching Alpine Linux netboot files..." + + local alpine_base="https://dl-cdn.alpinelinux.org/alpine/${ALPINE_VERSION}/releases/x86_64/netboot" + + # Download kernel + download_file "${alpine_base}/vmlinuz-lts" "${OUTPUT_DIR}/alpine/vmlinuz-lts" + + # Download initramfs + download_file "${alpine_base}/initramfs-lts" "${OUTPUT_DIR}/alpine/initramfs-lts" + + # Download modloop (for ZFS and other modules) + download_file "${alpine_base}/modloop-lts" "${OUTPUT_DIR}/alpine/modloop-lts" + + # Download config for reference + download_file "${alpine_base}/config-lts" "${OUTPUT_DIR}/alpine/config-lts" || true +} + +# Main execution +log_info "Starting dependency fetch..." +log_info "Output directory: ${OUTPUT_DIR}" + +if [[ "${SKIP_ZBM}" != "true" ]]; then + fetch_zfsbootmenu || log_warn "ZFSBootMenu fetch failed, continuing..." +fi + +if [[ "${SKIP_NETBOOT}" != "true" ]]; then + fetch_netboot || log_warn "netboot.xyz fetch failed, continuing..." +fi + +if [[ "${SKIP_ALPINE}" != "true" ]]; then + fetch_alpine || log_warn "Alpine Linux fetch failed, continuing..." +fi + +# List downloaded files +log_info "Downloaded dependencies:" +find "${OUTPUT_DIR}" -type f -exec ls -lh {} \; 2>/dev/null || true + +log_info "Dependency fetch complete!" From ae64c166d5d451ae6a4ec830672ece6bf37379ee Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 04:03:17 +0000 Subject: [PATCH 03/12] Fix code review issues and add workflow permissions Co-authored-by: danielbodnar <1790726+danielbodnar@users.noreply.github.com> --- .github/workflows/build.yml | 7 +++++++ config/cmdline.d/profiles/fedora-coreos.conf | 4 ++++ config/cmdline.d/profiles/vyos.conf | 4 ++++ scripts/create-usb.sh | 9 +++++---- scripts/fetch-deps.sh | 6 +++++- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3d3b01d..7f8210a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,10 +32,15 @@ on: env: BUILD_DIR: build +permissions: + contents: read + jobs: build: name: Build BitBoot runs-on: ubuntu-latest + permissions: + contents: read container: image: registry.fedoraproject.org/fedora:41 options: --privileged @@ -151,6 +156,8 @@ jobs: validate: name: Validate Configuration runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository diff --git a/config/cmdline.d/profiles/fedora-coreos.conf b/config/cmdline.d/profiles/fedora-coreos.conf index e5f271d..aeec40f 100644 --- a/config/cmdline.d/profiles/fedora-coreos.conf +++ b/config/cmdline.d/profiles/fedora-coreos.conf @@ -1,5 +1,9 @@ # Fedora CoreOS network pull cmdline # Embedded in fedora-coreos.efi UKI +# +# NOTE: The Fedora CoreOS URL contains a specific build version. +# Update this URL periodically from: +# https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/ rd.systemd.pull=raw,machine,verify=no,blockdev:rootdisk:https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/41.20250117.3.0/x86_64/fedora-coreos-41.20250117.3.0-metal.x86_64.raw.xz root=gpt-auto diff --git a/config/cmdline.d/profiles/vyos.conf b/config/cmdline.d/profiles/vyos.conf index 5b30f8c..9a6c1ee 100644 --- a/config/cmdline.d/profiles/vyos.conf +++ b/config/cmdline.d/profiles/vyos.conf @@ -1,5 +1,9 @@ # VyOS 1.5 Rolling network boot cmdline # Embedded in vyos.efi UKI +# +# NOTE: The VyOS URL contains a specific rolling build date. +# Update this URL periodically from: +# https://github.com/vyos/vyos-rolling-nightly-builds/releases/ boot=live fetch=https://github.com/vyos/vyos-rolling-nightly-builds/releases/download/1.5-rolling-202501200023/vyos-1.5-rolling-202501200023-amd64.iso diff --git a/scripts/create-usb.sh b/scripts/create-usb.sh index eb5a247..5f1ced9 100644 --- a/scripts/create-usb.sh +++ b/scripts/create-usb.sh @@ -172,14 +172,15 @@ mkdir -p "${MOUNT_POINT}/nixos" mkdir -p "${MOUNT_POINT}/vyos" # Copy BitBoot UKIs -if [[ -d "${BUILD_DIR}/efi" ]]; then +UKI_BUILD_DIR="$(dirname "${UKI_PATH}")" +if [[ -d "${UKI_BUILD_DIR}/efi" ]]; then log_info "Installing BitBoot UKIs..." mkdir -p "${MOUNT_POINT}/EFI/bitboot" - cp "${BUILD_DIR}/efi/"*.efi "${MOUNT_POINT}/EFI/bitboot/" 2>/dev/null || true + cp "${UKI_BUILD_DIR}/efi/"*.efi "${MOUNT_POINT}/EFI/bitboot/" 2>/dev/null || true # Use bitboot.efi as the default UEFI boot entry - if [[ -f "${BUILD_DIR}/efi/bitboot.efi" ]]; then - cp "${BUILD_DIR}/efi/bitboot.efi" "${MOUNT_POINT}/EFI/BOOT/BOOTX64.EFI" + if [[ -f "${UKI_BUILD_DIR}/efi/bitboot.efi" ]]; then + cp "${UKI_BUILD_DIR}/efi/bitboot.efi" "${MOUNT_POINT}/EFI/BOOT/BOOTX64.EFI" fi elif [[ -f "${UKI_PATH}" ]]; then log_info "Installing BitBoot UKI (legacy single-file mode)..." diff --git a/scripts/fetch-deps.sh b/scripts/fetch-deps.sh index 5c0dba7..e6de3e5 100644 --- a/scripts/fetch-deps.sh +++ b/scripts/fetch-deps.sh @@ -138,7 +138,11 @@ fetch_zfsbootmenu() { # Also download the recovery image if available local zbm_recovery_url - zbm_recovery_url=$(curl -fsSL "${zbm_api}/latest" | grep -oP '"browser_download_url":\s*"\K[^"]+recovery[^"]+\.EFI' | head -1 || true) + if [[ "${ZBM_VERSION}" == "latest" ]]; then + zbm_recovery_url=$(curl -fsSL "${zbm_api}/latest" | grep -oP '"browser_download_url":\s*"\K[^"]+recovery[^"]+\.EFI' | head -1 || true) + else + zbm_recovery_url=$(curl -fsSL "${zbm_api}/tags/${ZBM_VERSION}" | grep -oP '"browser_download_url":\s*"\K[^"]+recovery[^"]+\.EFI' | head -1 || true) + fi if [[ -n "${zbm_recovery_url}" ]]; then download_file "${zbm_recovery_url}" "${OUTPUT_DIR}/zbm/zfsbootmenu-recovery.efi" || true fi From 4b38281228da4375cd81b61711ba6dd63c42a9ca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 04:15:59 +0000 Subject: [PATCH 04/12] Fix kernel detection for containerized builds Co-authored-by: danielbodnar <1790726+danielbodnar@users.noreply.github.com> --- scripts/build-initramfs.sh | 25 +++++++++++++++++++++++++ scripts/build-uki.sh | 28 +++++++++++++++++++++------- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/scripts/build-initramfs.sh b/scripts/build-initramfs.sh index b4d3325..b8a5f92 100644 --- a/scripts/build-initramfs.sh +++ b/scripts/build-initramfs.sh @@ -84,10 +84,35 @@ mkdir -p "$(dirname "${OUTPUT_FILE}")" # Detect kernel version if [[ -z "${KERNEL_VERSION}" ]]; then + # First try uname -r (works when running on the same kernel) KERNEL_VERSION=$(uname -r) + + # Check if kernel modules exist for this version + if [[ ! -d "/lib/modules/${KERNEL_VERSION}" ]]; then + log_warn "Kernel modules not found for ${KERNEL_VERSION}" + log_info "Searching for installed kernel in /lib/modules/..." + + # Find the newest installed kernel in /lib/modules/ + KERNEL_VERSION=$(ls -1 /lib/modules/ 2>/dev/null | sort -V | tail -1) + + if [[ -z "${KERNEL_VERSION}" ]] || [[ ! -d "/lib/modules/${KERNEL_VERSION}" ]]; then + log_error "No kernel modules found in /lib/modules/" + log_error "Please install a kernel package or specify --kernel VERSION" + exit 1 + fi + log_info "Found installed kernel: ${KERNEL_VERSION}" + fi fi log_info "Building initramfs for kernel: ${KERNEL_VERSION}" +# Verify kernel modules directory exists +if [[ ! -d "/lib/modules/${KERNEL_VERSION}" ]]; then + log_error "Kernel modules directory not found: /lib/modules/${KERNEL_VERSION}" + log_error "Available kernels:" + ls -1 /lib/modules/ 2>/dev/null || echo " (none)" + exit 1 +fi + # Check for dracut if ! command -v dracut >/dev/null 2>&1; then log_error "dracut not found. Please install dracut." diff --git a/scripts/build-uki.sh b/scripts/build-uki.sh index 377ab97..fdbc0f1 100644 --- a/scripts/build-uki.sh +++ b/scripts/build-uki.sh @@ -113,21 +113,30 @@ mkdir -p "${BUILD_DIR}/efi" # Auto-detect kernel if not specified if [[ -z "${KERNEL}" ]]; then - # Try common kernel paths + # Try common kernel paths (Fedora, Arch, Alpine, etc.) for kpath in \ /boot/vmlinuz-linux-lts \ /boot/vmlinuz-linux \ - /lib/modules/*/vmlinuz \ - /boot/vmlinuz + /boot/vmlinuz \ + /lib/modules/*/vmlinuz do - if [[ -f "${kpath}" ]]; then - KERNEL="${kpath}" - break - fi + # Handle glob patterns + for k in ${kpath}; do + if [[ -f "${k}" ]]; then + KERNEL="${k}" + break 2 + fi + done done + # Fedora stores kernels with version suffix if [[ -z "${KERNEL}" ]]; then + KERNEL=$(ls -1 /boot/vmlinuz-* 2>/dev/null | sort -V | tail -1) + fi + + if [[ -z "${KERNEL}" ]] || [[ ! -f "${KERNEL}" ]]; then log_error "Could not find kernel image. Use --kernel to specify." + log_error "Searched in /boot/vmlinuz*, /lib/modules/*/vmlinuz" exit 1 fi fi @@ -148,7 +157,12 @@ if [[ -z "${INITRD}" ]]; then fi done + # Fedora stores initramfs with version suffix if [[ -z "${INITRD}" ]]; then + INITRD=$(ls -1 /boot/initramfs-*.img 2>/dev/null | grep -v rescue | sort -V | tail -1) + fi + + if [[ -z "${INITRD}" ]] || [[ ! -f "${INITRD}" ]]; then log_warn "No initramfs found. Building one..." "${SCRIPT_DIR}/build-initramfs.sh" --output "${BUILD_DIR}/initramfs-bitboot.img" INITRD="${BUILD_DIR}/initramfs-bitboot.img" From 1b4873553c06526337c92ed8a09ffb5a311e189b Mon Sep 17 00:00:00 2001 From: Daniel Bodnar <1790726+danielbodnar@users.noreply.github.com> Date: Sat, 29 Nov 2025 22:33:54 -0600 Subject: [PATCH 05/12] Update config/cmdline.d/profiles/vyos.conf Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Daniel Bodnar <1790726+danielbodnar@users.noreply.github.com> --- config/cmdline.d/profiles/vyos.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/cmdline.d/profiles/vyos.conf b/config/cmdline.d/profiles/vyos.conf index 9a6c1ee..a186926 100644 --- a/config/cmdline.d/profiles/vyos.conf +++ b/config/cmdline.d/profiles/vyos.conf @@ -1,10 +1,10 @@ # VyOS 1.5 Rolling network boot cmdline # Embedded in vyos.efi UKI # -# NOTE: The VyOS URL contains a specific rolling build date. -# Update this URL periodically from: +# NOTE: The VyOS URL below contains a specific rolling build date and will become outdated quickly. +# IMPORTANT: You MUST update the URL below regularly to use the latest rolling build. +# Get the latest build URL from: # https://github.com/vyos/vyos-rolling-nightly-builds/releases/ - boot=live fetch=https://github.com/vyos/vyos-rolling-nightly-builds/releases/download/1.5-rolling-202501200023/vyos-1.5-rolling-202501200023-amd64.iso ip=dhcp From e50707f3fdaf2a3da0e8e848ce8b75c4a512b3a0 Mon Sep 17 00:00:00 2001 From: Daniel Bodnar <1790726+danielbodnar@users.noreply.github.com> Date: Sat, 29 Nov 2025 22:34:39 -0600 Subject: [PATCH 06/12] Update initramfs/modules/bitboot-pull.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Daniel Bodnar <1790726+danielbodnar@users.noreply.github.com> --- initramfs/modules/bitboot-pull.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/initramfs/modules/bitboot-pull.sh b/initramfs/modules/bitboot-pull.sh index 40a64fa..b61a9a6 100644 --- a/initramfs/modules/bitboot-pull.sh +++ b/initramfs/modules/bitboot-pull.sh @@ -80,7 +80,7 @@ install() { mkdir -p "${initdir}/run/systemd/machines" # Enable socket activation for importd - systemctl -q --root "$initdir" enable systemd-importd.socket 2>/dev/null || true + systemctl -q --root "${initdir}" enable systemd-importd.socket 2>/dev/null || true # Install a hook script to ensure network is up before pulling inst_hook pre-mount 10 "$moddir/bitboot-pull-premount.sh" From 5dc8a7b759434212fde6a279c08519608b6f5d94 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 04:35:12 +0000 Subject: [PATCH 07/12] Fix GITHUB_REPOSITORY variable in release.yml heredoc Co-authored-by: danielbodnar <1790726+danielbodnar@users.noreply.github.com> --- .github/workflows/release.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a016037..9d65526 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -90,6 +90,7 @@ jobs: id: release_notes run: | VERSION="${{ steps.version.outputs.version }}" + REPO="${{ github.repository }}" cat > release_notes.md << EOF # BitBoot ${VERSION} @@ -120,7 +121,7 @@ jobs: \`\`\`bash # Download the compressed USB image - wget https://github.com/\${GITHUB_REPOSITORY}/releases/download/${VERSION}/bitboot-x86_64.img.xz + wget https://github.com/${REPO}/releases/download/${VERSION}/bitboot-x86_64.img.xz # Decompress and write to USB (replace /dev/sdX with your USB device) xzcat bitboot-x86_64.img.xz | sudo dd of=/dev/sdX bs=4M status=progress conv=fsync @@ -141,7 +142,7 @@ jobs: ## Documentation - See the [README](https://github.com/\${GITHUB_REPOSITORY}#readme) for detailed usage instructions. + See the [README](https://github.com/${REPO}#readme) for detailed usage instructions. EOF cat release_notes.md From 3379ff0abb6d9f68e20599f347e67141d168fa58 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 04:38:13 +0000 Subject: [PATCH 08/12] Fix code review issues: simplify cat command and quote $initdir variable Co-authored-by: danielbodnar <1790726+danielbodnar@users.noreply.github.com> --- initramfs/hooks/bitboot-network-pull.sh | 2 +- scripts/build-uki.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/initramfs/hooks/bitboot-network-pull.sh b/initramfs/hooks/bitboot-network-pull.sh index 9f3f1a1..fdec52f 100644 --- a/initramfs/hooks/bitboot-network-pull.sh +++ b/initramfs/hooks/bitboot-network-pull.sh @@ -56,7 +56,7 @@ install() { mkdir -p "${initdir}/run/machines" # Enable the services - systemctl -q --root "$initdir" enable systemd-importd.socket 2>/dev/null || true + systemctl -q --root "${initdir}" enable systemd-importd.socket 2>/dev/null || true } installkernel() { diff --git a/scripts/build-uki.sh b/scripts/build-uki.sh index fdbc0f1..b617a78 100644 --- a/scripts/build-uki.sh +++ b/scripts/build-uki.sh @@ -203,7 +203,7 @@ build_uki() { grep -v '^\s*#' "${cmdline_file}" | grep -v '^\s*$' | tr '\n' ' ' > "${cmdline_temp}" echo "" >> "${cmdline_temp}" - log_info " Cmdline: $(cat ${cmdline_temp})" + log_info " Cmdline: $(tr '\n' ' ' < "${cmdline_temp}")" if command -v ukify >/dev/null 2>&1; then local ukify_args=( From 8817a2fc798e122bb38b43b236fff4e6f97faa90 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 04:42:49 +0000 Subject: [PATCH 09/12] Update Alpine Linux URLs to use edge release and remove non-existent config-lts Co-authored-by: danielbodnar <1790726+danielbodnar@users.noreply.github.com> --- config/cmdline.d/profiles/alpine-netboot.conf | 4 ++-- config/cmdline.d/profiles/alpine-zfs-installer.conf | 4 ++-- scripts/fetch-deps.sh | 6 ++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/config/cmdline.d/profiles/alpine-netboot.conf b/config/cmdline.d/profiles/alpine-netboot.conf index 23e80c4..632db06 100644 --- a/config/cmdline.d/profiles/alpine-netboot.conf +++ b/config/cmdline.d/profiles/alpine-netboot.conf @@ -1,8 +1,8 @@ # Alpine Linux netboot cmdline # Embedded in alpine-netboot.efi UKI -modloop=https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/x86_64/netboot/modloop-lts -alpine_repo=https://dl-cdn.alpinelinux.org/alpine/v3.21/main +modloop=https://dl-cdn.alpinelinux.org/alpine/edge/releases/x86_64/netboot/modloop-lts +alpine_repo=https://dl-cdn.alpinelinux.org/alpine/edge/main ip=dhcp console=tty0 console=ttyS0,115200n8 diff --git a/config/cmdline.d/profiles/alpine-zfs-installer.conf b/config/cmdline.d/profiles/alpine-zfs-installer.conf index 97d1db4..05b9f60 100644 --- a/config/cmdline.d/profiles/alpine-zfs-installer.conf +++ b/config/cmdline.d/profiles/alpine-zfs-installer.conf @@ -1,8 +1,8 @@ # Alpine Linux ZFS Installer cmdline # Embedded in alpine-zfs-installer.efi UKI -modloop=https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/x86_64/netboot/modloop-lts -alpine_repo=https://dl-cdn.alpinelinux.org/alpine/v3.21/main +modloop=https://dl-cdn.alpinelinux.org/alpine/edge/releases/x86_64/netboot/modloop-lts +alpine_repo=https://dl-cdn.alpinelinux.org/alpine/edge/main ip=dhcp console=tty0 console=ttyS0,115200n8 diff --git a/scripts/fetch-deps.sh b/scripts/fetch-deps.sh index e6de3e5..604dcae 100644 --- a/scripts/fetch-deps.sh +++ b/scripts/fetch-deps.sh @@ -8,7 +8,7 @@ # --output DIR Output directory (default: ./build/deps) # --zbm-version VER ZFSBootMenu version (default: latest) # --netboot-version Netboot.xyz version (default: latest) -# --alpine-version Alpine Linux version (default: v3.21) +# --alpine-version Alpine Linux version (default: edge) # --skip-zbm Skip ZFSBootMenu download # --skip-netboot Skip netboot.xyz download # --skip-alpine Skip Alpine Linux download @@ -22,7 +22,7 @@ PROJECT_DIR="$(dirname "$SCRIPT_DIR")" OUTPUT_DIR="${PROJECT_DIR}/build/deps" ZBM_VERSION="latest" NETBOOT_VERSION="latest" -ALPINE_VERSION="v3.21" +ALPINE_VERSION="edge" SKIP_ZBM=false SKIP_NETBOOT=false SKIP_ALPINE=false @@ -187,8 +187,6 @@ fetch_alpine() { # Download modloop (for ZFS and other modules) download_file "${alpine_base}/modloop-lts" "${OUTPUT_DIR}/alpine/modloop-lts" - # Download config for reference - download_file "${alpine_base}/config-lts" "${OUTPUT_DIR}/alpine/config-lts" || true } # Main execution From d8bbaec74e7dd8ce29551b8a08b4cce1a6e17635 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 04:46:20 +0000 Subject: [PATCH 10/12] Clarify systemd version requirement comment (v258 introduced rd.systemd.pull) Co-authored-by: danielbodnar <1790726+danielbodnar@users.noreply.github.com> --- initramfs/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/initramfs/config.yaml b/initramfs/config.yaml index f991aae..ff03916 100644 --- a/initramfs/config.yaml +++ b/initramfs/config.yaml @@ -2,7 +2,7 @@ # This file defines how the custom initramfs is built using dracut # # The initramfs must contain: -# - systemd v258+ with rd.systemd.pull support +# - systemd v258 or higher (rd.systemd.pull was introduced in v258) # - Network stack (networkd, resolved) # - Compression support (xz, zstd, gzip) # - Loop device support From 5325e9c12ba0acd2db552cd8a79f9bbba0793ecf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 04:49:48 +0000 Subject: [PATCH 11/12] Update Fedora version from 41 to 43 across all configurations Co-authored-by: danielbodnar <1790726+danielbodnar@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- Containerfile | 4 ++-- config/cmdline.d/profiles/fedora-coreos.conf | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7f8210a..e1e2923 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: permissions: contents: read container: - image: registry.fedoraproject.org/fedora:41 + image: registry.fedoraproject.org/fedora:43 options: --privileged steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9d65526..4201ed6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: name: Create Release runs-on: ubuntu-latest container: - image: registry.fedoraproject.org/fedora:41 + image: registry.fedoraproject.org/fedora:43 options: --privileged steps: diff --git a/Containerfile b/Containerfile index 936826b..0c61951 100644 --- a/Containerfile +++ b/Containerfile @@ -4,7 +4,7 @@ # Build: podman build -t bitboot-builder . # Run: podman run --rm -v $(pwd):/workspace:Z bitboot-builder make all -FROM registry.fedoraproject.org/fedora:41 +FROM registry.fedoraproject.org/fedora:43 LABEL maintainer="BitBoot Project" LABEL description="Build environment for BitBoot multiboot USB system" @@ -38,7 +38,7 @@ RUN dnf install -y \ wget \ # ZFS (from RPM Fusion) && dnf install -y \ - https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-41.noarch.rpm \ + https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-43.noarch.rpm \ && dnf install -y zfs || true \ # Kernel and firmware && dnf install -y \ diff --git a/config/cmdline.d/profiles/fedora-coreos.conf b/config/cmdline.d/profiles/fedora-coreos.conf index aeec40f..8df7c68 100644 --- a/config/cmdline.d/profiles/fedora-coreos.conf +++ b/config/cmdline.d/profiles/fedora-coreos.conf @@ -5,7 +5,7 @@ # Update this URL periodically from: # https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/ -rd.systemd.pull=raw,machine,verify=no,blockdev:rootdisk:https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/41.20250117.3.0/x86_64/fedora-coreos-41.20250117.3.0-metal.x86_64.raw.xz +rd.systemd.pull=raw,machine,verify=no,blockdev:rootdisk:https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/43.20250521.3.0/x86_64/fedora-coreos-43.20250521.3.0-metal.x86_64.raw.xz root=gpt-auto ip=dhcp console=tty0 From 204c3e17a39379286f5500bdacb076abf7c4099c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 05:04:30 +0000 Subject: [PATCH 12/12] Split workflows into modular jobs and replace Fedora CoreOS with uCore Co-authored-by: danielbodnar <1790726+danielbodnar@users.noreply.github.com> --- .github/workflows/build-uki.yml | 98 ++++++ .github/workflows/build.yml | 315 ++++++++++++------ .github/workflows/release.yml | 169 ++++++++-- .../{fedora-coreos.conf => ucore.conf} | 12 +- config/loader/entries/fedora-coreos.conf | 11 - config/loader/entries/ucore.conf | 16 + 6 files changed, 473 insertions(+), 148 deletions(-) create mode 100644 .github/workflows/build-uki.yml rename config/cmdline.d/profiles/{fedora-coreos.conf => ucore.conf} (53%) delete mode 100644 config/loader/entries/fedora-coreos.conf create mode 100644 config/loader/entries/ucore.conf diff --git a/.github/workflows/build-uki.yml b/.github/workflows/build-uki.yml new file mode 100644 index 0000000..c6e7358 --- /dev/null +++ b/.github/workflows/build-uki.yml @@ -0,0 +1,98 @@ +# Reusable workflow for building individual UKI profiles +# This workflow can be called by other workflows to build a single profile +# +# Usage: +# jobs: +# build-profile: +# uses: ./.github/workflows/build-uki.yml +# with: +# profile: fedora-coreos + +name: Build UKI Profile + +on: + workflow_call: + inputs: + profile: + description: 'Profile name to build (e.g., fedora-coreos, alpine-netboot)' + required: true + type: string + outputs: + artifact-name: + description: 'Name of the uploaded artifact' + value: ${{ jobs.build.outputs.artifact-name }} + +env: + BUILD_DIR: build + +jobs: + build: + name: Build ${{ inputs.profile }} + runs-on: ubuntu-latest + permissions: + contents: read + container: + image: registry.fedoraproject.org/fedora:43 + options: --privileged + outputs: + artifact-name: ${{ steps.upload.outputs.artifact-name }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install build dependencies + run: | + dnf install -y \ + make \ + gcc \ + binutils \ + systemd-boot-unsigned \ + systemd-ukify \ + dracut \ + xz \ + zstd \ + gzip \ + bzip2 \ + curl \ + wget \ + kernel \ + kernel-devel \ + linux-firmware \ + util-linux \ + coreutils \ + findutils \ + file + + - name: Make scripts executable + run: chmod +x scripts/*.sh + + - name: Fetch dependencies + run: make deps + env: + BUILD_DIR: ${{ env.BUILD_DIR }} + + - name: Build initramfs + run: make initramfs + env: + BUILD_DIR: ${{ env.BUILD_DIR }} + + - name: Build UKI for ${{ inputs.profile }} + run: | + ./scripts/build-uki.sh --profile "${{ inputs.profile }}" --output "${BUILD_DIR}/efi/${{ inputs.profile }}.efi" + env: + BUILD_DIR: ${{ env.BUILD_DIR }} + + - name: Verify UKI + run: | + echo "=== UKI Info for ${{ inputs.profile }} ===" + file "${BUILD_DIR}/efi/${{ inputs.profile }}.efi" || true + ls -lh "${BUILD_DIR}/efi/${{ inputs.profile }}.efi" || true + + - name: Upload UKI artifact + id: upload + uses: actions/upload-artifact@v4 + with: + name: uki-${{ inputs.profile }} + path: ${{ env.BUILD_DIR }}/efi/${{ inputs.profile }}.efi + if-no-files-found: error diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e1e2923..c60f358 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,13 +1,16 @@ # BitBoot Build Workflow # Main CI/CD pipeline for building the BitBoot multiboot USB system # +# This workflow builds each distro profile as a separate job, allowing +# individual profiles to fail without blocking others. +# # Triggers: # - Push to main branch # - Pull requests to main branch # - Manual workflow dispatch # # Outputs: -# - bitboot.efi (Unified Kernel Image) +# - Individual UKI files per profile # - bitboot-x86_64.img (Bootable USB image) name: Build BitBoot @@ -36,8 +39,102 @@ permissions: contents: read jobs: - build: - name: Build BitBoot + # Validate configuration before building + validate: + name: Validate Configuration + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Validate loader configuration + run: | + echo "=== Checking loader.conf ===" + cat config/loader/loader.conf + echo "" + + echo "=== Checking boot entries ===" + for entry in config/loader/entries/*.conf; do + echo "--- ${entry} ---" + cat "${entry}" + echo "" + done + + - name: Validate kernel cmdline profiles + run: | + echo "=== Kernel command line profiles ===" + for conf in config/cmdline.d/profiles/*.conf; do + echo "--- ${conf} ---" + cat "${conf}" + echo "" + done + + - name: Check for shell script issues + run: | + if command -v shellcheck >/dev/null 2>&1; then + echo "Running shellcheck..." + shellcheck scripts/*.sh || true + else + echo "shellcheck not installed, skipping..." + fi + + # Build each profile as a separate job + build-ucore: + name: uCore + needs: validate + uses: ./.github/workflows/build-uki.yml + with: + profile: ucore + + build-flatcar: + name: Flatcar Linux + needs: validate + uses: ./.github/workflows/build-uki.yml + with: + profile: flatcar + + build-alpine-zfs-installer: + name: Alpine ZFS Installer + needs: validate + uses: ./.github/workflows/build-uki.yml + with: + profile: alpine-zfs-installer + + build-alpine-netboot: + name: Alpine Netboot + needs: validate + uses: ./.github/workflows/build-uki.yml + with: + profile: alpine-netboot + + build-nixos-kexec: + name: NixOS Kexec + needs: validate + uses: ./.github/workflows/build-uki.yml + with: + profile: nixos-kexec + + build-vyos: + name: VyOS + needs: validate + uses: ./.github/workflows/build-uki.yml + with: + profile: vyos + + build-bitboot: + name: BitBoot Recovery + needs: validate + uses: ./.github/workflows/build-uki.yml + with: + profile: bitboot + + # Build chainload profiles (ZFSBootMenu, netboot.xyz) + build-chainload: + name: Build Chainload Profiles + needs: validate runs-on: ubuntu-latest permissions: contents: read @@ -49,93 +146,113 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Install build dependencies + - name: Install dependencies + run: | + dnf install -y curl wget xz file + + - name: Make scripts executable + run: chmod +x scripts/*.sh + + - name: Fetch ZFSBootMenu and netboot.xyz + run: | + mkdir -p ${BUILD_DIR}/efi + ./scripts/fetch-deps.sh --skip-alpine --output ${BUILD_DIR}/deps + # Copy the EFI files + cp ${BUILD_DIR}/deps/zfsbootmenu/*.efi ${BUILD_DIR}/efi/ 2>/dev/null || true + cp ${BUILD_DIR}/deps/netboot/*.efi ${BUILD_DIR}/efi/ 2>/dev/null || true + env: + BUILD_DIR: ${{ env.BUILD_DIR }} + + - name: Verify chainload EFIs + run: | + echo "=== Chainload EFI files ===" + ls -la ${BUILD_DIR}/efi/ || true + for f in ${BUILD_DIR}/efi/*.efi; do + [ -f "$f" ] && file "$f" + done + + - name: Upload chainload artifacts + uses: actions/upload-artifact@v4 + with: + name: uki-chainload + path: ${{ env.BUILD_DIR }}/efi/ + if-no-files-found: warn + + # Assemble all UKIs into a bootable USB image + assemble-usb: + name: Assemble USB Image + needs: + - build-ucore + - build-flatcar + - build-alpine-zfs-installer + - build-alpine-netboot + - build-nixos-kexec + - build-vyos + - build-bitboot + - build-chainload + if: always() + runs-on: ubuntu-latest + permissions: + contents: read + container: + image: registry.fedoraproject.org/fedora:43 + options: --privileged + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install dependencies run: | dnf install -y \ make \ - gcc \ - binutils \ - systemd-boot-unsigned \ - systemd-ukify \ - dracut \ parted \ gdisk \ dosfstools \ e2fsprogs \ - xz \ - zstd \ - gzip \ - bzip2 \ - curl \ - wget \ - kernel \ - kernel-devel \ - linux-firmware \ util-linux \ coreutils \ findutils \ - file - - - name: Verify systemd version - run: | - echo "Checking systemd version (need v258+)..." - systemctl --version | head -1 - SYSTEMD_VER=$(systemctl --version | head -1 | awk '{print $2}') - echo "systemd version: ${SYSTEMD_VER}" + file \ + curl \ + wget \ + xz - name: Make scripts executable run: chmod +x scripts/*.sh - - name: Fetch dependencies - run: make deps - env: - BUILD_DIR: ${{ env.BUILD_DIR }} + - name: Download all UKI artifacts + uses: actions/download-artifact@v4 + with: + pattern: uki-* + path: ${{ env.BUILD_DIR }}/efi-artifacts/ + merge-multiple: false - - name: Build initramfs - run: make initramfs - env: - BUILD_DIR: ${{ env.BUILD_DIR }} + - name: Organize UKI files + run: | + mkdir -p ${BUILD_DIR}/efi + # Move all EFI files to the efi directory + find ${BUILD_DIR}/efi-artifacts -name "*.efi" -exec cp {} ${BUILD_DIR}/efi/ \; + echo "=== Collected UKI files ===" + ls -la ${BUILD_DIR}/efi/ - - name: Build UKI - run: make uki + - name: Fetch Alpine dependencies for USB + run: | + ./scripts/fetch-deps.sh --skip-zbm --skip-netboot --output ${BUILD_DIR}/deps env: BUILD_DIR: ${{ env.BUILD_DIR }} - name: Create USB image - run: make usb + run: | + ./scripts/create-usb.sh --output ${BUILD_DIR}/bitboot-x86_64.img env: BUILD_DIR: ${{ env.BUILD_DIR }} - - name: Verify build outputs + - name: Verify USB image run: | - echo "=== Build Artifacts ===" - ls -la ${BUILD_DIR}/ - echo "" - echo "=== UKI Directory ===" - ls -la ${BUILD_DIR}/efi/ || true - echo "" - echo "=== UKI Info ===" - for uki in ${BUILD_DIR}/efi/*.efi; do - if [[ -f "${uki}" ]]; then - echo "--- $(basename ${uki}) ---" - file "${uki}" || true - ls -lh "${uki}" || true - fi - done - echo "" echo "=== USB Image Info ===" file ${BUILD_DIR}/bitboot-x86_64.img || true ls -lh ${BUILD_DIR}/bitboot-x86_64.img || true - echo "" - echo "=== Dependencies ===" - find ${BUILD_DIR}/deps -type f -exec ls -lh {} \; || true - - - name: Upload UKI artifacts - uses: actions/upload-artifact@v4 - with: - name: bitboot-ukis - path: ${{ env.BUILD_DIR }}/efi/ - if-no-files-found: warn - name: Upload USB image artifact uses: actions/upload-artifact@v4 @@ -145,51 +262,37 @@ jobs: if-no-files-found: warn compression-level: 9 - - name: Upload dependencies artifact - uses: actions/upload-artifact@v4 - with: - name: bitboot-deps - path: ${{ env.BUILD_DIR }}/deps/ - if-no-files-found: warn - - # Validation job to check boot entries and configurations - validate: - name: Validate Configuration + # Summary job to report build status + summary: + name: Build Summary + needs: + - build-ucore + - build-flatcar + - build-alpine-zfs-installer + - build-alpine-netboot + - build-nixos-kexec + - build-vyos + - build-bitboot + - build-chainload + - assemble-usb + if: always() runs-on: ubuntu-latest permissions: contents: read - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Validate loader configuration - run: | - echo "=== Checking loader.conf ===" - cat config/loader/loader.conf - echo "" - - echo "=== Checking boot entries ===" - for entry in config/loader/entries/*.conf; do - echo "--- ${entry} ---" - cat "${entry}" - echo "" - done - - - name: Validate kernel cmdline fragments - run: | - echo "=== Kernel command line fragments ===" - for conf in config/cmdline.d/*.conf; do - echo "--- ${conf} ---" - cat "${conf}" - echo "" - done - - name: Check for shell script issues + steps: + - name: Check build results run: | - if command -v shellcheck >/dev/null 2>&1; then - echo "Running shellcheck..." - shellcheck scripts/*.sh || true - else - echo "shellcheck not installed, skipping..." - fi + echo "## Build Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Profile | Status |" >> $GITHUB_STEP_SUMMARY + echo "|---------|--------|" >> $GITHUB_STEP_SUMMARY + echo "| uCore | ${{ needs.build-ucore.result }} |" >> $GITHUB_STEP_SUMMARY + echo "| Flatcar Linux | ${{ needs.build-flatcar.result }} |" >> $GITHUB_STEP_SUMMARY + echo "| Alpine ZFS Installer | ${{ needs.build-alpine-zfs-installer.result }} |" >> $GITHUB_STEP_SUMMARY + echo "| Alpine Netboot | ${{ needs.build-alpine-netboot.result }} |" >> $GITHUB_STEP_SUMMARY + echo "| NixOS Kexec | ${{ needs.build-nixos-kexec.result }} |" >> $GITHUB_STEP_SUMMARY + echo "| VyOS | ${{ needs.build-vyos.result }} |" >> $GITHUB_STEP_SUMMARY + echo "| BitBoot Recovery | ${{ needs.build-bitboot.result }} |" >> $GITHUB_STEP_SUMMARY + echo "| Chainload (ZBM/netboot) | ${{ needs.build-chainload.result }} |" >> $GITHUB_STEP_SUMMARY + echo "| USB Image Assembly | ${{ needs.assemble-usb.result }} |" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4201ed6..4932988 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,12 +1,15 @@ # BitBoot Release Workflow # Automated release creation for tagged versions # +# This workflow builds each distro profile as a separate job, allowing +# individual profiles to fail without blocking the release of working ones. +# # Triggers: # - Push of version tags (v*) # # Actions: -# - Builds BitBoot UKI and USB image -# - Creates GitHub release with artifacts +# - Builds each BitBoot UKI profile independently +# - Creates GitHub release with all successful artifacts # - Generates release notes name: Release BitBoot @@ -23,9 +26,102 @@ permissions: contents: write jobs: + # Build each profile as a separate job + build-ucore: + name: uCore + uses: ./.github/workflows/build-uki.yml + with: + profile: ucore + + build-flatcar: + name: Flatcar Linux + uses: ./.github/workflows/build-uki.yml + with: + profile: flatcar + + build-alpine-zfs-installer: + name: Alpine ZFS Installer + uses: ./.github/workflows/build-uki.yml + with: + profile: alpine-zfs-installer + + build-alpine-netboot: + name: Alpine Netboot + uses: ./.github/workflows/build-uki.yml + with: + profile: alpine-netboot + + build-nixos-kexec: + name: NixOS Kexec + uses: ./.github/workflows/build-uki.yml + with: + profile: nixos-kexec + + build-vyos: + name: VyOS + uses: ./.github/workflows/build-uki.yml + with: + profile: vyos + + build-bitboot: + name: BitBoot Recovery + uses: ./.github/workflows/build-uki.yml + with: + profile: bitboot + + # Build chainload profiles (ZFSBootMenu, netboot.xyz) + build-chainload: + name: Build Chainload Profiles + runs-on: ubuntu-latest + permissions: + contents: read + container: + image: registry.fedoraproject.org/fedora:43 + options: --privileged + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + dnf install -y curl wget xz file + + - name: Make scripts executable + run: chmod +x scripts/*.sh + + - name: Fetch ZFSBootMenu and netboot.xyz + run: | + mkdir -p ${BUILD_DIR}/efi + ./scripts/fetch-deps.sh --skip-alpine --output ${BUILD_DIR}/deps + cp ${BUILD_DIR}/deps/zfsbootmenu/*.efi ${BUILD_DIR}/efi/ 2>/dev/null || true + cp ${BUILD_DIR}/deps/netboot/*.efi ${BUILD_DIR}/efi/ 2>/dev/null || true + env: + BUILD_DIR: ${{ env.BUILD_DIR }} + + - name: Upload chainload artifacts + uses: actions/upload-artifact@v4 + with: + name: uki-chainload + path: ${{ env.BUILD_DIR }}/efi/ + if-no-files-found: warn + + # Assemble and release release: name: Create Release + needs: + - build-ucore + - build-flatcar + - build-alpine-zfs-installer + - build-alpine-netboot + - build-nixos-kexec + - build-vyos + - build-bitboot + - build-chainload + if: always() runs-on: ubuntu-latest + permissions: + contents: write container: image: registry.fedoraproject.org/fedora:43 options: --privileged @@ -36,32 +132,21 @@ jobs: with: fetch-depth: 0 - - name: Install build dependencies + - name: Install dependencies run: | dnf install -y \ make \ - gcc \ - binutils \ - systemd-boot-unsigned \ - systemd-ukify \ - dracut \ parted \ gdisk \ dosfstools \ e2fsprogs \ - xz \ - zstd \ - gzip \ - bzip2 \ - curl \ - wget \ - kernel \ - kernel-devel \ - linux-firmware \ util-linux \ coreutils \ findutils \ file \ + curl \ + wget \ + xz \ git - name: Get version info @@ -74,8 +159,29 @@ jobs: - name: Make scripts executable run: chmod +x scripts/*.sh - - name: Build all artifacts - run: make all + - name: Download all UKI artifacts + uses: actions/download-artifact@v4 + with: + pattern: uki-* + path: ${{ env.BUILD_DIR }}/efi-artifacts/ + merge-multiple: false + + - name: Organize UKI files + run: | + mkdir -p ${BUILD_DIR}/efi + find ${BUILD_DIR}/efi-artifacts -name "*.efi" -exec cp {} ${BUILD_DIR}/efi/ \; + echo "=== Collected UKI files ===" + ls -la ${BUILD_DIR}/efi/ + + - name: Fetch Alpine dependencies for USB + run: | + ./scripts/fetch-deps.sh --skip-zbm --skip-netboot --output ${BUILD_DIR}/deps + env: + BUILD_DIR: ${{ env.BUILD_DIR }} + + - name: Create USB image + run: | + ./scripts/create-usb.sh --output ${BUILD_DIR}/bitboot-x86_64.img env: BUILD_DIR: ${{ env.BUILD_DIR }} @@ -83,7 +189,6 @@ jobs: run: | cd ${BUILD_DIR} xz -9 -k bitboot-x86_64.img - # Create checksums for all artifacts sha256sum efi/*.efi bitboot-x86_64.img bitboot-x86_64.img.xz > SHA256SUMS - name: Generate release notes @@ -91,6 +196,20 @@ jobs: run: | VERSION="${{ steps.version.outputs.version }}" REPO="${{ github.repository }}" + + # Determine which profiles were built successfully + echo "## Build Results" > build_status.md + echo "" >> build_status.md + echo "| Profile | Status |" >> build_status.md + echo "|---------|--------|" >> build_status.md + + for uki in ${BUILD_DIR}/efi/*.efi; do + if [[ -f "$uki" ]]; then + name=$(basename "$uki" .efi) + echo "| ${name} | ✅ Built |" >> build_status.md + fi + done + cat > release_notes.md << EOF # BitBoot ${VERSION} @@ -105,15 +224,7 @@ jobs: ## Included Boot Profiles - - Fedora CoreOS (Network Pull) - - Flatcar Container Linux (Network Pull) - - Alpine Linux ZFS Installer - - Alpine Linux Netboot - - NixOS Kexec Installer - - VyOS 1.5 Rolling - - ZFSBootMenu Recovery - - netboot.xyz - - BitBoot Recovery Shell + $(cat build_status.md) ## Installation diff --git a/config/cmdline.d/profiles/fedora-coreos.conf b/config/cmdline.d/profiles/ucore.conf similarity index 53% rename from config/cmdline.d/profiles/fedora-coreos.conf rename to config/cmdline.d/profiles/ucore.conf index 8df7c68..ca3d931 100644 --- a/config/cmdline.d/profiles/fedora-coreos.conf +++ b/config/cmdline.d/profiles/ucore.conf @@ -1,9 +1,17 @@ -# Fedora CoreOS network pull cmdline -# Embedded in fedora-coreos.efi UKI +# uCore (ublue-os) network pull cmdline +# Embedded in ucore.efi UKI +# +# uCore is an OCI image of Fedora CoreOS with "batteries included". +# This profile boots a base Fedora CoreOS image that can be rebased to uCore. +# +# After booting, rebase to uCore with: +# sudo rpm-ostree rebase ostree-unverified-registry:ghcr.io/ublue-os/ucore:stable # # NOTE: The Fedora CoreOS URL contains a specific build version. # Update this URL periodically from: # https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/ +# +# For more info: https://github.com/ublue-os/ucore rd.systemd.pull=raw,machine,verify=no,blockdev:rootdisk:https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/43.20250521.3.0/x86_64/fedora-coreos-43.20250521.3.0-metal.x86_64.raw.xz root=gpt-auto diff --git a/config/loader/entries/fedora-coreos.conf b/config/loader/entries/fedora-coreos.conf deleted file mode 100644 index 612f5e8..0000000 --- a/config/loader/entries/fedora-coreos.conf +++ /dev/null @@ -1,11 +0,0 @@ -# Fedora CoreOS - Network Pull Boot Profile -# Uses rd.systemd.pull to download and boot raw disk image -# -# Fedora CoreOS provides DDI-compliant raw disk images that support -# GPT auto-discovery for automatic root partition detection. -# -# This profile uses a dedicated UKI with embedded kernel cmdline for -# Fedora CoreOS network pull booting. - -title Fedora CoreOS (Network Pull) -efi /EFI/bitboot/fedora-coreos.efi diff --git a/config/loader/entries/ucore.conf b/config/loader/entries/ucore.conf new file mode 100644 index 0000000..c891ce1 --- /dev/null +++ b/config/loader/entries/ucore.conf @@ -0,0 +1,16 @@ +# uCore (ublue-os) - Network Pull Boot Profile +# Uses rd.systemd.pull to download and boot Fedora CoreOS raw disk image +# +# uCore is an OCI-based OS built on Fedora CoreOS with added features: +# - ZFS support pre-installed +# - Cockpit for web-based management +# - Docker/Podman container runtimes +# - Tailscale and WireGuard tools +# +# This profile boots Fedora CoreOS which can then be rebased to uCore. +# After boot, run: sudo rpm-ostree rebase ostree-unverified-registry:ghcr.io/ublue-os/ucore:stable +# +# For more info: https://github.com/ublue-os/ucore + +title uCore / Fedora CoreOS (Network Pull) +efi /EFI/bitboot/ucore.efi