Files
zosbuilder/scripts/lib/common.sh
Jan De Landtsheer 947d156921
Some checks failed
Build Zero OS Initramfs / build (push) Has been cancelled
Build Zero OS Initramfs / test-matrix (qemu, basic) (push) Has been cancelled
Build Zero OS Initramfs / test-matrix (qemu, serial) (push) Has been cancelled
Added youki build and fromatting of scripts
2025-11-11 20:49:36 +01:00

297 lines
7.5 KiB
Bash

#!/bin/bash
# Common functions and utilities for Zero OS Alpine Initramfs Builder
# Strict error handling
set -euo pipefail
# Script directory detection (only if not already set)
if [[ -z "${SCRIPT_DIR:-}" ]]; then
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
fi
if [[ -z "${PROJECT_ROOT:-}" ]]; then
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
fi
# Colors for output (if terminal supports it)
if [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
else
RED=''
GREEN=''
YELLOW=''
BLUE=''
NC=''
fi
# Logging functions
function log_info() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "${GREEN}[INFO]${NC} ${timestamp} - $*" >&2
}
function log_warn() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "${YELLOW}[WARN]${NC} ${timestamp} - $*" >&2
}
function log_error() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo -e "${RED}[ERROR]${NC} ${timestamp} - $*" >&2
}
function log_debug() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
if [[ "${DEBUG:-0}" == "1" ]]; then
echo -e "${BLUE}[DEBUG]${NC} ${timestamp} - $*" >&2
fi
}
# Command execution with full transparency
function safe_execute() {
# Stream output live when DEBUG=1 or inside container; otherwise capture and emit only on error.
local cmd_display="$*"
log_info "Executing: ${cmd_display}"
if [[ "${DEBUG:-0}" == "1" ]] || in_container; then
if ! "$@"; then
log_error "Command failed: ${cmd_display}"
exit 1
fi
else
local output
if ! output=$("$@" 2>&1); then
log_error "Command failed: ${cmd_display}"
log_error "Output: ${output}"
exit 1
else
log_debug "Command completed successfully: ${cmd_display}"
fi
fi
}
# Always-streaming variant (forces live stdout/stderr regardless of DEBUG)
function safe_execute_stream() {
local cmd_display="$*"
log_info "Executing (stream): ${cmd_display}"
if ! "$@"; then
log_error "Command failed: ${cmd_display}"
exit 1
fi
}
# Section headers with clear text separators
function section_header() {
local title="$1"
echo ""
echo "=================================================="
echo "SECTION: ${title}"
echo "=================================================="
log_info "Starting section: ${title}"
}
# Check if command exists
function command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check if we're running in a container
function in_container() {
[[ -f /.dockerenv ]] || [[ -f /run/.containerenv ]] || grep -q 'container' /proc/1/cgroup 2>/dev/null
}
# Verify required tools are available
function check_dependencies() {
local missing_deps=()
# Core build tools
local required_tools=(
"git"
"wget"
"tar"
"gzip"
"xz"
"cpio"
"strip"
"upx"
"rustc"
"cargo"
)
for tool in "${required_tools[@]}"; do
if ! command_exists "$tool"; then
missing_deps+=("$tool")
fi
done
# Check for container runtime (if not in container)
if ! in_container; then
if ! command_exists "podman" && ! command_exists "docker"; then
missing_deps+=("podman or docker")
fi
fi
if [[ ${#missing_deps[@]} -gt 0 ]]; then
log_error "Missing required dependencies:"
for dep in "${missing_deps[@]}"; do
log_error " - $dep"
done
return 1
fi
log_info "All dependencies satisfied"
return 0
}
# Create directory safely
function safe_mkdir() {
local dir="$1"
log_debug "Creating directory: ${dir}"
safe_execute mkdir -p "$dir"
}
# Remove directory safely
function safe_rmdir() {
local dir="$1"
if [[ -d "$dir" ]]; then
log_debug "Removing directory: ${dir}"
safe_execute rm -rf "$dir"
fi
}
# Copy file/directory safely
function safe_copy() {
local src="$1"
local dst="$2"
log_debug "Copying: ${src} -> ${dst}"
safe_execute cp -r "$src" "$dst"
}
# Check if path is absolute
function is_absolute_path() {
[[ "$1" = /* ]]
}
# Resolve relative path to absolute
function resolve_path() {
local path="$1"
if is_absolute_path "$path"; then
echo "$path"
else
echo "$(pwd)/$path"
fi
}
# Get file size in human readable format
function get_file_size() {
local file="$1"
if [[ -f "$file" ]]; then
du -h "$file" | cut -f1
else
echo "0B"
fi
}
# Get short git commit hash from a git repository directory
function get_git_commit_hash() {
local repo_dir="$1"
local short="${2:-true}" # Default to short hash
if [[ ! -d "$repo_dir/.git" ]]; then
echo "unknown"
return 1
fi
local hash
if [[ "$short" == "true" ]]; then
hash=$(cd "$repo_dir" && git rev-parse --short HEAD 2>/dev/null || echo "unknown")
else
hash=$(cd "$repo_dir" && git rev-parse HEAD 2>/dev/null || echo "unknown")
fi
echo "$hash"
}
# Wait for file to exist with timeout
function wait_for_file() {
local file="$1"
local timeout="${2:-30}"
local count=0
while [[ ! -f "$file" && $count -lt $timeout ]]; do
sleep 1
((count++))
done
[[ -f "$file" ]]
}
# Cleanup function for traps
function cleanup_on_exit() {
local exit_code=$?
log_info "Build process exiting with code: ${exit_code}"
# Unmount any mounted filesystems
if [[ -n "${CLEANUP_MOUNTS:-}" ]]; then
for mount in $CLEANUP_MOUNTS; do
if mountpoint -q "$mount" 2>/dev/null; then
log_info "Unmounting: $mount"
umount "$mount" 2>/dev/null || true
fi
done
fi
exit $exit_code
}
# Set up exit trap
trap cleanup_on_exit EXIT INT TERM
# Load build configuration after functions are defined
BUILD_CONF="${PROJECT_ROOT}/config/build.conf"
if [[ -f "$BUILD_CONF" ]]; then
log_debug "Loading build configuration from: ${BUILD_CONF}"
# shellcheck source=/dev/null
source "$BUILD_CONF"
else
log_warn "Build configuration not found: ${BUILD_CONF}"
log_warn "Using default values"
fi
# Normalize key directory variables to absolute paths anchored at PROJECT_ROOT.
# This prevents later re-sourcing from accidentally re-introducing relative paths.
if [[ -z "${INSTALL_DIR:-}" ]]; then
INSTALL_DIR="${PROJECT_ROOT}/initramfs"
elif [[ "${INSTALL_DIR}" != /* ]]; then
INSTALL_DIR="${PROJECT_ROOT}/${INSTALL_DIR#./}"
fi
if [[ -z "${COMPONENTS_DIR:-}" ]]; then
COMPONENTS_DIR="${PROJECT_ROOT}/components"
elif [[ "${COMPONENTS_DIR}" != /* ]]; then
COMPONENTS_DIR="${PROJECT_ROOT}/${COMPONENTS_DIR#./}"
fi
if [[ -z "${KERNEL_DIR:-}" ]]; then
KERNEL_DIR="${PROJECT_ROOT}/kernel"
elif [[ "${KERNEL_DIR}" != /* ]]; then
KERNEL_DIR="${PROJECT_ROOT}/${KERNEL_DIR#./}"
fi
if [[ -z "${DIST_DIR:-}" ]]; then
DIST_DIR="${PROJECT_ROOT}/dist"
elif [[ "${DIST_DIR}" != /* ]]; then
DIST_DIR="${PROJECT_ROOT}/${DIST_DIR#./}"
fi
# Export common variables
export SCRIPT_DIR PROJECT_ROOT
export INSTALL_DIR COMPONENTS_DIR KERNEL_DIR DIST_DIR
export -f log_info log_warn log_error log_debug
export -f safe_execute safe_execute_stream section_header
export -f command_exists in_container check_dependencies
export -f safe_mkdir safe_rmdir safe_copy
export -f is_absolute_path resolve_path get_file_size wait_for_file