Files
komp_ac/packaging/appimage/container-build-debian.sh
2026-09-02 21:27:54 +02:00

225 lines
6.5 KiB
Bash
Executable File

#!/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 \
OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu \
OPENSSL_INCLUDE_DIR=/usr/include \
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
gui_features=()
if [[ "$server_db_mode" == embedded ]]; then
gui_features=(--features full-embed)
fi
echo "+ yarn tauri build --bundles appimage ${gui_features[*]}"
set +e
APPIMAGE_EXTRACT_AND_RUN=1 \
CARGO_TARGET_DIR=/cargo-target \
yarn tauri build --bundles appimage "${gui_features[@]}"
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"