#!/bin/bash set -euo pipefail if [[ "$(uname -m)" != "x86_64" ]]; then echo "The Debian production build requires an x86_64 container" >&2 exit 1 fi output_name="${OUTPUT_NAME:-KompAC-debian12-amd64.AppImage}" if [[ "$output_name" == */* || "$output_name" != *.AppImage ]]; then echo "OUTPUT_NAME must be a portable filename ending in .AppImage" >&2 exit 1 fi server_feature="${SERVER_FEATURE:-}" server_db_mode="${SERVER_DB_MODE:-}" case "$server_feature:$server_db_mode" in full-embed:embedded|full:external) ;; *) echo "Expected SERVER_FEATURE/SERVER_DB_MODE to be full-embed/embedded or full/external" >&2 exit 2 ;; esac source_root=/work/source rm -rf -- "$source_root" mkdir -p "$source_root" /out/bin /cargo-target /var/cache/yarn echo "+ copying source snapshot into the build container" tar \ --exclude-vcs \ --exclude='./target' \ --exclude='*/target' \ --exclude='./.direnv' \ --exclude='*/.direnv' \ --exclude='./.env' \ --exclude='*/.env' \ --exclude='./.env.*' \ --exclude='*/.env.*' \ --exclude='./.env_*' \ --exclude='*/.env_*' \ --exclude='*/.envrc' \ --exclude='*/.agents' \ --exclude='*/.claude' \ --exclude='*/.codex' \ --exclude='*/.codex-scheduled' \ --exclude='*/.forge' \ --exclude='*/.opencode' \ --exclude='*/.idea' \ --exclude='*/.vscode' \ --exclude='./.aider*' \ --exclude='*/tantivy_indexes' \ --exclude='*/dumps' \ --exclude='*/.postgres-data' \ --exclude='*/benchmark-results' \ --exclude='*/.mutants-run' \ --exclude='./mutants*' \ --exclude='*/metrics_output' \ --exclude='*/metrics.json' \ --exclude='*/perf.data*' \ --exclude='*/flamegraph.svg' \ --exclude='*/__pycache__' \ --exclude='*/node_modules' \ --exclude='./client-gui2/dist' \ --exclude='./client-gui2/dist-ssr' \ --exclude='./client-gui2/coverage' \ --exclude='./client-gui2/.vite' \ --exclude='./client-gui2/src-tauri/gen/schemas' \ --exclude='./client-gui2/scripts' \ --exclude='*.tsbuildinfo' \ --exclude='*.log' \ --exclude='*.swp' \ --exclude='*.swo' \ --exclude='*~' \ --exclude='*.local' \ --exclude='./server/docs-prod' \ --exclude='./testing' \ --exclude='./run_bins' \ --exclude='./corporate_erp' \ --exclude='./debian-dist' \ --exclude='./result' \ --exclude='./result-*' \ -C /source -cf - \ Cargo.toml \ Cargo.lock \ .cargo \ .sqlx \ client \ client-gui2 \ common \ komp-app \ packaging \ search \ server \ tui-canvas \ tui-pages \ web \ | tar -C "$source_root" -xf - unexpected_snapshot_path="$(find "$source_root" \ \( \ -name target -o \ -name node_modules -o \ -name tantivy_indexes -o \ -name dumps -o \ -name .postgres-data -o \ -name .direnv -o \ -name .env -o \ -name '.env.*' -o \ -name '.env_*' -o \ -name .envrc -o \ -name benchmark-results -o \ -name metrics_output -o \ -name 'perf.data*' \ \) \ -print -quit)" if [[ -n "$unexpected_snapshot_path" ]]; then echo "Generated or private data entered the production snapshot: $unexpected_snapshot_path" >&2 exit 1 fi echo "Source snapshot size: $(du -sh "$source_root" | cut -f1)" cd "$source_root/client-gui2" echo "+ yarn install --frozen-lockfile --non-interactive" yarn install --frozen-lockfile --non-interactive cd "$source_root" echo "+ cargo build --release --package server --features $server_feature" OPENSSL_STATIC=1 \ POSTGRESQL_VERSION='=17.10.0' \ SQLX_OFFLINE=true \ CARGO_TARGET_DIR=/cargo-target \ cargo build --release --package server --features "$server_feature" server_binary=/cargo-target/release/server if [[ ! -x "$server_binary" ]]; then echo "The release server binary was not produced" >&2 exit 1 fi if readelf -l "$server_binary" | grep -q '/nix/store'; then echo "The Debian server unexpectedly contains a Nix interpreter" >&2 exit 1 fi if readelf -d "$server_binary" | grep -Eq 'libssl\.so|libcrypto\.so'; then echo "The release server did not link OpenSSL statically" >&2 exit 1 fi install -Dm755 "$server_binary" /out/bin/server cd "$source_root/client-gui2" appimage_bundle_dir=/cargo-target/release/bundle/appimage case "$appimage_bundle_dir" in /cargo-target/release/bundle/appimage) rm -rf -- "$appimage_bundle_dir" ;; *) echo "Refusing to clear unexpected AppImage staging path: $appimage_bundle_dir" >&2 exit 1 ;; esac echo "+ yarn tauri build --bundles appimage" set +e APPIMAGE_EXTRACT_AND_RUN=1 \ CARGO_TARGET_DIR=/cargo-target \ yarn tauri build --bundles appimage tauri_status=$? set -e shopt -s nullglob gui_appimages=("$appimage_bundle_dir"/*.AppImage) gui_bundle="" if [[ $tauri_status -eq 0 && ${#gui_appimages[@]} -gt 0 ]]; then gui_bundle="${gui_appimages[0]}" for candidate in "${gui_appimages[@]:1}"; do if [[ "$candidate" -nt "$gui_bundle" ]]; then gui_bundle="$candidate" fi done fi if [[ -z "$gui_bundle" ]]; then gui_appdir="$appimage_bundle_dir/komp_ac.AppDir" if [[ -d "$gui_appdir" ]] \ && cmp -s /cargo-target/release/client-gui2 "$gui_appdir/usr/bin/client-gui2"; then gui_bundle="$gui_appdir" echo "Tauri completed its AppDir; bypassing its failed linuxdeploy output step" else echo "Tauri did not produce a current GUI AppImage or AppDir" >&2 exit "${tauri_status:-1}" fi fi if [[ $tauri_status -ne 0 ]]; then echo "Continuing because the current release GUI and AppDir were completed before linuxdeploy failed" fi install -Dm755 /cargo-target/release/client-gui2 /out/bin/client-gui2 cd "$source_root" echo "+ packaging/appimage/assemble.sh $gui_bundle $server_binary $server_db_mode /out/$output_name" packaging/appimage/assemble.sh \ "$gui_bundle" \ "$server_binary" \ "$server_db_mode" \ "/out/$output_name" if [[ ! -x "/out/$output_name" ]]; then echo "The combined AppImage was not produced" >&2 exit 1 fi echo echo "Debian production artifacts:" echo " /out/$output_name" echo " /out/bin/client-gui2" echo " /out/bin/server"