#!/bin/bash # compile.sh - Compile installer sources into a single distributable script # This script is run during the build process to create the final installer set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" OUTPUT_FILE="${1:-install-usm.sh}" VERSION="${2:-0.1.0}" USM_SOURCE_DIR="${3:-$SCRIPT_DIR/../src}" # Temporary directory for building WORK_DIR=$(mktemp -d) trap "rm -rf $WORK_DIR" EXIT echo "Compiling USM installer..." echo " Version: $VERSION" echo " Output: $OUTPUT_FILE" echo " USM Source: $USM_SOURCE_DIR" # Step 1: Clone external repositories echo "Cloning external repositories..." SOURCES_DIR="$WORK_DIR/sources" mkdir -p "$SOURCES_DIR" # Clone Invercargill if [[ ! -d "$SOURCES_DIR/Invercargill" ]]; then echo " Cloning Invercargill..." git clone --depth 1 https://fabrica.unitatem.net/Tilo15/Invercargill.git "$SOURCES_DIR/Invercargill" 2>/dev/null || { echo "Warning: Could not clone Invercargill, will try without --depth 1" git clone https://fabrica.unitatem.net/Tilo15/Invercargill.git "$SOURCES_DIR/Invercargill" 2>/dev/null || { echo "Error: Could not clone Invercargill repository" exit 1 } } fi # Clone Invercargill-Json if [[ ! -d "$SOURCES_DIR/Invercargill-Json" ]]; then echo " Cloning Invercargill-Json..." git clone --depth 1 https://git.sr.ht/~tilo15/Invercargill-Json "$SOURCES_DIR/Invercargill-Json" 2>/dev/null || { echo "Warning: Could not clone Invercargill-Json, will try without --depth 1" git clone https://git.sr.ht/~tilo15/Invercargill-Json "$SOURCES_DIR/Invercargill-Json" 2>/dev/null || { echo "Error: Could not clone Invercargill-Json repository" exit 1 } } fi # Copy USM source echo "Copying USM source..." mkdir -p "$SOURCES_DIR/usm" cp -r "$USM_SOURCE_DIR" "$SOURCES_DIR/usm/src" cp "$USM_SOURCE_DIR/../MANIFEST.usm" "$SOURCES_DIR/usm/" 2>/dev/null || true cp "$USM_SOURCE_DIR/../usm.config" "$SOURCES_DIR/usm/" 2>/dev/null || true # Step 2: Create the combined script header echo "Building combined script..." HEADER_FILE="$WORK_DIR/header.sh" cat > "$HEADER_FILE" << SCRIPT_HEADER #!/bin/bash # USM Installer - self-contained installation script # This script was automatically generated. Do not edit. # Version: $VERSION # Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC") set -e SCRIPT_HEADER # Step 3: Combine all shell scripts # We need to rename the PM functions to avoid conflicts COMBINED_FILE="$WORK_DIR/combined.sh" # Function to process a bash script (minify and adapt) process_script() { local file="$1" local section="$2" echo "# === $section ===" # For PM files, we need to prefix the functions case "$section" in "Package Manager APT") # Rename functions with apt_ prefix sed -e 's/^pm_is_installed()/pm_apt_is_installed()/' \ -e 's/^pm_get_package_name()/pm_apt_get_package_name()/' \ -e 's/^pm_update()/pm_apt_update()/' \ -e 's/^pm_install()/pm_apt_install()/' \ -e 's/^get_missing_deps()/get_missing_deps_apt()/' \ "$file" ;; "Package Manager DNF") # Rename functions with dnf_ prefix sed -e 's/^pm_is_installed()/pm_dnf_is_installed()/' \ -e 's/^pm_get_package_name()/pm_dnf_get_package_name()/' \ -e 's/^pm_update()/pm_dnf_update()/' \ -e 's/^pm_install()/pm_dnf_install()/' \ -e 's/^get_missing_deps()/get_missing_deps_dnf()/' \ "$file" ;; *) cat "$file" ;; esac } # Create the dispatcher functions DISPATCHER_FILE="$WORK_DIR/dispatcher.sh" cat > "$DISPATCHER_FILE" << 'DISPATCHER' # === Package Manager Dispatcher === # These functions dispatch to the appropriate PM implementation pm_is_installed() { case "$PM_TYPE" in apt) pm_apt_is_installed "$@" ;; dnf) pm_dnf_is_installed "$@" ;; esac } pm_get_package_name() { case "$PM_TYPE" in apt) pm_apt_get_package_name "$@" ;; dnf) pm_dnf_get_package_name "$@" ;; esac } pm_update() { case "$PM_TYPE" in apt) pm_apt_update "$@" ;; dnf) pm_dnf_update "$@" ;; esac } pm_install() { case "$PM_TYPE" in apt) pm_apt_install "$@" ;; dnf) pm_dnf_install "$@" ;; esac } get_missing_deps() { case "$PM_TYPE" in apt) get_missing_deps_apt "$@" ;; dnf) get_missing_deps_dnf "$@" ;; esac } DISPATCHER # Combine scripts in order - IMPORTANT: # 1. PM implementations (apt, dnf) come first with prefixed names # 2. Dispatcher comes next to provide the generic interface # 3. pm_base.sh uses the generic interface (get_missing_deps, etc.) # 4. main.sh and others come last { process_script "$SCRIPT_DIR/utils.sh" "Utils" echo "" process_script "$SCRIPT_DIR/ui.sh" "UI" echo "" # PM implementations first (with prefixed function names) process_script "$SCRIPT_DIR/pm_apt.sh" "Package Manager APT" echo "" process_script "$SCRIPT_DIR/pm_dnf.sh" "Package Manager DNF" echo "" # Dispatcher provides the generic interface using PM_TYPE cat "$DISPATCHER_FILE" echo "" # Now pm_base.sh can use the generic interface process_script "$SCRIPT_DIR/pm_base.sh" "Package Manager Base" echo "" process_script "$SCRIPT_DIR/build_config.sh" "Build Config" echo "" process_script "$SCRIPT_DIR/main.sh" "Main" echo "" # Add the main call at the very end echo "# Run main function" echo 'main "$@"' } > "$COMBINED_FILE" # Replace version placeholder in combined file sed -i "s/@VERSION@/$VERSION/g" "$COMBINED_FILE" # Step 4: Create the payload echo "Creating compressed payload..." PAYLOAD_FILE="$WORK_DIR/payload.tar.xz" # Use highest compression for smaller file size tar -C "$SOURCES_DIR" -cf - . | xz -9 -T0 > "$PAYLOAD_FILE" # Step 5: Assemble final installer echo "Assembling final installer..." { cat "$HEADER_FILE" cat "$COMBINED_FILE" echo "" echo "__PAYLOAD_START__" cat "$PAYLOAD_FILE" } > "$OUTPUT_FILE" # Make executable chmod +x "$OUTPUT_FILE" # Calculate output size OUTPUT_SIZE=$(du -h "$OUTPUT_FILE" | cut -f1) echo "" echo "✓ Installer created successfully!" echo " Output: $OUTPUT_FILE" echo " Size: $OUTPUT_SIZE"