forked from tfgrid/zosbuilder
feat: Implement complete Zero OS Alpine Initramfs Builder
- 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
This commit is contained in:
276
scripts/lib/docker.sh
Normal file
276
scripts/lib/docker.sh
Normal file
@@ -0,0 +1,276 @@
|
||||
#!/bin/bash
|
||||
# Container management for rootless Docker/Podman builds
|
||||
|
||||
# Source common functions
|
||||
LIB_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${LIB_SCRIPT_DIR}/common.sh"
|
||||
|
||||
# Container configuration
|
||||
CONTAINER_RUNTIME=""
|
||||
BUILDER_IMAGE="zero-os-builder:latest"
|
||||
ALPINE_VERSION="${ALPINE_VERSION:-3.22}"
|
||||
|
||||
# Detect available container runtime
|
||||
function docker_detect_runtime() {
|
||||
section_header "Detecting Container Runtime"
|
||||
|
||||
if command_exists "podman"; then
|
||||
CONTAINER_RUNTIME="podman"
|
||||
log_info "Using Podman as container runtime"
|
||||
elif command_exists "docker"; then
|
||||
CONTAINER_RUNTIME="docker"
|
||||
log_info "Using Docker as container runtime"
|
||||
else
|
||||
log_error "No container runtime found (podman or docker required)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if rootless setup is working
|
||||
docker_verify_rootless
|
||||
}
|
||||
|
||||
# Verify rootless container setup
|
||||
function docker_verify_rootless() {
|
||||
section_header "Verifying Rootless Container Setup"
|
||||
|
||||
log_info "Checking ${CONTAINER_RUNTIME} rootless configuration"
|
||||
safe_execute ${CONTAINER_RUNTIME} system info
|
||||
|
||||
# Test basic rootless functionality
|
||||
log_info "Testing rootless container execution"
|
||||
safe_execute ${CONTAINER_RUNTIME} run --rm alpine:${ALPINE_VERSION} echo "Rootless container test successful"
|
||||
|
||||
log_info "Rootless container setup verified"
|
||||
}
|
||||
|
||||
# Build container image with build tools
|
||||
function docker_build_container() {
|
||||
local dockerfile_path="${1:-${PROJECT_ROOT}/Dockerfile}"
|
||||
local tag="${2:-${BUILDER_IMAGE}}"
|
||||
|
||||
section_header "Building Container Image"
|
||||
|
||||
# Create Dockerfile if it doesn't exist
|
||||
if [[ ! -f "$dockerfile_path" ]]; then
|
||||
docker_create_dockerfile "$dockerfile_path"
|
||||
fi
|
||||
|
||||
log_info "Building container image: ${tag}"
|
||||
safe_execute ${CONTAINER_RUNTIME} build -t "${tag}" -f "${dockerfile_path}" "${PROJECT_ROOT}"
|
||||
|
||||
log_info "Container image built successfully: ${tag}"
|
||||
}
|
||||
|
||||
# Create optimized Dockerfile for build environment
|
||||
function docker_create_dockerfile() {
|
||||
local dockerfile_path="$1"
|
||||
|
||||
section_header "Creating Dockerfile"
|
||||
|
||||
cat > "$dockerfile_path" << 'EOF'
|
||||
FROM alpine:3.22
|
||||
|
||||
# Install build dependencies
|
||||
RUN apk add --no-cache \
|
||||
build-base \
|
||||
rust \
|
||||
cargo \
|
||||
upx \
|
||||
git \
|
||||
wget \
|
||||
tar \
|
||||
gzip \
|
||||
xz \
|
||||
cpio \
|
||||
binutils \
|
||||
linux-headers \
|
||||
musl-dev \
|
||||
pkgconfig \
|
||||
openssl-dev
|
||||
|
||||
# Add Rust musl target
|
||||
RUN rustup target add x86_64-unknown-linux-musl
|
||||
|
||||
# Create non-root user for builds
|
||||
RUN adduser -D -s /bin/sh builder && \
|
||||
chown -R builder:builder /home/builder
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /workspace
|
||||
|
||||
# Switch to non-root user
|
||||
USER builder
|
||||
|
||||
# Set environment variables for static linking
|
||||
ENV RUSTFLAGS="-C target-feature=+crt-static"
|
||||
ENV CC_x86_64_unknown_linux_musl="musl-gcc"
|
||||
ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER="musl-gcc"
|
||||
|
||||
CMD ["/bin/sh"]
|
||||
EOF
|
||||
|
||||
log_info "Created Dockerfile: ${dockerfile_path}"
|
||||
}
|
||||
|
||||
# Start rootless container for building
|
||||
function docker_start_rootless() {
|
||||
local image="${1:-${BUILDER_IMAGE}}"
|
||||
local workdir="${2:-/workspace}"
|
||||
local command="${3:-/bin/sh}"
|
||||
|
||||
section_header "Starting Rootless Container"
|
||||
|
||||
# Setup volume mounts
|
||||
local user_args="--user $(id -u):$(id -g)"
|
||||
local volume_args="-v ${PROJECT_ROOT}:${workdir}"
|
||||
local env_args=""
|
||||
|
||||
# Pass through environment variables
|
||||
local env_vars=(
|
||||
"DEBUG"
|
||||
"ALPINE_VERSION"
|
||||
"KERNEL_VERSION"
|
||||
"RUST_TARGET"
|
||||
"OPTIMIZATION_LEVEL"
|
||||
)
|
||||
|
||||
for var in "${env_vars[@]}"; do
|
||||
if [[ -n "${!var:-}" ]]; then
|
||||
env_args="${env_args} -e ${var}=${!var}"
|
||||
fi
|
||||
done
|
||||
|
||||
log_info "Starting container with rootless privileges"
|
||||
safe_execute ${CONTAINER_RUNTIME} run --rm -it \
|
||||
${user_args} \
|
||||
${volume_args} \
|
||||
${env_args} \
|
||||
-w "${workdir}" \
|
||||
"${image}" \
|
||||
${command}
|
||||
}
|
||||
|
||||
# Run build command in container
|
||||
function docker_run_build() {
|
||||
local build_script="${1:-./scripts/build.sh}"
|
||||
local image="${2:-${BUILDER_IMAGE}}"
|
||||
|
||||
section_header "Running Build in Container"
|
||||
|
||||
# Ensure build script is executable
|
||||
safe_execute chmod +x "${PROJECT_ROOT}/${build_script}"
|
||||
|
||||
# Setup container arguments
|
||||
local user_args="--user $(id -u):$(id -g)"
|
||||
local volume_args="-v ${PROJECT_ROOT}:/workspace"
|
||||
local work_args="-w /workspace"
|
||||
|
||||
log_info "Executing build script in container: ${build_script}"
|
||||
safe_execute ${CONTAINER_RUNTIME} run --rm \
|
||||
${user_args} \
|
||||
${volume_args} \
|
||||
${work_args} \
|
||||
"${image}" \
|
||||
${build_script}
|
||||
}
|
||||
|
||||
# Commit container state for reuse
|
||||
function docker_commit_builder() {
|
||||
local container_id="$1"
|
||||
local new_tag="${2:-${BUILDER_IMAGE}-cached}"
|
||||
|
||||
section_header "Committing Builder Container"
|
||||
|
||||
log_info "Committing container ${container_id} as ${new_tag}"
|
||||
safe_execute ${CONTAINER_RUNTIME} commit "${container_id}" "${new_tag}"
|
||||
|
||||
log_info "Container committed successfully: ${new_tag}"
|
||||
}
|
||||
|
||||
# Clean up container images
|
||||
function docker_cleanup() {
|
||||
local keep_builder="${1:-false}"
|
||||
|
||||
section_header "Cleaning Up Container Images"
|
||||
|
||||
if [[ "$keep_builder" != "true" ]]; then
|
||||
log_info "Removing builder images"
|
||||
safe_execute ${CONTAINER_RUNTIME} rmi "${BUILDER_IMAGE}" || true
|
||||
safe_execute ${CONTAINER_RUNTIME} rmi "${BUILDER_IMAGE}-cached" || true
|
||||
fi
|
||||
|
||||
log_info "Pruning unused containers and images"
|
||||
safe_execute ${CONTAINER_RUNTIME} system prune -f
|
||||
|
||||
log_info "Container cleanup complete"
|
||||
}
|
||||
|
||||
# Check container runtime capabilities
|
||||
function docker_check_capabilities() {
|
||||
section_header "Checking Container Capabilities"
|
||||
|
||||
# Check user namespace support
|
||||
if [[ -f /proc/sys/user/max_user_namespaces ]]; then
|
||||
local max_namespaces=$(cat /proc/sys/user/max_user_namespaces)
|
||||
log_info "User namespaces available: ${max_namespaces}"
|
||||
|
||||
if [[ "$max_namespaces" -eq 0 ]]; then
|
||||
log_warn "User namespaces are disabled, rootless containers may not work"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check subuid/subgid configuration
|
||||
local current_user=$(whoami)
|
||||
if [[ -f /etc/subuid ]] && grep -q "^${current_user}:" /etc/subuid; then
|
||||
log_info "subuid configured for user: ${current_user}"
|
||||
else
|
||||
log_warn "subuid not configured for user: ${current_user}"
|
||||
log_warn "Run: echo '${current_user}:100000:65536' | sudo tee -a /etc/subuid"
|
||||
fi
|
||||
|
||||
if [[ -f /etc/subgid ]] && grep -q "^${current_user}:" /etc/subgid; then
|
||||
log_info "subgid configured for user: ${current_user}"
|
||||
else
|
||||
log_warn "subgid not configured for user: ${current_user}"
|
||||
log_warn "Run: echo '${current_user}:100000:65536' | sudo tee -a /etc/subgid"
|
||||
fi
|
||||
}
|
||||
|
||||
# Setup rootless environment
|
||||
function docker_setup_rootless() {
|
||||
section_header "Setting Up Rootless Environment"
|
||||
|
||||
local current_user=$(whoami)
|
||||
|
||||
# Check if running as root
|
||||
if [[ "$EUID" -eq 0 ]]; then
|
||||
log_error "Do not run as root. Rootless containers require non-root user."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check and setup subuid/subgid if needed
|
||||
if ! grep -q "^${current_user}:" /etc/subuid 2>/dev/null; then
|
||||
log_info "Setting up subuid for ${current_user}"
|
||||
echo "${current_user}:100000:65536" | sudo tee -a /etc/subuid
|
||||
fi
|
||||
|
||||
if ! grep -q "^${current_user}:" /etc/subgid 2>/dev/null; then
|
||||
log_info "Setting up subgid for ${current_user}"
|
||||
echo "${current_user}:100000:65536" | sudo tee -a /etc/subgid
|
||||
fi
|
||||
|
||||
# Initialize container runtime if needed
|
||||
if [[ "$CONTAINER_RUNTIME" == "podman" ]]; then
|
||||
log_info "Initializing Podman for rootless use"
|
||||
safe_execute podman system migrate || true
|
||||
fi
|
||||
|
||||
log_info "Rootless environment setup complete"
|
||||
}
|
||||
|
||||
# Export functions
|
||||
export -f docker_detect_runtime docker_verify_rootless
|
||||
export -f docker_build_container docker_create_dockerfile
|
||||
export -f docker_start_rootless docker_run_build
|
||||
export -f docker_commit_builder docker_cleanup
|
||||
export -f docker_check_capabilities docker_setup_rootless
|
||||
Reference in New Issue
Block a user