forked from tfgrid/zosbuilder
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:
@@ -422,4 +422,4 @@ function main() {
|
||||
}
|
||||
|
||||
# Run main function with all arguments
|
||||
main "$@"
|
||||
main "$@"
|
||||
|
||||
219
scripts/dev-container.sh
Executable file
219
scripts/dev-container.sh
Executable file
@@ -0,0 +1,219 @@
|
||||
#!/bin/bash
|
||||
# Efficient development container workflow - persistent container for debugging
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# Container configuration
|
||||
CONTAINER_NAME="zero-os-dev"
|
||||
BUILDER_IMAGE="zero-os-builder:latest"
|
||||
|
||||
# Source common functions
|
||||
source "${SCRIPT_DIR}/lib/common.sh"
|
||||
|
||||
function show_usage() {
|
||||
cat << EOF
|
||||
Zero OS Development Container Manager
|
||||
|
||||
Usage: $0 [COMMAND]
|
||||
|
||||
Commands:
|
||||
start Start persistent development container
|
||||
stop Stop development container
|
||||
shell Enter development container shell
|
||||
build Run build in persistent container
|
||||
clean Clean and restart container
|
||||
status Show container status
|
||||
logs Show container logs
|
||||
|
||||
Environment Variables:
|
||||
DEBUG Enable debug output (default: 1 for dev)
|
||||
|
||||
Examples:
|
||||
$0 start # Start persistent container
|
||||
$0 shell # Enter container for debugging
|
||||
$0 build # Run build in persistent container
|
||||
EOF
|
||||
}
|
||||
|
||||
function dev_container_start() {
|
||||
section_header "Starting Development Container"
|
||||
|
||||
# Check if container already exists
|
||||
if podman container exists "$CONTAINER_NAME" 2>/dev/null; then
|
||||
if podman container inspect "$CONTAINER_NAME" --format '{{.State.Status}}' | grep -q "running"; then
|
||||
log_info "Development container already running"
|
||||
return 0
|
||||
else
|
||||
log_info "Starting existing development container"
|
||||
safe_execute podman start "$CONTAINER_NAME"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "Creating new development container: ${CONTAINER_NAME}"
|
||||
|
||||
# Create persistent container with all necessary mounts and environment
|
||||
safe_execute podman run -d \
|
||||
--name "$CONTAINER_NAME" \
|
||||
--privileged \
|
||||
-v "${PROJECT_ROOT}:/workspace" \
|
||||
-w /workspace \
|
||||
-e DEBUG=1 \
|
||||
-e ALPINE_VERSION=3.22 \
|
||||
-e KERNEL_VERSION=6.12.44 \
|
||||
-e RUST_TARGET=x86_64-unknown-linux-musl \
|
||||
-e OPTIMIZATION_LEVEL=max \
|
||||
"$BUILDER_IMAGE" \
|
||||
sleep infinity
|
||||
|
||||
log_info "Development container started successfully"
|
||||
log_info "Container name: ${CONTAINER_NAME}"
|
||||
log_info "Access with: $0 shell"
|
||||
}
|
||||
|
||||
function dev_container_stop() {
|
||||
section_header "Stopping Development Container"
|
||||
|
||||
if podman container exists "$CONTAINER_NAME" 2>/dev/null; then
|
||||
log_info "Stopping development container: ${CONTAINER_NAME}"
|
||||
safe_execute podman stop "$CONTAINER_NAME"
|
||||
log_info "Development container stopped"
|
||||
else
|
||||
log_info "Development container not found"
|
||||
fi
|
||||
}
|
||||
|
||||
function dev_container_shell() {
|
||||
section_header "Entering Development Container Shell"
|
||||
|
||||
if ! podman container exists "$CONTAINER_NAME" 2>/dev/null; then
|
||||
log_info "Development container not found, starting..."
|
||||
dev_container_start
|
||||
fi
|
||||
|
||||
if ! podman container inspect "$CONTAINER_NAME" --format '{{.State.Status}}' | grep -q "running"; then
|
||||
log_info "Starting stopped development container"
|
||||
safe_execute podman start "$CONTAINER_NAME"
|
||||
fi
|
||||
|
||||
log_info "Entering container shell (exit with 'exit' or Ctrl+D)"
|
||||
# Use direct execution for interactive shell (don't use safe_execute)
|
||||
exec podman exec -it "$CONTAINER_NAME" /bin/bash
|
||||
}
|
||||
|
||||
function dev_container_build() {
|
||||
section_header "Running Build in Development Container"
|
||||
|
||||
if ! podman container exists "$CONTAINER_NAME" 2>/dev/null; then
|
||||
log_info "Development container not found, starting..."
|
||||
dev_container_start
|
||||
fi
|
||||
|
||||
if ! podman container inspect "$CONTAINER_NAME" --format '{{.State.Status}}' | grep -q "running"; then
|
||||
log_info "Starting stopped development container"
|
||||
safe_execute podman start "$CONTAINER_NAME"
|
||||
fi
|
||||
|
||||
log_info "Running build in persistent container (real-time output)"
|
||||
log_info "Command: podman exec $CONTAINER_NAME ./scripts/build.sh $*"
|
||||
|
||||
# Use direct execution to show real-time output (bypass safe_execute)
|
||||
podman exec "$CONTAINER_NAME" ./scripts/build.sh "$@"
|
||||
local exit_code=$?
|
||||
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
log_info "Build completed successfully in container"
|
||||
else
|
||||
log_error "Build failed in container with exit code: $exit_code"
|
||||
fi
|
||||
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
function dev_container_clean() {
|
||||
section_header "Cleaning Development Container"
|
||||
|
||||
if podman container exists "$CONTAINER_NAME" 2>/dev/null; then
|
||||
log_info "Removing existing development container"
|
||||
safe_execute podman rm -f "$CONTAINER_NAME"
|
||||
fi
|
||||
|
||||
log_info "Starting fresh development container"
|
||||
dev_container_start
|
||||
}
|
||||
|
||||
function dev_container_status() {
|
||||
section_header "Development Container Status"
|
||||
|
||||
if podman container exists "$CONTAINER_NAME" 2>/dev/null; then
|
||||
local status=$(podman container inspect "$CONTAINER_NAME" --format '{{.State.Status}}')
|
||||
local created=$(podman container inspect "$CONTAINER_NAME" --format '{{.Created}}')
|
||||
|
||||
log_info "Container: ${CONTAINER_NAME}"
|
||||
log_info "Status: ${status}"
|
||||
log_info "Created: ${created}"
|
||||
|
||||
if [[ "$status" == "running" ]]; then
|
||||
log_info "✓ Ready for development"
|
||||
else
|
||||
log_info "⚠ Container stopped - use '$0 start' to start"
|
||||
fi
|
||||
else
|
||||
log_info "Development container not found"
|
||||
log_info "Use '$0 start' to create"
|
||||
fi
|
||||
}
|
||||
|
||||
function dev_container_logs() {
|
||||
section_header "Development Container Logs"
|
||||
|
||||
if podman container exists "$CONTAINER_NAME" 2>/dev/null; then
|
||||
safe_execute podman logs "$CONTAINER_NAME"
|
||||
else
|
||||
log_error "Development container not found"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main function
|
||||
function main() {
|
||||
local command="${1:-help}"
|
||||
|
||||
case "$command" in
|
||||
start)
|
||||
dev_container_start
|
||||
;;
|
||||
stop)
|
||||
dev_container_stop
|
||||
;;
|
||||
shell)
|
||||
dev_container_shell
|
||||
;;
|
||||
build)
|
||||
shift
|
||||
dev_container_build "$@"
|
||||
;;
|
||||
clean)
|
||||
dev_container_clean
|
||||
;;
|
||||
status)
|
||||
dev_container_status
|
||||
;;
|
||||
logs)
|
||||
dev_container_logs
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_usage
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown command: $command"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
171
scripts/lib/stages.sh
Normal 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
|
||||
Reference in New Issue
Block a user