#!/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" HOST_SSH_DIR="${SSH_MOUNT_DIR:-${HOME}/.ssh}" # Default to verbose, streaming logs for dev flows unless explicitly disabled export DEBUG="${DEBUG:-1}" # 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 ensure_builder_image() { section_header "Ensuring Builder Image" # Check for existing image under common tags (short and localhost-qualified) local candidates=("$BUILDER_IMAGE" "localhost/${BUILDER_IMAGE}") for img in "${candidates[@]}"; do if podman image exists "$img" 2>/dev/null; then log_info "Found builder image: ${img}" BUILDER_IMAGE="$img" return 0 fi done log_warn "Builder image not found locally; building: ${BUILDER_IMAGE}" safe_execute podman build -t "${BUILDER_IMAGE}" -f "${PROJECT_ROOT}/Dockerfile" "${PROJECT_ROOT}" if podman image exists "$BUILDER_IMAGE" 2>/dev/null; then log_info "Builder image built successfully: ${BUILDER_IMAGE}" return 0 fi # As a fallback, also tag with localhost to satisfy Podman short-name policy local local_tag="localhost/${BUILDER_IMAGE}" log_info "Tagging builder image as ${local_tag}" safe_execute podman tag "${BUILDER_IMAGE}" "${local_tag}" || true if podman image exists "$local_tag" 2>/dev/null; then BUILDER_IMAGE="$local_tag" fi } function dev_container_start() { section_header "Starting Development Container" # Ensure builder image exists (handles clean --all case and short-name policy) ensure_builder_image # 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 local podman_args=( 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 ) if [[ -d "$HOST_SSH_DIR" ]]; then log_info "Mounting SSH directory: ${HOST_SSH_DIR} -> /root/.ssh (read-only)" podman_args+=(-v "${HOST_SSH_DIR}:/root/.ssh:ro") else log_warn "SSH directory not found at ${HOST_SSH_DIR}; skipping SSH mount" fi podman_args+=( "$BUILDER_IMAGE" sleep infinity ) safe_execute podman "${podman_args[@]}" 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 "$@"