#!/usr/bin/env bash set -Eeuo pipefail ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) OUT=${1:-$ROOT/dist/csr-intake-x86_64.run} GUI="$ROOT/gui/target/release/intake-gui" SCANNER="$ROOT/scanner/target/release/pc-intake" MANIFEST="$ROOT/build/release.json" BUNDLE_CONFIG=${CSR_BUNDLE_CONFIG_DIR:-$ROOT/.bundle-secrets} for file in "$GUI" "$SCANNER" "$MANIFEST"; do [[ -f $file ]] || { echo 'Release files are missing. Run bash scripts/build-release.sh first.' >&2 exit 2 } done BUILD_ID=$(python3 "$ROOT/scripts/release-manifest.py" --source-id) GUI_VERSION=$("$GUI" --version) grep -qF "$BUILD_ID" <<< "$GUI_VERSION" || { echo 'Release binaries are stale. Run bash scripts/build-release.sh first.' >&2 exit 2 } VERSION=$(sed -E 's/^CS-R Intake ([^ ]+).*/\1/' <<< "$GUI_VERSION") TMP=$(mktemp -d) cleanup() { find "$TMP" -depth -delete 2>/dev/null || true } trap cleanup EXIT mkdir -p "$TMP/app" "$(dirname "$OUT")" install -m755 "$GUI" "$TMP/app/intake-gui" install -m755 "$SCANNER" "$TMP/app/pc-intake" install -m644 "$MANIFEST" "$TMP/app/release.json" python3 "$ROOT/scripts/release-manifest.py" --verify-release "$TMP/app" contains_credentials=0 if [[ -f $BUNDLE_CONFIG/inventory-api-key && -f $BUNDLE_CONFIG/inventory-server.crt ]]; then key=$(tr -d '\r\n' < "$BUNDLE_CONFIG/inventory-api-key") [[ -n $key && $key != *[[:space:]]* ]] || { echo 'Bundled inventory API key is empty or malformed.' >&2 exit 2 } openssl x509 -in "$BUNDLE_CONFIG/inventory-server.crt" -noout -checkend 0 >/dev/null || { echo 'Bundled inventory certificate is invalid or expired.' >&2 exit 2 } mkdir -m700 "$TMP/app/default-config" install -m600 "$BUNDLE_CONFIG/inventory-api-key" "$TMP/app/default-config/inventory-api-key" install -m600 "$BUNDLE_CONFIG/inventory-server.crt" "$TMP/app/default-config/inventory-server.crt" printf '%s\n' '{"url":"https://192.168.178.17"}' > "$TMP/app/default-config/inventory.json" chmod 600 "$TMP/app/default-config/inventory.json" contains_credentials=1 elif [[ -e $BUNDLE_CONFIG/inventory-api-key || -e $BUNDLE_CONFIG/inventory-server.crt ]]; then echo 'Both inventory-api-key and inventory-server.crt are required for bundled configuration.' >&2 exit 2 fi ( cd "$TMP/app" find . -type f ! -name SHA256SUMS -print0 | sort -z | xargs -0 sha256sum > SHA256SUMS ) tar -C "$TMP/app" -czf "$TMP/payload.tar.gz" . cat > "$TMP/launcher" </dev/null 2>&1) } if ! bundle_valid; then if [ -e "\$app_dir" ]; then find "\$app_dir" -depth -delete 2>/dev/null || true fi temp_dir=\$cache_root/.extract-\$BUILD_ID-\$\$ mkdir -p "\$temp_dir" trap 'find "\$temp_dir" -depth -delete 2>/dev/null || true' EXIT HUP INT TERM tail -n +"\$PAYLOAD_LINE" "\$0" | gzip -dc | tar -xf - -C "\$temp_dir" (cd "\$temp_dir" && sha256sum -c SHA256SUMS >/dev/null) mkdir -p "\$cache_root" if ! mv -T "\$temp_dir" "\$app_dir" 2>/dev/null; then find "\$temp_dir" -depth -delete 2>/dev/null || true fi trap - EXIT HUP INT TERM fi bundle_valid } case \${1:-} in --version) printf 'CS-R Intake %s ยท %s\n' "\$VERSION" "\$BUILD_ID" exit 0 ;; --check-bundle) extract_bundle printf 'Standalone bundle verified: %s\n' "\$BUILD_ID" exit 0 ;; esac extract_bundle data_dir=\${XDG_DATA_HOME:-\${HOME:-\$PWD}/.local/share}/csr-intake expect_data=0 has_data=0 for argument in "\$@"; do if [ "\$expect_data" -eq 1 ]; then data_dir=\$argument expect_data=0 elif [ "\$argument" = '--data' ]; then has_data=1 expect_data=1 fi done [ "\$expect_data" -eq 0 ] || { echo 'Missing directory after --data.' >&2 exit 2 } if [ -d "\$app_dir/default-config" ]; then config_dir=\$data_dir/config mkdir -p "\$config_dir" chmod 700 "\$config_dir" for name in inventory.json inventory-api-key inventory-server.crt; do target=\$config_dir/\$name if [ ! -e "\$target" ]; then install -m600 "\$app_dir/default-config/\$name" "\$target" fi done fi workshop_config=\$data_dir/config/workshop.json if [ -f "\$workshop_config" ]; then keyboard_layout=\$(python3 - "\$workshop_config" <<'PY' 2>/dev/null || true import json import sys layout = json.load(open(sys.argv[1], encoding='utf-8')).get('keyboard_layout', '') if layout in ('de', 'us'): print(layout) PY ) if [ -n "\$keyboard_layout" ]; then export XKB_DEFAULT_LAYOUT=\$keyboard_layout fi fi export CSR_SCANNER="\$app_dir/pc-intake" if [ "\$has_data" -eq 1 ]; then exec "\$app_dir/intake-gui" "\$@" fi exec "\$app_dir/intake-gui" "\$@" --data "\$data_dir" EOF payload_line=$(( $(wc -l < "$TMP/launcher") + 1 )) sed -i "s/__PAYLOAD_LINE__/$payload_line/" "$TMP/launcher" cat "$TMP/launcher" "$TMP/payload.tar.gz" > "$OUT" if [[ $contains_credentials -eq 1 ]]; then chmod 700 "$OUT" else chmod 755 "$OUT" fi "$OUT" --check-bundle if [[ $contains_credentials -eq 1 ]]; then echo "Standalone executable created with inventory configuration: $OUT" else echo "Standalone executable created: $OUT" fi