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
85 changes: 85 additions & 0 deletions .github/workflows/tauri-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,91 @@ jobs:
retention-days: 1
include-hidden-files: true

linux-wry:
name: Build Linux x86_64 (wry)
needs: setup-release
runs-on: ubuntu-22.04
timeout-minutes: 90
permissions:
contents: write
id-token: write
attestations: write
artifact-metadata: write
env:
ARCH: x86_64
TAG: ${{ needs.setup-release.outputs.tag }}
VERSION: ${{ needs.setup-release.outputs.version }}
VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }}
SABLE_BUILD_FLAVOR: ${{ needs.setup-release.outputs.nightly == 'true' && 'dev' || 'stable' }}
VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }}
VITE_SENTRY_ENVIRONMENT: ${{ needs.setup-release.outputs.nightly == 'true' && 'preview' || 'production' }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
IS_NIGHTLY: ${{ needs.setup-release.outputs.nightly }}
MISE_ENV: tauri
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.setup-release.outputs.ref }}
persist-credentials: false

- name: Setup app
uses: ./.github/actions/setup
with:
tauri: 'true'

- name: Cache Rust build
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
workspaces: src-tauri
key: wry-x86_64

- name: Stamp release version into tauri.conf.json
shell: bash
env:
UPDATER_ENDPOINT: ${{ needs.setup-release.outputs.nightly == 'true' && format('https://github.com/{0}/releases/download/nightly/latest.json', github.repository) || '' }}
run: node .github/scripts/set-tauri-version.mjs "$VERSION" "$UPDATER_ENDPOINT"

- name: Build and package Linux bundles
shell: bash
run: |
pnpm tauri:wry build --no-bundle
! strings src-tauri/target/release/sable | grep -F 'http://localhost:8080'
cp src-tauri/target/release/sable src-tauri/target/release/sable-updater
pnpm tauri:wry build --no-bundle --no-updater

DISPLAY_NAME=Sable
[ "$IS_NIGHTLY" = "true" ] && DISPLAY_NAME="Sable Nightly"
mise run webkit:package "$VERSION" \
src-tauri/target/release/sable "$DISPLAY_NAME" \
src-tauri/target/release/sable-updater

test -f src-tauri/target/release/webkit-pkg/stage/runtime/webkit/WEBKITGTK-LICENSE.txt

- name: Sign the AppImage for the updater
if: ${{ env.TAURI_SIGNING_PRIVATE_KEY != '' }}
shell: bash
run: |
for f in src-tauri/target/release/bundle/appimage/Sable-*-linux-${ARCH}.AppImage; do
[ -e "$f" ] || continue
pnpm tauri signer sign "$f"
done

- name: Upload wry bundles as workflow artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: sable-linux-wry-${{ env.ARCH }}
path: |
src-tauri/target/release/bundle/deb/Sable-*.deb
src-tauri/target/release/bundle/rpm/Sable-*.rpm
src-tauri/target/release/bundle/appimage/Sable-*.AppImage
if-no-files-found: error
retention-days: 7
include-hidden-files: true

distribute-nightly-aur:
name: Publish nightly to AUR
needs: [setup-release, linux]
Expand Down
91 changes: 91 additions & 0 deletions nfpm.webkit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# yaml-language-server: $schema=https://nfpm.goreleaser.com/schema.json
# wry flavour. The embedded WebKit runtime carries its own private libraries
# (ICU, libsoup3, GStreamer, bwrap), so the only dependencies left are the ones
# the app and the engine must share a single instance of.

name: sable
arch: ${PKG_ARCH}
platform: linux
version: ${PKG_VERSION}
release: ${PKG_RELEASE}
version_schema: semver
section: net
maintainer: SableClient
description: Sable, a Matrix client
homepage: https://sable.moe

contents:
- src: ${PKGROOT}/opt/sable/
dst: /opt/sable
type: tree
expand: true
- src: ${PKGROOT}/usr/
dst: /usr
type: tree
expand: true
- src: src-tauri/packaging/deb/apparmor/sable
dst: /etc/apparmor.d/sable
type: config
packager: deb

overrides:
deb:
scripts:
postinstall: src-tauri/packaging/deb/postinst
preremove: src-tauri/packaging/deb/prerm
depends:
- apparmor
- libgtk-3-0
- libglib2.0-0
- libcairo2
- libpango-1.0-0
- libgdk-pixbuf-2.0-0
- libatk1.0-0
- libharfbuzz0b
- libfreetype6
- libfontconfig1
- libx11-6
- libgl1
- libegl1
- libxkbcommon0
- libxtst6
- libxss1
- libatomic1
- libharfbuzz-icu0
- libnghttp2-14
- librsvg2-2
- libpsl5
- libtasn1-6
- libwayland-client0
- libwayland-egl1
- libxcb1
- xdg-utils
- libayatana-appindicator3-1
rpm:
depends:
- gtk3
- glib2
- cairo
- pango
- gdk-pixbuf2
- atk
- harfbuzz
- freetype
- fontconfig
- libX11
- mesa-libGL
- mesa-libEGL
- libxkbcommon
- libXtst
- libXScrnSaver
- libatomic
- harfbuzz-icu
- libnghttp2
- librsvg2
- libpsl
- libtasn1
- libwayland-client
- libwayland-egl
- libxcb
- xdg-utils
- libayatana-appindicator3.so.1()(64bit)
53 changes: 53 additions & 0 deletions scripts/webkit/copy-libs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
#MISE description="Copy the WebRTC WebKitGTK runtime next to the built binary"
# Copy the portable WebKitGTK runtime next to a built binary so wry builds get
# WebRTC regardless of the host's webkit2gtk. The bundle carries its own rpaths
# and private dependencies.
#
# Usage: scripts/webkit/copy-libs.sh [debug|release] [dest-dir]
# dest-dir defaults to src-tauri/target/<profile> (beside the built binary).
set -euo pipefail
PROFILE="${1:-debug}"
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
DEST="${2:-$ROOT/src-tauri/target/$PROFILE}"

if [ "$(uname -m)" != x86_64 ]; then
echo "❌ the WebKit runtime is only built for x86_64 (got $(uname -m))." >&2
exit 1
fi

SRC="${SABLE_WEBKIT_RUNTIME_DIR:-}"
if [ -z "$SRC" ]; then
CACHE="$ROOT/src-tauri/target/webkit-runtime"
bash "$ROOT/scripts/webkit/fetch.sh" "$CACHE"
SRC="$CACHE/runtime"
fi

WEBKIT_LIB="$SRC/libwebkit2gtk-4.1.so.0"
[ -f "$WEBKIT_LIB" ] || {
echo "❌ libwebkit2gtk-4.1.so.0 not found in $SRC." >&2
exit 1
}

# Without the patch the library silently forks the host's helpers instead.
# Process substitution, not a pipe: grep -q exits early and SIGPIPEs strings,
# which trips pipefail.
if ! grep -qx WEBKIT_EXEC_PATH < <(strings -a "$WEBKIT_LIB"); then
echo "❌ $WEBKIT_LIB was built without relocatable-exec-path.patch." >&2
echo " Re-pin scripts/webkit/runtime.json at a patched build." >&2
exit 1
fi

NEED="$(node -e 'process.stdout.write(require(process.argv[1]).glibc_floor)' \
"$SRC/runtime.json" 2>/dev/null || echo 0)"
HAVE="$(ldd --version | sed -n '1s/.*[^0-9]\([0-9]\+\.[0-9]\+\)$/\1/p')"
if [ "$(printf '%s\n%s\n' "$NEED" "$HAVE" | sort -V | tail -1)" != "$HAVE" ]; then
echo "❌ runtime needs glibc $NEED, this host has $HAVE." >&2
exit 1
fi

rm -rf "$DEST/webkit"
mkdir -p "$DEST"
cp -a "$SRC" "$DEST/webkit"

echo "✅ WebKit runtime staged in $DEST/webkit (glibc floor $NEED)"
49 changes: 49 additions & 0 deletions scripts/webkit/fetch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
#MISE description="Download the WebRTC-enabled WebKitGTK runtime"
# Downloads the pinned webkitgtk-webrtc portable artifact into a cache dir.
#
# Usage: scripts/webkit/fetch.sh [cache-dir]
# cache-dir defaults to src-tauri/target/webkit-runtime.
#
# Override the source with SABLE_WEBKIT_ARTIFACT_URL, or point
# SABLE_WEBKIT_RUNTIME_DIR at an already-unpacked tree to skip this entirely.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
CACHE="${1:-$ROOT/src-tauri/target/webkit-runtime}"
MANIFEST="$ROOT/scripts/webkit/runtime.json"

read -r REPO RUN ARTIFACT VERSION < <(
node -e '
const m = require(process.argv[1]);
process.stdout.write([m.repository, m.run, m.artifact, m.version].join(" ") + "\n");
' "$MANIFEST"
)

URL="${SABLE_WEBKIT_ARTIFACT_URL:-$REPO/actions/runs/$RUN/artifacts/$ARTIFACT}"
STAMP="$CACHE/.stamp"
WANT="$URL $VERSION"

if [ -f "$STAMP" ] && [ "$(cat "$STAMP")" = "$WANT" ]; then
echo "→ WebKit runtime already cached in $CACHE"
exit 0
fi

WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT

echo "→ downloading $URL"
curl -fL --retry 3 --progress-bar -o "$WORK/artifact.zip" "$URL"
unzip -q -j "$WORK/artifact.zip" -d "$WORK"

TARBALL="$(find "$WORK" -maxdepth 1 -name '*-portable-*.tar.zst' -print -quit)"
[ -n "$TARBALL" ] || {
echo "❌ no portable runtime tarball in the artifact." >&2
exit 1
}

rm -rf "$CACHE"
mkdir -p "$CACHE"
tar --zstd -xf "$TARBALL" -C "$CACHE"

echo "$WANT" > "$STAMP"
echo "✅ WebKit runtime $VERSION unpacked into $CACHE"
Loading
Loading