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