Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ jobs:
scripts/test-mobile-release-candidate-publisher.sh
- name: Mobile worktree identity contract
run: scripts/test-mobile-worktree-overrides.sh
- name: Tauri Linux deps guard contract
run: scripts/test-check-tauri-linux-deps.sh
- name: File size ratchet unit tests
run: node --test scripts/check-file-sizes-core.test.mjs

Expand Down Expand Up @@ -167,11 +169,14 @@ jobs:
libasound2-dev \
libayatana-appindicator3-dev \
libgtk-3-dev \
libjavascriptcoregtk-4.1-dev \
librsvg2-dev \
libssl-dev \
libsoup-3.0-dev \
libwebkit2gtk-4.1-dev \
libxdo-dev \
patchelf \
pkg-config \
wget
- name: Get pnpm store directory
id: pnpm-cache
Expand Down
15 changes: 8 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,15 @@ versions in the table above.
#### Linux: Tauri system libraries

Hermit pins language toolchains, not system libraries. On Linux, the desktop
app's Rust crates link against GTK and WebKitGTK, so `just ci` (and any
`just desktop-tauri-*` recipe) needs these installed system-wide first. On
Debian/Ubuntu:
app's Rust crates link against GTK and WebKitGTK, so `just dev`, `just ci`
(and any `just desktop-tauri-*` recipe) needs these installed system-wide
first. On Debian/Ubuntu:

```bash
sudo apt-get install -y --no-install-recommends \
build-essential curl file libasound2-dev libayatana-appindicator3-dev \
libgtk-3-dev librsvg2-dev libssl-dev libwebkit2gtk-4.1-dev libxdo-dev \
patchelf wget
libgtk-3-dev libjavascriptcoregtk-4.1-dev librsvg2-dev libssl-dev \
libsoup-3.0-dev libwebkit2gtk-4.1-dev libxdo-dev patchelf pkg-config wget
```

This is the same list CI installs (see `.github/workflows/ci.yml`), so matching
Expand All @@ -118,8 +118,9 @@ under different package names — see the
[Tauri prerequisites](https://tauri.app/start/prerequisites/) for the
equivalents.

Without them, `just ci` fails partway through `just check` with a pkg-config
error such as:
`just dev` checks for these libraries up front and fails fast with the
install line above. Without the check, the missing libraries surface partway
through a cargo build as a pkg-config error such as:

```
The system library `gdk-pixbuf-2.0` required by crate `gdk-pixbuf-sys` was not found.
Expand Down
8 changes: 7 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,14 @@ relay-release: _ensure-migrations
cargo run -p buzz-relay --release


# Fail fast on Linux when the Tauri desktop app's native system libraries are
# missing (no-op on other platforms); see CONTRIBUTING.md "Linux: Tauri system
# libraries". Runs as the first dev prerequisite, before bootstrap builds.
_check-tauri-linux-deps:
./scripts/check-tauri-linux-deps.sh

# Run the desktop Tauri app in dev mode with a local relay (ports and identity derived from worktree)
dev *ARGS: bootstrap _ensure-sidecar-stubs _ensure-migrations
dev *ARGS: _check-tauri-linux-deps bootstrap _ensure-sidecar-stubs _ensure-migrations
#!/usr/bin/env bash
set -euo pipefail
export PATH="{{justfile_directory()}}/bin:$PATH"
Expand Down
58 changes: 58 additions & 0 deletions scripts/check-tauri-linux-deps.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
# Fail fast on Linux when the Tauri desktop app's native system dependencies
# are missing, so `just dev` does not die deep inside cargo with a cryptic
# pkg-config error. No-op on other platforms. This script never installs
# anything and never calls sudo; it only reports what is missing.
set -euo pipefail

case "${BUZZ_TEST_PLATFORM:-$(uname -s)}" in
Linux) ;;
*) exit 0 ;;
esac

remediate() {
{
echo "Error: $1"
echo
echo "just dev builds the Tauri desktop app, which links against GTK and"
echo "WebKitGTK system libraries on Linux. Install them (Debian/Ubuntu):"
echo
echo " sudo apt-get install -y --no-install-recommends \\"
echo " build-essential curl file libasound2-dev libayatana-appindicator3-dev \\"
echo " libgtk-3-dev libjavascriptcoregtk-4.1-dev librsvg2-dev libssl-dev \\"
echo " libsoup-3.0-dev libwebkit2gtk-4.1-dev libxdo-dev patchelf pkg-config wget"
echo
echo "Other distributions ship these under different names — see"
echo "CONTRIBUTING.md, \"Linux: Tauri system libraries\", and"
echo "https://tauri.app/start/prerequisites/."
} >&2
exit 1
}

if ! command -v pkg-config >/dev/null 2>&1; then
remediate "pkg-config is not installed, so the Tauri desktop build cannot locate its native system libraries."
fi

# pkg-config modules that just dev needs, mapped from the -dev packages
# documented in CONTRIBUTING.md (the same list CI installs).
modules=(
alsa
gtk+-3.0
libayatana-appindicator3-0.1
librsvg-2.0
libsoup-3.0
openssl
webkit2gtk-4.1
xdo
)

missing=()
for module in "${modules[@]}"; do
if ! pkg-config --exists "$module"; then
missing+=("$module")
fi
done

if ((${#missing[@]} > 0)); then
remediate "missing Tauri native dependencies (pkg-config modules): ${missing[*]}"
fi
117 changes: 117 additions & 0 deletions scripts/test-check-tauri-linux-deps.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/usr/bin/env bash
# Contract test for scripts/check-tauri-linux-deps.sh.
set -euo pipefail

repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
guard="$repo_root/scripts/check-tauri-linux-deps.sh"
# The mocked pkg-config must be executable, but /tmp is mounted noexec on some
# environments, so default the scratch dir to the repository root and clean it
# up on exit. BUZZ_TEST_TMPDIR overrides the parent directory when needed.
tmp=$(mktemp -d "${BUZZ_TEST_TMPDIR:-$repo_root}/.check-tauri-linux-deps-test.XXXXXX")
trap 'rm -rf "$tmp"' EXIT

# The guard runs under `#!/usr/bin/env bash`; give each case an isolated PATH
# containing only bash plus whatever stub the case installs, so the host's
# real pkg-config can never leak in.
mock_bin="$tmp/bin"
mkdir -p "$mock_bin"
ln -s "$(command -v bash)" "$mock_bin/bash"

expect_fail() {
local name="$1"
shift
if out=$("$@" 2>&1); then
echo "expected $name to fail, but it exited 0" >&2
exit 1
fi
printf '%s' "$out"
}

# 1. Non-Linux platforms are a no-op, even with no pkg-config in PATH: the
# guard must exit 0 and emit no output. Capture status and output
# explicitly so a crashing or noisy guard fails the test.
if out=$(BUZZ_TEST_PLATFORM=Darwin PATH="$mock_bin" "$guard" 2>&1); then
status=0
else
status=$?
fi
if [[ "$status" -ne 0 ]]; then
echo "expected exit 0 on non-Linux, got exit $status" >&2
exit 1
fi
if [[ -n "$out" ]]; then
echo "expected no output on non-Linux, got:" >&2
printf '%s\n' "$out" >&2
exit 1
fi

# 2. Linux without pkg-config fails fast, names the missing command, and
# points at the documented apt install line.
out=$(expect_fail "missing pkg-config" env BUZZ_TEST_PLATFORM=Linux PATH="$mock_bin" "$guard")
grep -F "pkg-config" <<<"$out" >/dev/null
grep -F "apt-get install" <<<"$out" >/dev/null
grep -F "CONTRIBUTING.md" <<<"$out" >/dev/null

# Check the package tokens inside the printed apt command, rather than merely
# accepting a mention elsewhere in the diagnostic (for example, the missing
# command error itself).
install_command=$(sed -n '/^[[:space:]]*sudo apt-get install /,/^[[:space:]]*$/p' <<<"$out")
if [[ -z "$install_command" ]]; then
echo "expected a non-empty apt install command in the remediation" >&2
exit 1
fi
for package in \
pkg-config \
libjavascriptcoregtk-4.1-dev \
libsoup-3.0-dev \
libwebkit2gtk-4.1-dev; do
if ! grep -F " $package" <<<"$install_command" >/dev/null; then
echo "expected apt install command to contain package '$package':" >&2
printf '%s\n' "$install_command" >&2
exit 1
fi
done

# 3. Linux with pkg-config but one missing module fails and names the module.
cat > "$mock_bin/pkg-config" <<'MOCK'
#!/usr/bin/env bash
for arg in "$@"; do
if [[ "$arg" == "webkit2gtk-4.1" ]]; then
exit 1
fi
done
exit 0
MOCK
chmod +x "$mock_bin/pkg-config"
out=$(expect_fail "missing webkit2gtk-4.1" env BUZZ_TEST_PLATFORM=Linux PATH="$mock_bin" "$guard")
grep -F "webkit2gtk-4.1" <<<"$out" >/dev/null
grep -F "apt-get install" <<<"$out" >/dev/null
grep -F "CONTRIBUTING.md" <<<"$out" >/dev/null
# A module that exists must not be reported as missing.
if grep -F "gtk+-3.0" <<<"$out" >/dev/null; then
echo "gtk+-3.0 was reported missing but the stub resolves it" >&2
exit 1
fi

# 4. Multiple missing modules are all reported in one run.
cat > "$mock_bin/pkg-config" <<'MOCK'
#!/usr/bin/env bash
for arg in "$@"; do
case "$arg" in
webkit2gtk-4.1|libsoup-3.0) exit 1 ;;
esac
done
exit 0
MOCK
out=$(expect_fail "missing two modules" env BUZZ_TEST_PLATFORM=Linux PATH="$mock_bin" "$guard")
grep -F "webkit2gtk-4.1" <<<"$out" >/dev/null
grep -F "libsoup-3.0" <<<"$out" >/dev/null

# 5. Linux with all modules present passes silently.
cat > "$mock_bin/pkg-config" <<'MOCK'
#!/usr/bin/env bash
exit 0
MOCK
BUZZ_TEST_PLATFORM=Linux PATH="$mock_bin" "$guard"

echo "tauri linux deps guard test passed"