building appimage on the nixos for debian
This commit is contained in:
Submodule client-gui2 updated: 6b9004ef02...e80f9f12ed
Binary file not shown.
120
packaging/appimage/AppRun
Executable file
120
packaging/appimage/AppRun
Executable file
@@ -0,0 +1,120 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
umask 077
|
||||||
|
|
||||||
|
if [[ -z "${APPDIR:-}" ]]; then
|
||||||
|
echo "Komp AC: APPDIR is not set by the AppImage runtime" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
data_home="${XDG_DATA_HOME:-${HOME:?HOME is not set}/.local/share}"
|
||||||
|
data_root="${KOMP_AC_DATA_DIR:-$data_home/komp_ac}"
|
||||||
|
postgres_root="$data_root/postgresql"
|
||||||
|
dump_root="$data_root/dumps"
|
||||||
|
tantivy_root="$data_root/tantivy_indexes"
|
||||||
|
secret_file="$data_root/jwt.secret"
|
||||||
|
log_file="$data_root/server.log"
|
||||||
|
lock_dir="$data_root/appimage.lock"
|
||||||
|
server="$APPDIR/usr/bin/komp-ac-server"
|
||||||
|
gui="$APPDIR/AppRun.gui"
|
||||||
|
server_db_mode_file="$APPDIR/usr/share/komp-ac/server-db-mode"
|
||||||
|
|
||||||
|
if [[ ! -r "$server_db_mode_file" ]]; then
|
||||||
|
echo "Komp AC: bundled server database mode is missing" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
server_db_mode="$(<"$server_db_mode_file")"
|
||||||
|
if [[ "$server_db_mode" != embedded && "$server_db_mode" != external ]]; then
|
||||||
|
echo "Komp AC: invalid bundled server database mode: $server_db_mode" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$data_root" "$postgres_root" "$dump_root" "$tantivy_root"
|
||||||
|
|
||||||
|
if [[ ! -s "$secret_file" ]]; then
|
||||||
|
secret_tmp="$secret_file.$$"
|
||||||
|
if ! od -An -N 48 -tx1 /dev/urandom | tr -d ' \n' > "$secret_tmp"; then
|
||||||
|
rm -f -- "$secret_tmp"
|
||||||
|
echo "Komp AC: could not generate the local server secret" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
chmod 600 "$secret_tmp"
|
||||||
|
if [[ -e "$secret_file" ]]; then
|
||||||
|
rm -f -- "$secret_tmp"
|
||||||
|
else
|
||||||
|
mv -- "$secret_tmp" "$secret_file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! mkdir "$lock_dir" 2>/dev/null; then
|
||||||
|
lock_pid=""
|
||||||
|
if [[ -r "$lock_dir/pid" ]]; then
|
||||||
|
read -r lock_pid < "$lock_dir/pid" || true
|
||||||
|
fi
|
||||||
|
if [[ "$lock_pid" =~ ^[0-9]+$ ]] && kill -0 "$lock_pid" 2>/dev/null; then
|
||||||
|
echo "Komp AC is already running (launcher PID $lock_pid)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rm -f -- "$lock_dir/pid"
|
||||||
|
if ! rmdir "$lock_dir" 2>/dev/null || ! mkdir "$lock_dir" 2>/dev/null; then
|
||||||
|
echo "Komp AC: could not acquire the application lock at $lock_dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
printf '%s\n' "$$" > "$lock_dir/pid"
|
||||||
|
|
||||||
|
server_pid=""
|
||||||
|
cleanup() {
|
||||||
|
status=$?
|
||||||
|
trap - EXIT INT TERM HUP
|
||||||
|
if [[ -n "$server_pid" ]] && kill -0 "$server_pid" 2>/dev/null; then
|
||||||
|
kill -TERM "$server_pid" 2>/dev/null || true
|
||||||
|
wait "$server_pid" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
rm -f -- "$lock_dir/pid"
|
||||||
|
rmdir "$lock_dir" 2>/dev/null || true
|
||||||
|
exit "$status"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
trap 'exit 130' INT
|
||||||
|
trap 'exit 143' TERM
|
||||||
|
trap 'exit 129' HUP
|
||||||
|
|
||||||
|
export GRPC_ENDPOINT="http://127.0.0.1:50051"
|
||||||
|
|
||||||
|
if (exec 3<>/dev/tcp/127.0.0.1/50051) 2>/dev/null; then
|
||||||
|
echo "Komp AC: local port 50051 is already in use" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
RUST_DB_MODE="$server_db_mode" \
|
||||||
|
KOMP_AC_POSTGRES_DIR="$postgres_root" \
|
||||||
|
PG_DUMP_DIR="$dump_root" \
|
||||||
|
TANTIVY_INDEX_DIR="$tantivy_root" \
|
||||||
|
JWT_SECRET="$(<"$secret_file")" \
|
||||||
|
GRPC_LISTEN_ADDRESS="127.0.0.1:50051" \
|
||||||
|
"$server" server >> "$log_file" 2>&1 &
|
||||||
|
server_pid=$!
|
||||||
|
|
||||||
|
ready=false
|
||||||
|
for ((attempt = 0; attempt < 600; attempt++)); do
|
||||||
|
if ! kill -0 "$server_pid" 2>/dev/null; then
|
||||||
|
wait "$server_pid" || true
|
||||||
|
echo "Komp AC: the bundled server failed to start; see $log_file" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if (exec 3<>/dev/tcp/127.0.0.1/50051) 2>/dev/null; then
|
||||||
|
ready=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$ready" != true ]]; then
|
||||||
|
echo "Komp AC: the bundled server did not become ready; see $log_file" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$gui"
|
||||||
55
packaging/appimage/Containerfile.debian
Normal file
55
packaging/appimage/Containerfile.debian
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
FROM docker.io/library/node:22-bookworm
|
||||||
|
|
||||||
|
ARG RUST_TOOLCHAIN=1.85.0
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive \
|
||||||
|
RUSTUP_HOME=/opt/rust/rustup \
|
||||||
|
CARGO_HOME=/opt/rust/cargo \
|
||||||
|
PATH=/opt/rust/cargo/bin:${PATH} \
|
||||||
|
YARN_CACHE_FOLDER=/var/cache/yarn \
|
||||||
|
APPIMAGE_EXTRACT_AND_RUN=1
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
binutils \
|
||||||
|
build-essential \
|
||||||
|
ca-certificates \
|
||||||
|
clang \
|
||||||
|
cmake \
|
||||||
|
curl \
|
||||||
|
desktop-file-utils \
|
||||||
|
file \
|
||||||
|
git \
|
||||||
|
libayatana-appindicator3-dev \
|
||||||
|
libclang-dev \
|
||||||
|
libfuse2 \
|
||||||
|
libgtk-3-dev \
|
||||||
|
librsvg2-dev \
|
||||||
|
libssl-dev \
|
||||||
|
libwebkit2gtk-4.1-dev \
|
||||||
|
libxdo-dev \
|
||||||
|
patchelf \
|
||||||
|
pkg-config \
|
||||||
|
protobuf-compiler \
|
||||||
|
python3 \
|
||||||
|
squashfs-tools \
|
||||||
|
wget \
|
||||||
|
xz-utils \
|
||||||
|
zsync \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
|
||||||
|
| sh -s -- -y --profile minimal --default-toolchain "${RUST_TOOLCHAIN}" \
|
||||||
|
&& rustc --version \
|
||||||
|
&& cargo --version
|
||||||
|
|
||||||
|
RUN node --version \
|
||||||
|
&& yarn_version="$(yarn --version)" \
|
||||||
|
&& test "$yarn_version" = 1.22.22 \
|
||||||
|
&& echo "yarn $yarn_version"
|
||||||
|
|
||||||
|
COPY container-build-debian.sh /usr/local/bin/build-komp-ac-debian
|
||||||
|
RUN chmod 755 /usr/local/bin/build-komp-ac-debian
|
||||||
|
|
||||||
|
WORKDIR /work
|
||||||
|
ENTRYPOINT ["/usr/local/bin/build-komp-ac-debian"]
|
||||||
121
packaging/appimage/README.md
Normal file
121
packaging/appimage/README.md
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# Combined Linux AppImage
|
||||||
|
|
||||||
|
This packaging layer keeps `server` and `client-gui2` independently buildable
|
||||||
|
and combines their release artifacts into one `x86_64` Linux AppImage. The
|
||||||
|
AppImage entry point starts the selected server build, waits for its local gRPC
|
||||||
|
port, runs the GUI, and terminates the server when the GUI exits.
|
||||||
|
|
||||||
|
With the `full-embed` server feature, the PostgreSQL installation, database,
|
||||||
|
dumps, JWT secret and server log are persistent and are not stored in the
|
||||||
|
read-only AppImage. By default they live under
|
||||||
|
`${XDG_DATA_HOME:-$HOME/.local/share}/komp_ac`. Set `KOMP_AC_DATA_DIR` to
|
||||||
|
override that root. A `full` build instead uses the external PostgreSQL settings
|
||||||
|
supplied through `RUST_DB_*` environment variables. Both modes create fresh
|
||||||
|
runtime Tantivy indexes under the application-data root; existing repository
|
||||||
|
indexes are never packaged.
|
||||||
|
|
||||||
|
## Build both and combine
|
||||||
|
|
||||||
|
The frontend dependencies must already be present in `client-gui2/node_modules`.
|
||||||
|
The build also requires Nix and Tauri's Linux build dependencies. It uses
|
||||||
|
`appimagetool` from `APPIMAGETOOL` or `PATH` when available, otherwise it reuses
|
||||||
|
Tauri's cached `linuxdeploy-plugin-appimage.AppImage`. It does not fetch or
|
||||||
|
install frontend dependencies.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
packaging/appimage/build.sh ./KompAC.AppImage
|
||||||
|
```
|
||||||
|
|
||||||
|
This performs the existing builds independently:
|
||||||
|
|
||||||
|
1. `nix build '.?submodules=1#portable-server'`
|
||||||
|
2. `yarn tauri build --bundles appimage` in `client-gui2`
|
||||||
|
3. `assemble.sh` to add the portable server and supervisor to the GUI AppImage
|
||||||
|
|
||||||
|
The standalone server and GUI build processes remain available unchanged.
|
||||||
|
|
||||||
|
## Combine existing artifacts
|
||||||
|
|
||||||
|
Already-built artifacts can be combined without rebuilding either application.
|
||||||
|
The first argument may be either a completed GUI AppImage or Tauri's completed
|
||||||
|
`.AppDir` directory:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
packaging/appimage/assemble.sh \
|
||||||
|
client-gui2/src-tauri/target/release/bundle/appimage/komp_ac.AppDir \
|
||||||
|
result/bin/server \
|
||||||
|
embedded \
|
||||||
|
./KompAC.AppImage
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated AppImage uses local embedded mode and binds gRPC only to
|
||||||
|
`127.0.0.1:50051`. Only one combined AppImage instance may run against a data
|
||||||
|
directory at a time.
|
||||||
|
|
||||||
|
## Debian production build from NixOS
|
||||||
|
|
||||||
|
`build-debian.sh` uses rootless Podman to build inside a Debian 12 userspace.
|
||||||
|
The host source is mounted read-only and copied into the temporary container.
|
||||||
|
Cargo and Yarn caches use named Podman volumes, so neither Cargo target files
|
||||||
|
nor frontend dependencies are written into the repository. The source snapshot
|
||||||
|
excludes build outputs, local environment files and generated runtime data such
|
||||||
|
as Tantivy indexes, dumps and embedded PostgreSQL data.
|
||||||
|
|
||||||
|
The server feature is a required argument. Nothing implicitly chooses the
|
||||||
|
database implementation:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# One-click application with PostgreSQL embedded in the server binary
|
||||||
|
packaging/appimage/build-debian.sh full-embed
|
||||||
|
|
||||||
|
# GUI and server using a separately managed PostgreSQL instance
|
||||||
|
packaging/appimage/build-debian.sh full
|
||||||
|
```
|
||||||
|
|
||||||
|
The default embedded outputs are:
|
||||||
|
|
||||||
|
```text
|
||||||
|
debian-dist/KompAC-embedded-debian12-amd64.AppImage
|
||||||
|
debian-dist/bin/client-gui2
|
||||||
|
debian-dist/bin/server
|
||||||
|
```
|
||||||
|
|
||||||
|
`full-embed` runs `cargo build --release --package server --features
|
||||||
|
full-embed`; PostgreSQL 17.10.0 is embedded by `postgresql_embedded`. `full`
|
||||||
|
runs the same command with `--features full` and configures the AppImage launcher
|
||||||
|
for external database mode. OpenSSL is linked statically in both builds. The GUI
|
||||||
|
is built by Tauri inside Debian with its normal AppImage dependency collection.
|
||||||
|
|
||||||
|
An alternative output directory and filename may be supplied:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
packaging/appimage/build-debian.sh \
|
||||||
|
full-embed \
|
||||||
|
/path/to/releases \
|
||||||
|
KompAC-0.8.48-debian12-amd64.AppImage
|
||||||
|
```
|
||||||
|
|
||||||
|
An external build needs its database connection when launched:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
RUST_DB_USER=komp_ac \
|
||||||
|
RUST_DB_PASSWORD=secret \
|
||||||
|
RUST_DB_HOST=127.0.0.1 \
|
||||||
|
RUST_DB_PORT=5432 \
|
||||||
|
RUST_DB_NAME=komp_ac \
|
||||||
|
./debian-dist/KompAC-external-debian12-amd64.AppImage
|
||||||
|
```
|
||||||
|
|
||||||
|
The build requires network access for the Debian builder image, APT packages,
|
||||||
|
Rust crates, frontend packages, PostgreSQL's embedded archive and Tauri's
|
||||||
|
AppImage tooling. The named cache volumes are retained for later production
|
||||||
|
builds. They can be inspected with `podman volume ls`.
|
||||||
|
|
||||||
|
On NixOS, the host script uses `/etc/containers/containers.conf` when Podman's
|
||||||
|
vendor configuration below `/usr/share` is not readable. An explicitly supplied
|
||||||
|
`CONTAINERS_CONF` is always preserved. If `/usr/share/containers/seccomp.json`
|
||||||
|
is also unreadable, the script visibly passes `--security-opt
|
||||||
|
seccomp=unconfined` to the rootless build containers. Restore normal host
|
||||||
|
permissions with `sudo chmod 755 /usr/share` to retain seccomp isolation. The
|
||||||
|
same fallback disables automatic OCI hook discovery when its vendor directory
|
||||||
|
is unreadable; explicitly configured hook overrides are never replaced.
|
||||||
94
packaging/appimage/assemble.sh
Executable file
94
packaging/appimage/assemble.sh
Executable file
@@ -0,0 +1,94 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 GUI.AppImage-or-AppDir SERVER_BINARY SERVER_DB_MODE OUTPUT.AppImage" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $# -ne 4 ]]; then
|
||||||
|
usage
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
gui_bundle="$(realpath -- "$1")"
|
||||||
|
server_binary="$(realpath -- "$2")"
|
||||||
|
server_db_mode="$3"
|
||||||
|
output_parent="$(dirname -- "$4")"
|
||||||
|
mkdir -p "$output_parent"
|
||||||
|
output="$(cd -- "$output_parent" && pwd)/$(basename -- "$4")"
|
||||||
|
|
||||||
|
if [[ "$server_db_mode" != embedded && "$server_db_mode" != external ]]; then
|
||||||
|
echo "SERVER_DB_MODE must be embedded or external, got: $server_db_mode" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "$gui_bundle" && ! -x "$gui_bundle" ]]; then
|
||||||
|
echo "GUI bundle is neither an AppDir nor an executable AppImage: $gui_bundle" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ! -x "$server_binary" ]]; then
|
||||||
|
echo "Server binary is not executable: $server_binary" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
work_dir="$(mktemp -d /tmp/komp-ac-appimage.XXXXXX)"
|
||||||
|
cleanup() {
|
||||||
|
case "$work_dir" in
|
||||||
|
/tmp/komp-ac-appimage.*)
|
||||||
|
rm -rf -- "$work_dir"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Refusing to remove unexpected temporary directory: $work_dir" >&2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM HUP
|
||||||
|
|
||||||
|
app_dir="$work_dir/squashfs-root"
|
||||||
|
if [[ -d "$gui_bundle" ]]; then
|
||||||
|
mkdir "$app_dir"
|
||||||
|
cp -a -- "$gui_bundle/." "$app_dir/"
|
||||||
|
else
|
||||||
|
(
|
||||||
|
cd -- "$work_dir"
|
||||||
|
"$gui_bundle" --appimage-extract >/dev/null
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -e "$app_dir/AppRun" ]]; then
|
||||||
|
echo "The GUI AppImage did not contain an AppRun entry point" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv -- "$app_dir/AppRun" "$app_dir/AppRun.gui"
|
||||||
|
install -Dm755 "$script_dir/AppRun" "$app_dir/AppRun"
|
||||||
|
install -Dm755 "$server_binary" "$app_dir/usr/bin/komp-ac-server"
|
||||||
|
install -d "$app_dir/usr/share/komp-ac"
|
||||||
|
printf '%s\n' "$server_db_mode" > "$app_dir/usr/share/komp-ac/server-db-mode"
|
||||||
|
|
||||||
|
# Tauri names the root icon after productName (`komp_ac.png`) while its desktop
|
||||||
|
# entry names the Rust binary (`Icon=client-gui2`). AppImage packers require the
|
||||||
|
# desktop icon name to resolve at the AppDir root.
|
||||||
|
if [[ -f "$app_dir/komp_ac.png" && ! -e "$app_dir/client-gui2.png" ]]; then
|
||||||
|
ln -s komp_ac.png "$app_dir/client-gui2.png"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${APPIMAGETOOL:-}" ]]; then
|
||||||
|
ARCH=x86_64 "$APPIMAGETOOL" "$app_dir" "$output"
|
||||||
|
elif command -v appimagetool >/dev/null 2>&1; then
|
||||||
|
ARCH=x86_64 appimagetool "$app_dir" "$output"
|
||||||
|
else
|
||||||
|
cache_home="${XDG_CACHE_HOME:-${HOME:?HOME is not set}/.cache}"
|
||||||
|
appimage_plugin="$cache_home/tauri/linuxdeploy-plugin-appimage.AppImage"
|
||||||
|
if [[ ! -x "$appimage_plugin" ]]; then
|
||||||
|
echo "No AppImage packer found; set APPIMAGETOOL to appimagetool's executable path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
ARCH=x86_64 \
|
||||||
|
LDAI_NO_APPSTREAM=1 \
|
||||||
|
LDAI_OUTPUT="$output" \
|
||||||
|
"$appimage_plugin" --appimage-extract-and-run --appdir="$app_dir"
|
||||||
|
fi
|
||||||
|
echo "Created $output"
|
||||||
102
packaging/appimage/build-debian.sh
Executable file
102
packaging/appimage/build-debian.sh
Executable file
@@ -0,0 +1,102 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $# -lt 1 || $# -gt 3 ]]; then
|
||||||
|
echo "Usage: $0 SERVER_FEATURE [OUTPUT_DIRECTORY] [OUTPUT_FILENAME]" >&2
|
||||||
|
echo "SERVER_FEATURE must be full-embed or full" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
repo_root="$(cd -- "$script_dir/../.." && pwd)"
|
||||||
|
server_feature="$1"
|
||||||
|
output_directory="${2:-$repo_root/debian-dist}"
|
||||||
|
|
||||||
|
case "$server_feature" in
|
||||||
|
full-embed)
|
||||||
|
server_db_mode=embedded
|
||||||
|
default_output_name=KompAC-embedded-debian12-amd64.AppImage
|
||||||
|
;;
|
||||||
|
full)
|
||||||
|
server_db_mode=external
|
||||||
|
default_output_name=KompAC-external-debian12-amd64.AppImage
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "SERVER_FEATURE must be full-embed or full, got: $server_feature" >&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
output_name="${3:-$default_output_name}"
|
||||||
|
|
||||||
|
if [[ "$output_name" == */* || "$output_name" != *.AppImage ]]; then
|
||||||
|
echo "OUTPUT_FILENAME must be a portable filename ending in .AppImage" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if ! command -v podman >/dev/null 2>&1; then
|
||||||
|
echo "podman is required to build the Debian production AppImage" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Podman normally reads a vendor configuration from /usr/share before the
|
||||||
|
# NixOS-generated configuration in /etc. Some NixOS installations have no
|
||||||
|
# traversable /usr/share, so point Podman directly at the generated file.
|
||||||
|
if [[ -z "${CONTAINERS_CONF:-}" ]] \
|
||||||
|
&& [[ ! -r /usr/share/containers/containers.conf ]] \
|
||||||
|
&& [[ -r /etc/containers/containers.conf ]]; then
|
||||||
|
export CONTAINERS_CONF=/etc/containers/containers.conf
|
||||||
|
echo "Using NixOS Podman configuration: $CONTAINERS_CONF"
|
||||||
|
fi
|
||||||
|
|
||||||
|
podman_security_options=()
|
||||||
|
if [[ ! -r /usr/share/containers/seccomp.json ]]; then
|
||||||
|
podman_security_options=(--security-opt seccomp=unconfined)
|
||||||
|
echo "Warning: Podman's seccomp profile is unreadable; build-container seccomp is disabled"
|
||||||
|
echo "Host fix: sudo chmod 755 /usr/share"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -r /usr/share/containers/oci/hooks.d ]]; then
|
||||||
|
if [[ -n "${CONTAINERS_CONF_OVERRIDE:-}" ]]; then
|
||||||
|
echo "Podman's OCI hooks directory is unreadable and CONTAINERS_CONF_OVERRIDE is already set" >&2
|
||||||
|
echo "Fix the host with: sudo chmod 755 /usr/share" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
export CONTAINERS_CONF_OVERRIDE="$script_dir/containers-nix-unreadable-usr-share.conf"
|
||||||
|
echo "Disabling unreadable vendor OCI hooks with: $CONTAINERS_CONF_OVERRIDE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$output_directory"
|
||||||
|
output_directory="$(cd -- "$output_directory" && pwd)"
|
||||||
|
image=localhost/komp-ac-debian-builder:bookworm
|
||||||
|
|
||||||
|
echo "Building Debian 12 production artifacts"
|
||||||
|
echo " server feature: $server_feature"
|
||||||
|
echo " database mode: $server_db_mode"
|
||||||
|
echo " output: $output_directory/$output_name"
|
||||||
|
echo
|
||||||
|
echo "+ podman build ${podman_security_options[*]} --platform linux/amd64 --file $script_dir/Containerfile.debian --tag $image $script_dir"
|
||||||
|
podman build \
|
||||||
|
"${podman_security_options[@]}" \
|
||||||
|
--platform linux/amd64 \
|
||||||
|
--file "$script_dir/Containerfile.debian" \
|
||||||
|
--tag "$image" \
|
||||||
|
"$script_dir"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "+ podman run ${podman_security_options[*]} ... SERVER_FEATURE=$server_feature SERVER_DB_MODE=$server_db_mode ... $image"
|
||||||
|
podman run --rm \
|
||||||
|
"${podman_security_options[@]}" \
|
||||||
|
--platform linux/amd64 \
|
||||||
|
--env "OUTPUT_NAME=$output_name" \
|
||||||
|
--env "SERVER_FEATURE=$server_feature" \
|
||||||
|
--env "SERVER_DB_MODE=$server_db_mode" \
|
||||||
|
--volume "$repo_root:/source:ro" \
|
||||||
|
--volume "$output_directory:/out" \
|
||||||
|
--volume komp-ac-debian-cargo-target:/cargo-target \
|
||||||
|
--volume komp-ac-debian-cargo-registry:/opt/rust/cargo/registry \
|
||||||
|
--volume komp-ac-debian-cargo-git:/opt/rust/cargo/git \
|
||||||
|
--volume komp-ac-debian-yarn-cache:/var/cache/yarn \
|
||||||
|
"$image"
|
||||||
|
|
||||||
|
echo "Created $output_directory/$output_name"
|
||||||
73
packaging/appimage/build.sh
Executable file
73
packaging/appimage/build.sh
Executable file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $# -gt 1 ]]; then
|
||||||
|
echo "Usage: $0 [OUTPUT.AppImage]" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
repo_root="$(cd -- "$script_dir/../.." && pwd)"
|
||||||
|
output="${1:-$repo_root/KompAC.AppImage}"
|
||||||
|
work_dir="$(mktemp -d /tmp/komp-ac-appimage-build.XXXXXX)"
|
||||||
|
cleanup() {
|
||||||
|
case "$work_dir" in
|
||||||
|
/tmp/komp-ac-appimage-build.*)
|
||||||
|
rm -rf -- "$work_dir"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Refusing to remove unexpected temporary directory: $work_dir" >&2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM HUP
|
||||||
|
|
||||||
|
(
|
||||||
|
cd -- "$repo_root"
|
||||||
|
nix build '.?submodules=1#portable-server' --out-link "$work_dir/portable-server"
|
||||||
|
)
|
||||||
|
|
||||||
|
set +e
|
||||||
|
(
|
||||||
|
cd -- "$repo_root/client-gui2"
|
||||||
|
yarn tauri build --bundles appimage
|
||||||
|
)
|
||||||
|
tauri_status=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
shopt -s nullglob
|
||||||
|
gui_appimages=("$repo_root"/client-gui2/src-tauri/target/release/bundle/appimage/*.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="$repo_root/client-gui2/src-tauri/target/release/bundle/appimage/komp_ac.AppDir"
|
||||||
|
if [[ -d "$gui_appdir" ]] && \
|
||||||
|
cmp -s \
|
||||||
|
"$repo_root/client-gui2/src-tauri/target/release/client-gui2" \
|
||||||
|
"$gui_appdir/usr/bin/client-gui2"; then
|
||||||
|
gui_bundle="$gui_appdir"
|
||||||
|
echo "Tauri did not emit a current GUI AppImage; using its completed AppDir"
|
||||||
|
else
|
||||||
|
echo "The Tauri build 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 release GUI and its AppDir were completed before linuxdeploy failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$script_dir/assemble.sh" \
|
||||||
|
"$gui_bundle" \
|
||||||
|
"$work_dir/portable-server/bin/server" \
|
||||||
|
embedded \
|
||||||
|
"$output"
|
||||||
136
packaging/appimage/container-build-debian.sh
Executable file
136
packaging/appimage/container-build-debian.sh
Executable file
@@ -0,0 +1,136 @@
|
|||||||
|
#!/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='*/tantivy_indexes' \
|
||||||
|
--exclude='*/dumps' \
|
||||||
|
--exclude='*/.postgres-data' \
|
||||||
|
--exclude='*/benchmark-results' \
|
||||||
|
--exclude='*/.mutants-run' \
|
||||||
|
--exclude='*/__pycache__' \
|
||||||
|
--exclude='./client-gui2/node_modules' \
|
||||||
|
--exclude='./client-gui2/dist' \
|
||||||
|
--exclude='./debian-dist' \
|
||||||
|
--exclude='./result' \
|
||||||
|
-C /source -cf - . \
|
||||||
|
| tar -C "$source_root" -xf -
|
||||||
|
|
||||||
|
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"
|
||||||
|
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=(/cargo-target/release/bundle/appimage/*.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=/cargo-target/release/bundle/appimage/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"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[engine]
|
||||||
|
hooks_dir = []
|
||||||
2
server
2
server
Submodule server updated: 8837ddbcbd...590dea3135
Reference in New Issue
Block a user