mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 04:22:31 +00:00
+56
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
upstream_plugin="$script_dir/linuxdeploy-plugin-gtk-upstream.sh"
|
||||
[[ -x "$upstream_plugin" ]] || {
|
||||
echo "Missing executable upstream GTK plugin: $upstream_plugin" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# linuxdeploy queries this before invoking the plugin. Preserve the upstream
|
||||
# protocol response without requiring an AppDir.
|
||||
for argument in "$@"; do
|
||||
case "$argument" in
|
||||
--plugin-api-version | --help)
|
||||
exec "$upstream_plugin" "$@"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
appdir=''
|
||||
arguments=("$@")
|
||||
for ((index = 0; index < ${#arguments[@]}; index += 1)); do
|
||||
case "${arguments[index]}" in
|
||||
--appdir)
|
||||
index=$((index + 1))
|
||||
appdir=${arguments[index]:-}
|
||||
;;
|
||||
--appdir=*)
|
||||
appdir=${arguments[index]#--appdir=}
|
||||
;;
|
||||
esac
|
||||
done
|
||||
[[ -n "$appdir" ]] || {
|
||||
echo "GTK plugin wrapper did not receive --appdir" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
"$upstream_plugin" "$@"
|
||||
|
||||
# AppImages must use the target system's display stack. Bundling Ubuntu's
|
||||
# Wayland/XKB/XCB libraries ahead of a newer host Mesa stack can abort before
|
||||
# GTK creates a window (tauri-apps/tauri#15976).
|
||||
display_library_patterns=(
|
||||
'libwayland-*.so*'
|
||||
'libxkbcommon.so*'
|
||||
'libxcb-randr.so*'
|
||||
'libxcb-render.so*'
|
||||
'libxcb-shm.so*'
|
||||
'libXau.so*'
|
||||
'libXdmcp.so*'
|
||||
)
|
||||
|
||||
for pattern in "${display_library_patterns[@]}"; do
|
||||
find "$appdir/usr" \( -type f -o -type l \) -name "$pattern" -print -delete
|
||||
done
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
die() {
|
||||
echo "AppImage bundler setup failed: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
die "usage: $0 <Linux target triple>"
|
||||
fi
|
||||
|
||||
: "${XDG_CACHE_HOME:?XDG_CACHE_HOME must point to the isolated release cache}"
|
||||
|
||||
target=$1
|
||||
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
case "$target" in
|
||||
x86_64-unknown-linux-gnu)
|
||||
tools_arch=x86_64
|
||||
linuxdeploy_sha256=36a2d7e274d12e1050d0e9ecfe11d339ed54720b2bec464c286d53f8b07f5c62
|
||||
;;
|
||||
aarch64-unknown-linux-gnu)
|
||||
tools_arch=aarch64
|
||||
linuxdeploy_sha256=556ab80baa98e600aa80f0dcedfb70bca0e1ce7e9f147fb345be3fcc3e91b2b1
|
||||
;;
|
||||
*)
|
||||
die "unsupported Linux target: $target"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Tauri CLI 2.11.4 only downloads these tools when their cache entries are
|
||||
# absent, so an older runner cache can keep generating an AppRun hook that
|
||||
# unconditionally forces X11. Pin the tools from the accepted upstream fix
|
||||
# until a stable Tauri CLI release includes tauri-apps/tauri#16062.
|
||||
tauri_fix_commit=8e7028331ad37ac2db74d4ec20e66be5cacf2c40
|
||||
linuxdeploy_revision=07333c6
|
||||
gtk_plugin_sha256=ef6b9a980417243bc62e0241b51dc49876032afd1bab9b4762389f961b406d9b
|
||||
tools_dir="$XDG_CACHE_HOME/tauri"
|
||||
mkdir -p "$tools_dir"
|
||||
|
||||
download_path=''
|
||||
cleanup() {
|
||||
if [[ -n "$download_path" ]]; then
|
||||
rm -f -- "$download_path"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
install_verified_tool() {
|
||||
local name=$1
|
||||
local url=$2
|
||||
local expected_sha256=$3
|
||||
local destination="$tools_dir/$name"
|
||||
local actual_sha256
|
||||
|
||||
if [[ -f "$destination" ]]; then
|
||||
actual_sha256=$(sha256sum "$destination")
|
||||
actual_sha256=${actual_sha256%% *}
|
||||
if [[ "$actual_sha256" == "$expected_sha256" ]]; then
|
||||
chmod 0755 "$destination"
|
||||
echo "Using verified cached AppImage tool: $name"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
download_path=$(mktemp "$tools_dir/.${name}.XXXXXX")
|
||||
curl --fail --location --retry 3 --retry-all-errors --silent --show-error \
|
||||
"$url" --output "$download_path"
|
||||
actual_sha256=$(sha256sum "$download_path")
|
||||
actual_sha256=${actual_sha256%% *}
|
||||
[[ "$actual_sha256" == "$expected_sha256" ]] \
|
||||
|| die "$name SHA-256 mismatch: expected $expected_sha256, got $actual_sha256"
|
||||
chmod 0755 "$download_path"
|
||||
mv -f -- "$download_path" "$destination"
|
||||
download_path=''
|
||||
echo "Installed verified AppImage tool: $name"
|
||||
}
|
||||
|
||||
linuxdeploy_name="linuxdeploy-${tools_arch}.AppImage"
|
||||
install_verified_tool \
|
||||
"$linuxdeploy_name" \
|
||||
"https://github.com/tauri-apps/binary-releases/releases/download/linuxdeploy-${linuxdeploy_revision}/${linuxdeploy_name}" \
|
||||
"$linuxdeploy_sha256"
|
||||
|
||||
gtk_plugin_name=linuxdeploy-plugin-gtk-upstream.sh
|
||||
install_verified_tool \
|
||||
"$gtk_plugin_name" \
|
||||
"https://raw.githubusercontent.com/tauri-apps/tauri/${tauri_fix_commit}/crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh" \
|
||||
"$gtk_plugin_sha256"
|
||||
|
||||
upstream_gtk_plugin="$tools_dir/$gtk_plugin_name"
|
||||
gtk_plugin="$tools_dir/linuxdeploy-plugin-gtk.sh"
|
||||
install -m 0755 "$script_dir/linuxdeploy-plugin-gtk-wrapper.sh" "$gtk_plugin"
|
||||
bash -n "$upstream_gtk_plugin" "$gtk_plugin"
|
||||
if grep -Eq '^[[:space:]]*(export[[:space:]]+)?GDK_BACKEND[[:space:]]*=' "$upstream_gtk_plugin"; then
|
||||
die "pinned GTK plugin still contains an active GDK_BACKEND assignment"
|
||||
fi
|
||||
|
||||
echo "Prepared Wayland-capable AppImage tools for $target in $tools_dir"
|
||||
@@ -2,7 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
die() {
|
||||
echo "AppImage input-method verification failed: $*" >&2
|
||||
echo "AppImage runtime verification failed: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -37,6 +37,38 @@ appdir="$work_dir/squashfs-root"
|
||||
[[ -d "$appdir" ]] || die "AppImage extraction did not create squashfs-root"
|
||||
appdir=$(realpath "$appdir")
|
||||
|
||||
gtk_hook="$appdir/apprun-hooks/linuxdeploy-plugin-gtk.sh"
|
||||
[[ -f "$gtk_hook" ]] || die "missing GTK AppRun hook"
|
||||
if grep -Eq '^[[:space:]]*(export[[:space:]]+)?GDK_BACKEND[[:space:]]*=' "$gtk_hook"; then
|
||||
die "GTK AppRun hook overrides GDK_BACKEND"
|
||||
fi
|
||||
|
||||
for requested_backend in wayland x11; do
|
||||
preserved_backend=$(
|
||||
APPDIR="$appdir" GDK_BACKEND="$requested_backend" XDG_DATA_DIRS='' \
|
||||
bash --noprofile --norc -c 'source "$APPDIR/apprun-hooks/linuxdeploy-plugin-gtk.sh"; printf "%s" "$GDK_BACKEND"'
|
||||
)
|
||||
[[ "$preserved_backend" == "$requested_backend" ]] \
|
||||
|| die "GTK AppRun hook changed explicit GDK_BACKEND=$requested_backend to '$preserved_backend'"
|
||||
done
|
||||
echo "Verified GTK AppRun hook preserves explicit Wayland and X11 backends"
|
||||
|
||||
display_library_patterns=(
|
||||
'libwayland-*.so*'
|
||||
'libxkbcommon.so*'
|
||||
'libxcb-randr.so*'
|
||||
'libxcb-render.so*'
|
||||
'libxcb-shm.so*'
|
||||
'libXau.so*'
|
||||
'libXdmcp.so*'
|
||||
)
|
||||
for pattern in "${display_library_patterns[@]}"; do
|
||||
bundled_library=$(find "$appdir/usr" \( -type f -o -type l \) -name "$pattern" -print -quit)
|
||||
[[ -z "$bundled_library" ]] \
|
||||
|| die "bundled display-stack library must come from the host: ${bundled_library#"$appdir"/}"
|
||||
done
|
||||
echo "Verified AppImage relies on the host Wayland/XKB/XCB display stack"
|
||||
|
||||
require_artifact() {
|
||||
local basename=$1
|
||||
local match
|
||||
|
||||
@@ -256,6 +256,15 @@ jobs:
|
||||
# instead of loading potentially incompatible modules from the host.
|
||||
sudo apt-get install -y fcitx5-frontend-gtk3 ibus-gtk3 libwebkit2gtk-4.1-dev libgtk-3-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev xdg-utils
|
||||
|
||||
- name: Prepare Wayland-capable AppImage bundler
|
||||
if: startsWith(matrix.platform, 'ubuntu')
|
||||
shell: bash
|
||||
env:
|
||||
XDG_CACHE_HOME: ${{ runner.temp }}/dbx-tauri-cache
|
||||
run: |
|
||||
echo "XDG_CACHE_HOME=${XDG_CACHE_HOME}" >> "$GITHUB_ENV"
|
||||
./.github/scripts/prepare-appimage-bundler.sh "${{ matrix.target }}"
|
||||
|
||||
- name: Setup Tauri signing key
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -281,7 +290,7 @@ jobs:
|
||||
assetNamePattern: ${{ matrix.asset_pattern }}
|
||||
args: --target ${{ matrix.target }}
|
||||
|
||||
- name: Verify AppImage input methods
|
||||
- name: Verify AppImage runtime bundle
|
||||
if: startsWith(matrix.platform, 'ubuntu')
|
||||
shell: bash
|
||||
run: ./.github/scripts/verify-appimage-input-methods.sh "target/${{ matrix.target }}/release/bundle/appimage"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { chmodSync, existsSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { spawnSync } from "node:child_process";
|
||||
@@ -11,6 +11,9 @@ const releaseScript = join(repoRoot, "scripts/release.mjs");
|
||||
const releaseWorkflow = join(repoRoot, ".github/workflows/release.yml");
|
||||
const packagesWorkflow = join(repoRoot, ".github/workflows/mcp-release.yml");
|
||||
const vsignConfigPath = join(repoRoot, "src-tauri/tauri.vsign.conf.json");
|
||||
const appImageBundlerSetup = join(repoRoot, ".github/scripts/prepare-appimage-bundler.sh");
|
||||
const appImageGtkWrapper = join(repoRoot, ".github/scripts/linuxdeploy-plugin-gtk-wrapper.sh");
|
||||
const appImageVerifier = join(repoRoot, ".github/scripts/verify-appimage-input-methods.sh");
|
||||
|
||||
function runRelease(args, env = {}) {
|
||||
return spawnSync(process.execPath, [releaseScript, ...args], {
|
||||
@@ -169,6 +172,67 @@ test("Windows 7 release build does not use sccache", () => {
|
||||
assert.doesNotMatch(win7Job, /RUSTC_WRAPPER|SCCACHE_|sccache/i);
|
||||
});
|
||||
|
||||
test("Linux releases pin the Wayland-capable AppImage bundler before upload", () => {
|
||||
const workflow = readFileSync(releaseWorkflow, "utf8");
|
||||
const setup = readFileSync(appImageBundlerSetup, "utf8");
|
||||
const wrapper = readFileSync(appImageGtkWrapper, "utf8");
|
||||
const verifier = readFileSync(appImageVerifier, "utf8");
|
||||
const prepareStep = workflow.indexOf("Prepare Wayland-capable AppImage bundler");
|
||||
const buildStep = workflow.indexOf("Build Tauri app", prepareStep);
|
||||
const verifyStep = workflow.indexOf("Verify AppImage runtime bundle", buildStep);
|
||||
|
||||
assert.notEqual(prepareStep, -1);
|
||||
assert.ok(prepareStep < buildStep);
|
||||
assert.ok(buildStep < verifyStep);
|
||||
assert.match(workflow, /XDG_CACHE_HOME: \$\{\{ runner\.temp \}\}\/dbx-tauri-cache/);
|
||||
assert.match(setup, /tauri_fix_commit=8e7028331ad37ac2db74d4ec20e66be5cacf2c40/);
|
||||
assert.match(setup, /linuxdeploy_revision=07333c6/);
|
||||
assert.match(setup, /36a2d7e274d12e1050d0e9ecfe11d339ed54720b2bec464c286d53f8b07f5c62/);
|
||||
assert.match(setup, /556ab80baa98e600aa80f0dcedfb70bca0e1ce7e9f147fb345be3fcc3e91b2b1/);
|
||||
assert.match(wrapper, /libwayland-\*\.so\*/);
|
||||
assert.match(wrapper, /libxkbcommon\.so\*/);
|
||||
assert.match(verifier, /GTK AppRun hook overrides GDK_BACKEND/);
|
||||
assert.match(verifier, /for requested_backend in wayland x11/);
|
||||
assert.match(verifier, /host Wayland\/XKB\/XCB display stack/);
|
||||
});
|
||||
|
||||
test("AppImage GTK wrapper removes bundled display-stack libraries", () => {
|
||||
const directory = mkdtempSync(join(tmpdir(), "dbx-appimage-wrapper-test-"));
|
||||
const wrapper = join(directory, "linuxdeploy-plugin-gtk.sh");
|
||||
const upstream = join(directory, "linuxdeploy-plugin-gtk-upstream.sh");
|
||||
const appDir = join(directory, "DBX.AppDir");
|
||||
const libDir = join(appDir, "usr", "lib");
|
||||
const nestedLibDir = join(libDir, "nested");
|
||||
|
||||
copyFileSync(appImageGtkWrapper, wrapper);
|
||||
writeFileSync(
|
||||
upstream,
|
||||
'#!/usr/bin/env bash\nif [[ "$1" == "--plugin-api-version" ]]; then printf "0\\n"; fi\n',
|
||||
);
|
||||
chmodSync(wrapper, 0o755);
|
||||
chmodSync(upstream, 0o755);
|
||||
mkdirSync(nestedLibDir, { recursive: true });
|
||||
for (const path of [
|
||||
join(libDir, "libwayland-client.so.0"),
|
||||
join(libDir, "libxkbcommon.so.0"),
|
||||
join(nestedLibDir, "libXau.so.6"),
|
||||
join(libDir, "libsafe.so.1"),
|
||||
]) {
|
||||
writeFileSync(path, "");
|
||||
}
|
||||
|
||||
const apiVersion = spawnSync(wrapper, ["--plugin-api-version"], { encoding: "utf8" });
|
||||
assert.equal(apiVersion.status, 0, apiVersion.stderr);
|
||||
assert.equal(apiVersion.stdout.trim(), "0");
|
||||
|
||||
const cleanup = spawnSync(wrapper, ["--appdir", appDir], { encoding: "utf8" });
|
||||
assert.equal(cleanup.status, 0, cleanup.stderr);
|
||||
assert.equal(existsSync(join(libDir, "libwayland-client.so.0")), false);
|
||||
assert.equal(existsSync(join(libDir, "libxkbcommon.so.0")), false);
|
||||
assert.equal(existsSync(join(nestedLibDir, "libXau.so.6")), false);
|
||||
assert.equal(existsSync(join(libDir, "libsafe.so.1")), true);
|
||||
});
|
||||
|
||||
test("desktop-only Cargo.lock version refresh does not count as a Node package change", () => {
|
||||
const before = `
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user