- Complete bash framework with strict error handling - Modular library system (docker, alpine, components, initramfs, kernel, testing) - Rust component integration (zinit, rfs, mycelium) with musl targeting - Rootless Docker/Podman support for GitHub Actions - Centralized configuration in config/build.conf - 2-stage module loading system - Strip + UPX optimization for minimal size - Complete zinit integration replacing OpenRC - GitHub Actions CI/CD pipeline - Comprehensive documentation and usage guides Components: - Latest stable kernel 6.12.44 - Alpine Linux 3.22 base - ThreeFold components: zinit, mycelium, rfs, corex - Target: ~8-12MB final initramfs.cpio.xz
578 lines
17 KiB
Markdown
578 lines
17 KiB
Markdown
# Zero OS Alpine Initramfs Builder - Complete Implementation Plan
|
|
|
|
## Current Analysis
|
|
|
|
Based on the existing project structure and your requirements, I've analyzed:
|
|
|
|
- **Existing configs**: Excellent foundation with Alpine init, kernel config, minimal packages, and zinit services
|
|
- **New requirements**: Rust builds (zinit, rfs, mycelium) with musl, strip+UPX optimization, rootless containers
|
|
- **sources.conf**: Already defines ThreeFold components with proper build functions
|
|
|
|
## Directory Structure to Create
|
|
|
|
```
|
|
project-root/
|
|
├── config/
|
|
│ ├── zinit/
|
|
│ │ ├── services/ # zinit service definitions
|
|
│ │ └── zinit.conf # main zinit configuration
|
|
│ ├── packages.list # apk packages to install in initramfs
|
|
│ ├── sources.conf # components to download/build (EXISTING)
|
|
│ ├── kernel.config # kernel config with initramfs path
|
|
│ └── modules.conf # 2-stage module loading specification
|
|
├── scripts/
|
|
│ ├── lib/
|
|
│ │ ├── docker.sh # container lifecycle, rootless setup
|
|
│ │ ├── alpine.sh # miniroot extraction, apk operations
|
|
│ │ ├── components.sh # download/build from sources.conf
|
|
│ │ ├── initramfs.sh # assembly, aggressive cleanup, compression
|
|
│ │ ├── kernel.sh # kernel build with embedded initramfs
|
|
│ │ └── testing.sh # qemu/cloud-hypervisor test commands
|
|
│ ├── build.sh # main orchestrator script
|
|
│ └── clean.sh # cleanup all generated artifacts
|
|
├── initramfs/ # final initramfs tree (generated)
|
|
├── components/ # component build staging (generated)
|
|
├── kernel/ # kernel source tree (generated)
|
|
└── dist/ # final build artifacts (generated)
|
|
```
|
|
|
|
## Implementation Framework
|
|
|
|
### 1. Bash Scripting Standards
|
|
|
|
All scripts must follow these patterns:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# Source common functions
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "${SCRIPT_DIR}/lib/common.sh"
|
|
|
|
# Command execution pattern
|
|
function safe_execute() {
|
|
local cmd="$*"
|
|
log_info "Executing: ${cmd}"
|
|
if ! ${cmd}; then
|
|
log_error "Command failed: ${cmd}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Section separation
|
|
function section_header() {
|
|
echo "=================================================="
|
|
echo "SECTION: $1"
|
|
echo "=================================================="
|
|
}
|
|
```
|
|
|
|
### 2. Container Support (scripts/lib/docker.sh)
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Container management for rootless builds
|
|
|
|
function docker_build_container() {
|
|
local dockerfile_path="$1"
|
|
local tag="$2"
|
|
|
|
section_header "Building Container"
|
|
safe_execute docker build -t "${tag}" "${dockerfile_path}"
|
|
}
|
|
|
|
function docker_start_rootless() {
|
|
local image="$1"
|
|
local workdir="$2"
|
|
local volumes="$3"
|
|
|
|
section_header "Starting Rootless Container"
|
|
local user_args="--user $(id -u):$(id -g)"
|
|
local volume_args=""
|
|
|
|
for vol in ${volumes}; do
|
|
volume_args="${volume_args} -v ${vol}"
|
|
done
|
|
|
|
safe_execute docker run ${user_args} ${volume_args} -w "${workdir}" "${image}"
|
|
}
|
|
|
|
function docker_commit_builder() {
|
|
local container_id="$1"
|
|
local new_tag="$2"
|
|
|
|
section_header "Committing Builder Container"
|
|
safe_execute docker commit "${container_id}" "${new_tag}"
|
|
}
|
|
```
|
|
|
|
### 3. Alpine Operations (scripts/lib/alpine.sh)
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Alpine miniroot and package operations
|
|
|
|
function alpine_extract_miniroot() {
|
|
local version="3.22"
|
|
local arch="x86_64"
|
|
local target_dir="$1"
|
|
|
|
section_header "Extracting Alpine Miniroot"
|
|
local url="https://dl-cdn.alpinelinux.org/alpine/v${version}/releases/${arch}/alpine-minirootfs-${version}.0-${arch}.tar.gz"
|
|
|
|
safe_execute mkdir -p "${target_dir}"
|
|
safe_execute wget -O "/tmp/alpine-miniroot.tar.gz" "${url}"
|
|
safe_execute tar -xzf "/tmp/alpine-miniroot.tar.gz" -C "${target_dir}"
|
|
safe_execute rm "/tmp/alpine-miniroot.tar.gz"
|
|
}
|
|
|
|
function alpine_install_packages() {
|
|
local initramfs_dir="$1"
|
|
local packages_file="$2"
|
|
|
|
section_header "Installing Alpine Packages"
|
|
|
|
# Setup chroot environment
|
|
safe_execute mount --bind /proc "${initramfs_dir}/proc"
|
|
safe_execute mount --bind /sys "${initramfs_dir}/sys"
|
|
safe_execute mount --bind /dev "${initramfs_dir}/dev"
|
|
|
|
# Install packages (NO OpenRC)
|
|
local packages=$(grep -v '^#' "${packages_file}" | grep -v '^$' | tr '\n' ' ')
|
|
safe_execute chroot "${initramfs_dir}" apk add --no-cache ${packages}
|
|
|
|
# Cleanup
|
|
safe_execute umount "${initramfs_dir}/proc" || true
|
|
safe_execute umount "${initramfs_dir}/sys" || true
|
|
safe_execute umount "${initramfs_dir}/dev" || true
|
|
}
|
|
|
|
function alpine_aggressive_cleanup() {
|
|
local initramfs_dir="$1"
|
|
|
|
section_header "Aggressive Alpine Cleanup"
|
|
|
|
# Remove documentation
|
|
safe_execute rm -rf "${initramfs_dir}/usr/share/doc"
|
|
safe_execute rm -rf "${initramfs_dir}/usr/share/man"
|
|
safe_execute rm -rf "${initramfs_dir}/usr/share/info"
|
|
|
|
# Remove locales except C
|
|
safe_execute find "${initramfs_dir}/usr/share/locale" -mindepth 1 -maxdepth 1 -type d ! -name 'C' -exec rm -rf {} + 2>/dev/null || true
|
|
|
|
# Remove headers and development files
|
|
safe_execute rm -rf "${initramfs_dir}/usr/include"
|
|
safe_execute rm -rf "${initramfs_dir}/usr/lib/pkgconfig"
|
|
|
|
# Remove APK cache
|
|
safe_execute rm -rf "${initramfs_dir}/var/cache/apk"
|
|
safe_execute rm -rf "${initramfs_dir}/lib/apk"
|
|
}
|
|
```
|
|
|
|
### 4. Component Building (scripts/lib/components.sh)
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Component download and build system
|
|
|
|
function components_parse_sources_conf() {
|
|
local sources_file="$1"
|
|
local components_dir="$2"
|
|
|
|
section_header "Parsing Sources Configuration"
|
|
|
|
while IFS=: read -r type name url version build_func extra; do
|
|
[[ $type =~ ^#.*$ ]] && continue # Skip comments
|
|
[[ -z "$type" ]] && continue # Skip empty lines
|
|
|
|
log_info "Component: ${name} (${type})"
|
|
|
|
case "$type" in
|
|
"git")
|
|
components_download_git "$name" "$url" "$version" "$components_dir"
|
|
;;
|
|
"release")
|
|
components_download_release "$name" "$url" "$version" "$components_dir" "$extra"
|
|
;;
|
|
*)
|
|
log_error "Unknown component type: $type"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Build component
|
|
components_build_component "$name" "$build_func" "$components_dir"
|
|
|
|
done < <(grep -v '^#' "$sources_file" | grep -v '^$')
|
|
}
|
|
|
|
function components_download_git() {
|
|
local name="$1"
|
|
local url="$2"
|
|
local version="$3"
|
|
local components_dir="$4"
|
|
|
|
local target_dir="${components_dir}/${name}"
|
|
|
|
section_header "Downloading Git Component: ${name}"
|
|
|
|
if [[ -d "$target_dir" ]]; then
|
|
log_info "Component ${name} already exists, updating..."
|
|
safe_execute cd "$target_dir"
|
|
safe_execute git fetch
|
|
safe_execute git checkout "$version"
|
|
safe_execute git pull origin "$version" || true
|
|
else
|
|
log_info "Cloning ${name} from ${url}"
|
|
safe_execute git clone "$url" "$target_dir"
|
|
safe_execute cd "$target_dir"
|
|
safe_execute git checkout "$version"
|
|
fi
|
|
}
|
|
|
|
function components_download_release() {
|
|
local name="$1"
|
|
local url="$2"
|
|
local version="$3"
|
|
local components_dir="$4"
|
|
local extra="$5"
|
|
|
|
local target_dir="${components_dir}/${name}"
|
|
local filename=$(basename "$url")
|
|
|
|
section_header "Downloading Release Component: ${name}"
|
|
|
|
safe_execute mkdir -p "$target_dir"
|
|
safe_execute wget -O "${target_dir}/${filename}" "$url"
|
|
|
|
# Handle rename option
|
|
if [[ "$extra" =~ rename=(.+) ]]; then
|
|
local new_name="${BASH_REMATCH[1]}"
|
|
safe_execute mv "${target_dir}/${filename}" "${target_dir}/${new_name}"
|
|
fi
|
|
}
|
|
|
|
function components_build_component() {
|
|
local name="$1"
|
|
local build_func="$2"
|
|
local components_dir="$3"
|
|
|
|
section_header "Building Component: ${name}"
|
|
|
|
local component_dir="${components_dir}/${name}"
|
|
safe_execute cd "$component_dir"
|
|
|
|
# Call the specific build function
|
|
"$build_func" "$name" "$component_dir"
|
|
}
|
|
|
|
# Rust build functions for ThreeFold components
|
|
function build_zinit() {
|
|
local name="$1"
|
|
local component_dir="$2"
|
|
|
|
log_info "Building zinit with musl target"
|
|
export RUSTFLAGS="-C target-feature=+crt-static"
|
|
safe_execute cargo build --release --target x86_64-unknown-linux-musl
|
|
|
|
# Copy binary to install location
|
|
local binary_path="target/x86_64-unknown-linux-musl/release/zinit"
|
|
safe_execute cp "$binary_path" "${INSTALL_DIR}/sbin/zinit"
|
|
}
|
|
|
|
function build_rfs() {
|
|
local name="$1"
|
|
local component_dir="$2"
|
|
|
|
log_info "Building rfs with musl target"
|
|
export RUSTFLAGS="-C target-feature=+crt-static"
|
|
safe_execute cargo build --release --target x86_64-unknown-linux-musl
|
|
|
|
# Copy binary to install location
|
|
local binary_path="target/x86_64-unknown-linux-musl/release/rfs"
|
|
safe_execute cp "$binary_path" "${INSTALL_DIR}/usr/bin/rfs"
|
|
}
|
|
|
|
function build_mycelium() {
|
|
local name="$1"
|
|
local component_dir="$2"
|
|
|
|
log_info "Building mycelium with musl target (special directory)"
|
|
safe_execute cd myceliumd
|
|
export RUSTFLAGS="-C target-feature=+crt-static"
|
|
safe_execute cargo build --release --target x86_64-unknown-linux-musl
|
|
|
|
# Copy binary from special path
|
|
local binary_path="target/x86_64-unknown-linux-musl/release/mycelium"
|
|
safe_execute cp "$binary_path" "${INSTALL_DIR}/usr/bin/mycelium"
|
|
}
|
|
|
|
function install_corex() {
|
|
local name="$1"
|
|
local component_dir="$2"
|
|
|
|
log_info "Installing corex binary"
|
|
safe_execute chmod +x "${component_dir}/corex"
|
|
safe_execute cp "${component_dir}/corex" "${INSTALL_DIR}/usr/bin/corex"
|
|
}
|
|
```
|
|
|
|
### 5. Initramfs Assembly (scripts/lib/initramfs.sh)
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Initramfs assembly and optimization
|
|
|
|
function initramfs_setup_zinit() {
|
|
local initramfs_dir="$1"
|
|
local zinit_config_dir="$2"
|
|
|
|
section_header "Setting up zinit as init"
|
|
|
|
# Replace /sbin/init with zinit
|
|
safe_execute rm -f "${initramfs_dir}/sbin/init"
|
|
safe_execute ln -sf zinit "${initramfs_dir}/sbin/init"
|
|
|
|
# Copy zinit configuration
|
|
safe_execute mkdir -p "${initramfs_dir}/etc/zinit"
|
|
safe_execute cp -r "${zinit_config_dir}"/* "${initramfs_dir}/etc/zinit/"
|
|
}
|
|
|
|
function initramfs_setup_modules() {
|
|
local initramfs_dir="$1"
|
|
local modules_conf="$2"
|
|
local kernel_version="$3"
|
|
|
|
section_header "Setting up 2-stage module loading"
|
|
|
|
local modules_dir="${initramfs_dir}/lib/modules/${kernel_version}"
|
|
safe_execute mkdir -p "$modules_dir"
|
|
|
|
# Create stage1 and stage2 module lists
|
|
grep "^stage1:" "$modules_conf" | cut -d: -f2 > "${modules_dir}/stage1.list"
|
|
grep "^stage2:" "$modules_conf" | cut -d: -f2 > "${modules_dir}/stage2.list"
|
|
}
|
|
|
|
function initramfs_strip_and_upx() {
|
|
local initramfs_dir="$1"
|
|
|
|
section_header "Stripping and UPX compressing binaries"
|
|
|
|
# Find all executables and strip them
|
|
find "$initramfs_dir" -type f -executable -print0 | while IFS= read -r -d '' file; do
|
|
if file "$file" | grep -q "ELF.*executable"; then
|
|
log_info "Stripping: $file"
|
|
safe_execute strip "$file" || log_warn "Failed to strip $file"
|
|
|
|
log_info "UPX compressing: $file"
|
|
safe_execute upx --best "$file" || log_warn "Failed to UPX $file"
|
|
fi
|
|
done
|
|
|
|
# Strip libraries too
|
|
find "$initramfs_dir" -name "*.so*" -type f -print0 | while IFS= read -r -d '' file; do
|
|
if file "$file" | grep -q "ELF.*shared object"; then
|
|
log_info "Stripping library: $file"
|
|
safe_execute strip "$file" || log_warn "Failed to strip $file"
|
|
fi
|
|
done
|
|
}
|
|
|
|
function initramfs_create_cpio() {
|
|
local initramfs_dir="$1"
|
|
local output_file="$2"
|
|
|
|
section_header "Creating initramfs.cpio.xz"
|
|
|
|
safe_execute cd "$initramfs_dir"
|
|
safe_execute find . | cpio -o -H newc | xz -9 --check=crc32 > "$output_file"
|
|
|
|
local size=$(du -h "$output_file" | cut -f1)
|
|
log_info "Created initramfs: $output_file ($size)"
|
|
}
|
|
```
|
|
|
|
### 6. Configuration Files
|
|
|
|
#### config/packages.list (migrate from existing)
|
|
```
|
|
# Based on existing configs/packages-minimal.txt
|
|
# Core system (essential only)
|
|
alpine-baselayout
|
|
busybox
|
|
musl
|
|
|
|
# Module loading & hardware detection
|
|
eudev
|
|
eudev-hwids
|
|
eudev-libs
|
|
eudev-netifnames
|
|
kmod
|
|
|
|
# Console/terminal management
|
|
util-linux
|
|
|
|
# Essential networking
|
|
iproute2
|
|
ethtool
|
|
|
|
# Filesystem support
|
|
btrfs-progs
|
|
dosfstools
|
|
|
|
# Essential libraries
|
|
zlib
|
|
|
|
# Network utilities
|
|
dhcpcd
|
|
tcpdump
|
|
bmon
|
|
|
|
# Random number generation
|
|
haveged
|
|
|
|
# SSH access and terminal multiplexer
|
|
openssh-server
|
|
zellij
|
|
```
|
|
|
|
#### config/modules.conf
|
|
```bash
|
|
# 2-stage module loading based on existing configs/modules-essential.list
|
|
|
|
# Stage 1: Critical boot modules
|
|
stage1:virtio_net
|
|
stage1:virtio_scsi
|
|
stage1:virtio_blk
|
|
stage1:virtio_pci
|
|
stage1:e1000
|
|
stage1:e1000e
|
|
stage1:scsi_mod
|
|
stage1:sd_mod
|
|
|
|
# Stage 2: Extended hardware support
|
|
stage2:igb
|
|
stage2:ixgbe
|
|
stage2:i40e
|
|
stage2:ice
|
|
stage2:r8169
|
|
stage2:bnx2
|
|
stage2:bnx2x
|
|
stage2:tg3
|
|
stage2:overlay
|
|
stage2:tun
|
|
```
|
|
|
|
#### config/zinit/zinit.conf
|
|
```yaml
|
|
# Main zinit configuration
|
|
log_level: debug
|
|
init:
|
|
- stage1-modules
|
|
- stage2-modules
|
|
- networking
|
|
- services
|
|
```
|
|
|
|
### 7. Main Build Script (scripts/build.sh)
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
|
|
# Source all libraries
|
|
source "${SCRIPT_DIR}/lib/docker.sh"
|
|
source "${SCRIPT_DIR}/lib/alpine.sh"
|
|
source "${SCRIPT_DIR}/lib/components.sh"
|
|
source "${SCRIPT_DIR}/lib/initramfs.sh"
|
|
source "${SCRIPT_DIR}/lib/kernel.sh"
|
|
source "${SCRIPT_DIR}/lib/testing.sh"
|
|
|
|
# Configuration
|
|
ALPINE_VERSION="3.22"
|
|
KERNEL_VERSION="6.8.8"
|
|
export INSTALL_DIR="${PROJECT_ROOT}/initramfs"
|
|
export COMPONENTS_DIR="${PROJECT_ROOT}/components"
|
|
export KERNEL_DIR="${PROJECT_ROOT}/kernel"
|
|
export DIST_DIR="${PROJECT_ROOT}/dist"
|
|
|
|
function main() {
|
|
section_header "Zero OS Alpine Initramfs Builder"
|
|
|
|
# Setup build environment
|
|
setup_build_environment
|
|
|
|
# Extract Alpine miniroot
|
|
alpine_extract_miniroot "$INSTALL_DIR"
|
|
|
|
# Install packages
|
|
alpine_install_packages "$INSTALL_DIR" "${PROJECT_ROOT}/config/packages.list"
|
|
|
|
# Build and install components
|
|
export INSTALL_DIR # Make available to component build functions
|
|
components_parse_sources_conf "${PROJECT_ROOT}/config/sources.conf" "$COMPONENTS_DIR"
|
|
|
|
# Setup zinit
|
|
initramfs_setup_zinit "$INSTALL_DIR" "${PROJECT_ROOT}/config/zinit"
|
|
|
|
# Setup modules
|
|
initramfs_setup_modules "$INSTALL_DIR" "${PROJECT_ROOT}/config/modules.conf" "$KERNEL_VERSION"
|
|
|
|
# Aggressive cleanup
|
|
alpine_aggressive_cleanup "$INSTALL_DIR"
|
|
|
|
# Strip and UPX all binaries
|
|
initramfs_strip_and_upx "$INSTALL_DIR"
|
|
|
|
# Create initramfs
|
|
initramfs_create_cpio "$INSTALL_DIR" "${DIST_DIR}/initramfs.cpio.xz"
|
|
|
|
# Build kernel with embedded initramfs
|
|
kernel_build_with_initramfs "${PROJECT_ROOT}/config/kernel.config" "${DIST_DIR}/initramfs.cpio.xz" "${DIST_DIR}/vmlinuz.efi"
|
|
|
|
section_header "Build Complete"
|
|
log_info "Artifacts created in: $DIST_DIR"
|
|
}
|
|
|
|
function setup_build_environment() {
|
|
section_header "Setting up build environment"
|
|
|
|
safe_execute mkdir -p "$INSTALL_DIR"
|
|
safe_execute mkdir -p "$COMPONENTS_DIR"
|
|
safe_execute mkdir -p "$KERNEL_DIR"
|
|
safe_execute mkdir -p "$DIST_DIR"
|
|
|
|
# Install build dependencies
|
|
if command -v apk >/dev/null 2>&1; then
|
|
safe_execute apk add --no-cache build-base rust cargo upx strip git wget
|
|
elif command -v apt-get >/dev/null 2>&1; then
|
|
safe_execute apt-get update
|
|
safe_execute apt-get install -y build-essential rustc cargo upx-ucl binutils git wget
|
|
fi
|
|
}
|
|
|
|
main "$@"
|
|
```
|
|
|
|
## Next Steps
|
|
|
|
1. **Switch to Code Mode**: Request mode switch to implement all files
|
|
2. **Migrate Configs**: Move existing configurations to new structure
|
|
3. **Test Build**: Run complete build process
|
|
4. **Optimize**: Fine-tune strip/UPX and size optimization
|
|
5. **Documentation**: Create README and GitHub Actions integration
|
|
|
|
This plan provides a complete, production-ready build system with:
|
|
- ✅ Rootless container support
|
|
- ✅ Rust builds with musl targeting
|
|
- ✅ Strip + UPX optimization
|
|
- ✅ Strict error handling
|
|
- ✅ Modular architecture
|
|
- ✅ GitHub Actions compatibility
|
|
- ✅ 2-stage module loading
|
|
- ✅ Complete zinit integration |