#!/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}" INVERCARGILL_TAG="v1.0.1" # e.g. "v1.0.0"; empty = default branch INVERCARGILL_JSON_TAG="v0.1.1" # e.g. "v0.1.1"; empty = default branch USM_TAG="" # empty = use local source directory # 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" if [[ -n "$USM_TAG" ]]; then echo " USM Source: usm.git at tag \"$USM_TAG\"" else echo " USM Source: $USM_SOURCE_DIR" fi # Step 1: Clone external repositories echo "Cloning external repositories..." SOURCES_DIR="$WORK_DIR/sources" mkdir -p "$SOURCES_DIR" # Clone Invercargill INVERCARGILL_BRANCH_ARGS=() if [[ -n "$INVERCARGILL_TAG" ]]; then INVERCARGILL_BRANCH_ARGS+=(--branch "$INVERCARGILL_TAG") fi if [[ ! -d "$SOURCES_DIR/Invercargill" ]]; then echo " Cloning Invercargill..." git clone --depth 1 "${INVERCARGILL_BRANCH_ARGS[@]}" 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 "${INVERCARGILL_BRANCH_ARGS[@]}" 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 INVERCARGILL_JSON_BRANCH_ARGS=() if [[ -n "$INVERCARGILL_JSON_TAG" ]]; then INVERCARGILL_JSON_BRANCH_ARGS+=(--branch "$INVERCARGILL_JSON_TAG") fi if [[ ! -d "$SOURCES_DIR/Invercargill-Json" ]]; then echo " Cloning Invercargill-Json..." git clone --depth 1 "${INVERCARGILL_JSON_BRANCH_ARGS[@]}" 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 "${INVERCARGILL_JSON_BRANCH_ARGS[@]}" 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 # Clone USM at a tag instead of using the local source tree; the copy steps # below read everything through USM_SOURCE_DIR, so pointing it at the clone's # src/ picks up the tagged MANIFEST.usm, usm.config and spm/ helpers as well if [[ -n "$USM_TAG" ]]; then echo " Cloning USM at tag \"$USM_TAG\"..." USM_CLONE_DIR="$WORK_DIR/usm-clone" git clone --depth 1 --branch "$USM_TAG" https://fabrica.unitatem.net/Tilo15/usm.git "$USM_CLONE_DIR" 2>/dev/null || { echo "Warning: Could not clone USM at tag \"$USM_TAG\", will try without --depth 1" git clone --branch "$USM_TAG" https://fabrica.unitatem.net/Tilo15/usm.git "$USM_CLONE_DIR" 2>/dev/null || { echo "Error: Could not clone USM repository at tag \"$USM_TAG\"" exit 1 } } USM_SOURCE_DIR="$USM_CLONE_DIR/src" fi # Copy USM source (USM's meson.build is in src/, not project root) echo "Copying USM source..." mkdir -p "$SOURCES_DIR/usm" # USM_SOURCE_DIR points to the src/ directory which contains meson.build # Copy the entire src directory contents to usm/ cp -r "$USM_SOURCE_DIR"/* "$SOURCES_DIR/usm/" 2>/dev/null || true # Copy additional root level files for reference 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 # The SPM helpers live in spm/ at the project root, outside src/, but # src/meson.build configures them via '../spm//usm-spm-' — the # payload must therefore carry spm/ as a SIBLING of usm/ so the relative # inputs resolve inside the extracted tree echo "Copying USM SPM helpers..." mkdir -p "$SOURCES_DIR/spm" cp -r "$USM_SOURCE_DIR/../spm"/* "$SOURCES_DIR/spm/" 2>/dev/null || { echo "Error: Could not copy the spm/ directory from next to $USM_SOURCE_DIR" exit 1 } # 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: Auto-detect package manager implementations PM_IMPLEMENTATIONS=() for pm_file in "$SCRIPT_DIR"/pm_*.sh; do if [[ -f "$pm_file" ]]; then # Extract PM name from filename (pm_apt.sh -> apt) pm_name=$(basename "$pm_file" .sh | sed 's/^pm_//') # Skip pm_base.sh - it's the interface, not an implementation if [[ "$pm_name" != "base" ]]; then PM_IMPLEMENTATIONS+=("$pm_name") fi fi done echo " Detected package managers: ${PM_IMPLEMENTATIONS[*]}" # Step 4: Combine all shell scripts COMBINED_FILE="$WORK_DIR/combined.sh" # Build the PM implementations array initialization PM_ARRAY_FILE="$WORK_DIR/pm_array.sh" cat > "$PM_ARRAY_FILE" << PM_ARRAY # Auto-detected package manager implementations PM_IMPLEMENTATIONS=(${PM_IMPLEMENTATIONS[*]}) PM_ARRAY # Function to process a script file process_script() { local file="$1" local section="$2" echo "# === $section ===" cat "$file" } # Combine scripts in order { process_script "$SCRIPT_DIR/utils.sh" "Utils" echo "" process_script "$SCRIPT_DIR/ui.sh" "UI" echo "" # Include PM implementations first for pm_name in "${PM_IMPLEMENTATIONS[@]}"; do process_script "$SCRIPT_DIR/pm_${pm_name}.sh" "Package Manager ${pm_name}" echo "" done # Include PM array initialization cat "$PM_ARRAY_FILE" echo "" # Include pm_base which uses the array 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 "$@"' echo "" echo "# Exit before payload" echo "exit 0" } > "$COMBINED_FILE" # Replace version placeholder in combined file sed -i "s/@VERSION@/$VERSION/g" "$COMBINED_FILE" # Step 5: 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 6: 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"