ix init script duplication and CPIO creation issues

- Remove duplicate /sbin/init copying from initramfs_setup_zinit()
- Only /init should be config/init (initramfs setup script)
- No /sbin/init needed - config/init calls 'switch_root /mnt/root /sbin/zinit init'
- Remove unsupported cpio --owner option that broke CPIO creation
- Fix validation to not expect /sbin/init file
- Correct boot flow: /init → switch_root → /sbin/zinit init
- Remove strip and UPX compression from zinit binary copying
- UPX compression was corrupting the zinit binary causing segfaults after switch_root
- Keep zinit unmodified as it's
This commit is contained in:
2025-09-05 11:43:25 +02:00
parent 38dee2de74
commit 8c3868b242
102 changed files with 589 additions and 375 deletions

View File

@@ -374,17 +374,18 @@ function alpine_install_firmware() {
local initramfs_dir="$1"
local firmware_conf="$2"
section_header "Installing Required Firmware Packages"
section_header "Installing Required Firmware Packages (Selective)"
# Use smart firmware selection from module analysis if available
local firmware_packages=()
if [[ -n "${REQUIRED_FIRMWARE_PACKAGES:-}" ]]; then
log_info "Using intelligent firmware selection based on required modules"
log_info "Using intelligent firmware selection based on COPIED modules only"
read -ra firmware_packages <<< "$REQUIRED_FIRMWARE_PACKAGES"
log_info "Required firmware packages (${#firmware_packages[@]}):"
for package in "${firmware_packages[@]}"; do
log_info " Required by modules: ${package}"
log_info " ${package}"
done
else
log_info "Falling back to firmware configuration file"

View File

@@ -205,20 +205,41 @@ function docker_commit_builder() {
log_info "Container committed successfully: ${new_tag}"
}
# Clean up container images
# Clean up container images and running containers
function docker_cleanup() {
local keep_builder="${1:-false}"
section_header "Cleaning Up Container Images"
section_header "Cleaning Up Containers and Images"
if [[ "$keep_builder" != "true" ]]; then
log_info "Cleaning up builder containers and images"
# Stop and remove any containers using the builder image
local containers_using_image=$(${CONTAINER_RUNTIME} ps -a --filter "ancestor=${BUILDER_IMAGE}" --format "{{.ID}}" 2>/dev/null || true)
if [[ -n "$containers_using_image" ]]; then
log_info "Stopping containers using builder image"
for container_id in $containers_using_image; do
log_info "Stopping container: $container_id"
${CONTAINER_RUNTIME} stop "$container_id" 2>/dev/null || true
${CONTAINER_RUNTIME} rm "$container_id" 2>/dev/null || true
done
fi
# Stop and remove development container if it exists
local dev_container="zero-os-dev"
if ${CONTAINER_RUNTIME} container exists "$dev_container" 2>/dev/null; then
log_info "Removing development container: $dev_container"
${CONTAINER_RUNTIME} rm -f "$dev_container" 2>/dev/null || true
fi
# Now remove the images
log_info "Removing builder images"
safe_execute ${CONTAINER_RUNTIME} rmi "${BUILDER_IMAGE}" || true
safe_execute ${CONTAINER_RUNTIME} rmi "${BUILDER_IMAGE}-cached" || true
${CONTAINER_RUNTIME} rmi "${BUILDER_IMAGE}" 2>/dev/null || log_warn "Could not remove ${BUILDER_IMAGE} (may not exist)"
${CONTAINER_RUNTIME} rmi "${BUILDER_IMAGE}-cached" 2>/dev/null || log_warn "Could not remove ${BUILDER_IMAGE}-cached (may not exist)"
fi
log_info "Pruning unused containers and images"
safe_execute ${CONTAINER_RUNTIME} system prune -f
${CONTAINER_RUNTIME} system prune -f 2>/dev/null || log_warn "Container prune failed"
log_info "Container cleanup complete"
}

View File

@@ -27,18 +27,9 @@ function initramfs_setup_zinit() {
return 1
fi
# Copy config/init as /sbin/init (not a symlink to zinit)
log_info "Installing config/init as /sbin/init"
safe_execute rm -f "${initramfs_dir}/sbin/init"
local config_init="${PROJECT_ROOT}/config/init"
if [[ -f "$config_init" ]]; then
safe_execute cp "$config_init" "${initramfs_dir}/sbin/init"
safe_execute chmod 755 "${initramfs_dir}/sbin/init"
log_info "✓ Installed config/init as /sbin/init"
else
log_error "config/init not found: $config_init"
return 1
fi
# Note: /sbin/init is not needed - config/init calls "switch_root /mnt/root /sbin/zinit init"
# So after switch_root, /sbin/zinit is the init system (not /sbin/init)
log_info "zinit will be called directly via '/sbin/zinit init' after switch_root"
# Copy zinit configuration (all YAML and scripts)
log_info "Installing zinit configuration"
@@ -117,29 +108,16 @@ function initramfs_copy_components() {
local copied_count=0
local missing_count=0
# Copy zinit to /sbin
# Copy zinit to /sbin (NO stripping/UPX - critical init system)
local zinit_binary="${components_dir}/zinit/target/x86_64-unknown-linux-musl/release/zinit"
if [[ -f "$zinit_binary" ]]; then
safe_mkdir "${initramfs_dir}/sbin"
safe_execute cp "$zinit_binary" "${initramfs_dir}/sbin/zinit"
safe_execute chmod +x "${initramfs_dir}/sbin/zinit"
# Strip and UPX compress zinit
local original_size=$(get_file_size "${initramfs_dir}/sbin/zinit")
if strip "${initramfs_dir}/sbin/zinit" 2>/dev/null || true; then
log_debug "Stripped zinit"
else
log_debug "zinit already stripped or strip failed"
fi
if command_exists "upx" && upx --best --force "${initramfs_dir}/sbin/zinit" >/dev/null 2>&1 || true; then
log_debug "UPX compressed zinit"
else
log_debug "UPX failed or already compressed"
fi
local final_size=$(get_file_size "${initramfs_dir}/sbin/zinit")
log_info "✓ Copied zinit ${original_size}${final_size} to /sbin/zinit"
# Keep zinit unmodified to prevent segfaults after switch_root
local size=$(get_file_size "${initramfs_dir}/sbin/zinit")
log_info "✓ Copied zinit (${size}) to /sbin/zinit (no optimization - critical binary)"
((copied_count++))
else
log_error "✗ zinit binary not found: ${zinit_binary}"
@@ -669,7 +647,6 @@ function initramfs_validate() {
# Check essential files and directories
local essential_items=(
"init"
"sbin/init"
"sbin/zinit"
"bin/busybox"
"etc/zinit"
@@ -699,12 +676,11 @@ function initramfs_validate() {
((errors++))
fi
# Check that /sbin/init is a script
if [[ -f "${initramfs_dir}/sbin/init" && -x "${initramfs_dir}/sbin/init" ]]; then
log_info " /sbin/init script found"
# Check that /sbin/init does NOT exist (zinit called directly)
if [[ -e "${initramfs_dir}/sbin/init" ]]; then
log_warn " /sbin/init exists but should not (zinit called directly)"
else
log_error " /sbin/init is missing or not executable"
((errors++))
log_info " /sbin/init correctly absent (zinit called directly)"
fi
# Check zinit configuration

View File

@@ -10,6 +10,20 @@ KERNEL_VERSION="${KERNEL_VERSION:-6.12.44}"
KERNEL_SOURCE_URL="${KERNEL_SOURCE_URL:-https://cdn.kernel.org/pub/linux/kernel}"
KERNEL_CONFIG_SOURCE="${KERNEL_CONFIG_SOURCE:-${PROJECT_ROOT}/configs/kernel-config-generic}"
# Get actual kernel version including LOCALVERSION from kernel config
function kernel_get_full_version() {
local base_version="${1:-$KERNEL_VERSION}"
local config_file="${2:-${PROJECT_ROOT}/config/kernel.config}"
# Extract LOCALVERSION from kernel config
local localversion=""
if [[ -f "$config_file" ]] && grep -q "^CONFIG_LOCALVERSION=" "$config_file"; then
localversion=$(grep "^CONFIG_LOCALVERSION=" "$config_file" | cut -d'"' -f2)
fi
echo "${base_version}${localversion}"
}
# Download kernel source
function kernel_download_source() {
local kernel_dir="$1"
@@ -176,10 +190,6 @@ function kernel_build_with_initramfs() {
local source_dir="${kernel_dir}/current"
safe_execute cd "$source_dir"
# Clean previous build
log_info "Cleaning previous kernel build"
safe_execute make clean
# Determine number of cores for parallel build
local cores=$(nproc)
local jobs=$((cores > 1 ? cores - 1 : 1)) # Leave one core free
@@ -215,13 +225,13 @@ function kernel_build_with_initramfs() {
fi
}
# Build modules for initramfs
# Build and install modules in container for proper dependency resolution
function kernel_build_modules() {
local kernel_dir="$1"
local modules_install_dir="$2"
local version="${3:-$KERNEL_VERSION}"
local initramfs_dir="$2"
local base_version="${3:-$KERNEL_VERSION}"
section_header "Building Kernel Modules"
section_header "Building Kernel Modules in Container"
local source_dir="${kernel_dir}/current"
@@ -232,6 +242,11 @@ function kernel_build_modules() {
safe_execute cd "$source_dir"
# Get the full kernel version including LOCALVERSION
local full_version=$(kernel_get_full_version "$base_version" "${PROJECT_ROOT}/config/kernel.config")
log_info "Base kernel version: ${base_version}"
log_info "Full kernel version: ${full_version}"
# Build modules
local cores=$(nproc)
local jobs=$((cores > 1 ? cores - 1 : 1))
@@ -239,19 +254,30 @@ function kernel_build_modules() {
log_info "Building kernel modules with ${jobs} parallel jobs"
safe_execute make -j${jobs} modules
# Install modules to staging area
log_info "Installing modules to: ${modules_install_dir}"
safe_mkdir "$modules_install_dir"
safe_execute make modules_install INSTALL_MOD_PATH="$modules_install_dir"
# Install modules in container for proper modinfo/depmod access
local container_modules_dir="/lib/modules"
log_info "Installing modules in container: ${container_modules_dir}"
safe_execute make modules_install INSTALL_MOD_PATH=/
# Run depmod to create module dependencies
local modules_dir="${modules_install_dir}/lib/modules/${version}"
if [[ -d "$modules_dir" ]]; then
log_info "Running depmod for module dependencies"
safe_execute depmod -a -b "$modules_install_dir" "$version"
# Run depmod in container context for proper dependency resolution
log_info "Running depmod in container for ${full_version}"
safe_execute depmod -a "$full_version"
# Verify module installation in container
if [[ -d "/lib/modules/${full_version}" ]]; then
local module_count=$(find "/lib/modules/${full_version}" -name "*.ko*" | wc -l)
log_info "Container modules installed: ${module_count} modules in /lib/modules/${full_version}"
# Export the container modules path for dependency resolution
export CONTAINER_MODULES_PATH="/lib/modules/${full_version}"
export KERNEL_FULL_VERSION="$full_version"
log_info "Module dependency resolution will use: ${CONTAINER_MODULES_PATH}"
else
log_error "Module installation in container failed"
return 1
fi
log_info "Kernel modules build complete"
log_info "Kernel modules build and container installation complete"
}
# Clean kernel build artifacts
@@ -278,4 +304,4 @@ function kernel_cleanup() {
# Export functions
export -f kernel_download_source kernel_apply_config kernel_modify_config_for_initramfs
export -f kernel_build_with_initramfs kernel_build_modules kernel_cleanup
export -f kernel_build_with_initramfs kernel_build_modules kernel_cleanup kernel_get_full_version

171
scripts/lib/stages.sh Normal file
View File

@@ -0,0 +1,171 @@
#!/bin/bash
# Build stage tracking and incremental build support
# Source common functions
LIB_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${LIB_SCRIPT_DIR}/common.sh"
# Stage tracking configuration
STAGES_DIR="${PROJECT_ROOT:-/workspace}/.build-stages"
# Initialize stage tracking
function stages_init() {
section_header "Initializing Stage Tracking"
safe_mkdir "$STAGES_DIR"
log_info "Stage tracking directory: ${STAGES_DIR}"
# Show existing completed stages
local completed_stages=()
if ls "$STAGES_DIR"/*.done >/dev/null 2>&1; then
while IFS= read -r stage_file; do
local stage_name=$(basename "$stage_file" .done)
completed_stages+=("$stage_name")
done < <(ls "$STAGES_DIR"/*.done 2>/dev/null)
log_info "Previously completed stages: ${completed_stages[*]}"
else
log_info "No previously completed stages found"
fi
}
# Check if stage is already completed
function stage_is_completed() {
local stage_name="$1"
local stage_file="${STAGES_DIR}/${stage_name}.done"
if [[ -f "$stage_file" ]]; then
local completion_time=$(stat -c %Y "$stage_file" 2>/dev/null || echo "unknown")
log_debug "Stage '$stage_name' already completed at $(date -d @$completion_time 2>/dev/null || echo "unknown time")"
return 0
else
log_debug "Stage '$stage_name' not completed yet"
return 1
fi
}
# Mark stage as completed
function stage_mark_completed() {
local stage_name="$1"
local stage_details="${2:-}"
local stage_file="${STAGES_DIR}/${stage_name}.done"
# Create completion marker with metadata
cat > "$stage_file" << EOF
# Stage completion marker
STAGE_NAME="$stage_name"
COMPLETED_AT="$(date -Iseconds)"
COMPLETED_BY="$(whoami)"
DETAILS="$stage_details"
EOF
log_info "Stage completed: $stage_name"
if [[ -n "$stage_details" ]]; then
log_debug " Details: $stage_details"
fi
}
# Remove stage completion marker (force rebuild)
function stage_force_rebuild() {
local stage_name="$1"
local stage_file="${STAGES_DIR}/${stage_name}.done"
if [[ -f "$stage_file" ]]; then
safe_execute rm "$stage_file"
log_info "Stage marked for rebuild: $stage_name"
else
log_debug "Stage not completed, no need to force rebuild: $stage_name"
fi
}
# Clear all stage markers (full rebuild)
function stages_clear_all() {
section_header "Clearing All Stage Markers"
if [[ -d "$STAGES_DIR" ]]; then
local marker_count=$(ls "$STAGES_DIR"/*.done 2>/dev/null | wc -l || echo "0")
if [[ $marker_count -gt 0 ]]; then
safe_execute rm -f "$STAGES_DIR"/*.done
log_info "Cleared ${marker_count} stage completion markers"
else
log_info "No stage markers to clear"
fi
fi
log_info "Next build will rebuild all stages"
}
# Run stage with completion tracking
function stage_run() {
local stage_name="$1"
local stage_function="$2"
shift 2
local stage_args=("$@")
log_info "=== STAGE: $stage_name ==="
# Check if stage is already completed (unless forced)
if [[ "${FORCE_REBUILD:-false}" != "true" ]] && stage_is_completed "$stage_name"; then
log_info "Skipping completed stage: $stage_name"
log_info " (Use FORCE_REBUILD=true or remove ${STAGES_DIR}/${stage_name}.done to rebuild)"
return 0
fi
log_info "Running stage: $stage_name"
local start_time=$(date +%s)
# Run the stage function with its arguments
if "$stage_function" "${stage_args[@]}"; then
local end_time=$(date +%s)
local duration=$((end_time - start_time))
# Mark as completed with timing info
stage_mark_completed "$stage_name" "Duration: ${duration}s, Function: $stage_function"
log_info "Stage '$stage_name' completed successfully (${duration}s)"
return 0
else
local exit_code=$?
log_error "Stage '$stage_name' failed with exit code: $exit_code"
return $exit_code
fi
}
# Show stage status
function stages_status() {
section_header "Build Stages Status"
local all_stages=(
"alpine_extract"
"alpine_configure"
"alpine_packages"
"alpine_firmware"
"components_build"
"components_verify"
"kernel_modules"
"zinit_setup"
"init_script"
"modules_setup"
"modules_copy"
"cleanup"
"validation"
"initramfs_create"
"initramfs_test"
"kernel_build"
)
log_info "Stage completion status:"
for stage in "${all_stages[@]}"; do
if stage_is_completed "$stage"; then
local completion_time=$(stat -c %Y "${STAGES_DIR}/${stage}.done" 2>/dev/null || echo "0")
local time_str=$(date -d @$completion_time '+%H:%M:%S' 2>/dev/null || echo "unknown")
log_info " [DONE] $stage (completed at $time_str)"
else
log_info " [TODO] $stage (pending)"
fi
done
}
# Export functions
export -f stages_init stage_is_completed stage_mark_completed stage_force_rebuild
export -f stages_clear_all stage_run stages_status