#!/usr/bin/env bash set -euo pipefail APP_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) VERSION=${OPENLESS_LINUX_VERSION:?OPENLESS_LINUX_VERSION is required} # Never silently publish an unnumbered or stale package that apt treats as a # downgrade. Each candidate explicitly advances linux-egui/package-revision. REVISION=$(< "$APP_ROOT/linux-egui/package-revision") [[ "$REVISION" =~ ^[1-9][0-9]*$ ]] || { echo 'invalid Linux package revision' >&2; exit 1; } APP_VERSION=$(node -p "require('$APP_ROOT/package.json').version") EXPECTED_VERSION="${APP_VERSION%%+*}-$REVISION" if [ "$VERSION" != "$EXPECTED_VERSION" ]; then echo "Linux package version must be $EXPECTED_VERSION (got $VERSION)" >&2 exit 1 fi ARCH=${OPENLESS_LINUX_ARCH:-x86_64} TARGET_DIR=${CARGO_TARGET_DIR:-"$APP_ROOT/target"} BINARY="$TARGET_DIR/release/openless-linux-egui" PLUGIN_ROOT="$APP_ROOT/../scripts/linux-fcitx5-plugin/build" PACKAGING="$APP_ROOT/linux-egui/packaging" OUTPUT="$TARGET_DIR/linux-egui-packages" # 图标:与 Tauri 侧共用同一套画(`icon.png` 与 `public/AppIcon.png` 的 md5 相同, # 都是 512×512)。但本脚本必须保持 Tauri-free —— 两条契约都会检查脚本里不得出现 # Tauri 源码树的路径名 —— 所以这里放字节相同的副本,并由 release 契约做逐字节 # 一致性断言,保证两边共用一套素材且不会静默漂移。 ICON_DIR="$PACKAGING/icons" # 图标名 → hicolor 尺寸目录 → 源文件(与 Tauri 侧的图标集逐字节一致)。 HICOLOR_ICONS=( "32x32:32x32.png" "64x64:64x64.png" "128x128:128x128.png" "256x256:128x128@2x.png" "512x512:icon.png" ) test -x "$BINARY" test -d "$ICON_DIR" for spec in "${HICOLOR_ICONS[@]}"; do test -s "$ICON_DIR/${spec#*:}"; done test -s "$PLUGIN_ROOT/libopenless.so" test -s "$PLUGIN_ROOT/openless.conf" test -s "$PACKAGING/openless.desktop" test -s "$PACKAGING/top.openless.OpenLess.metainfo.xml" command -v dpkg-deb >/dev/null command -v rpmbuild >/dev/null mkdir -p "$OUTPUT" POST_INSTALL="$TARGET_DIR/openless-fcitx5-postinst" cat > "$POST_INSTALL" <<'EOF' #!/usr/bin/env bash set +e # Package installation runs as root, while fcitx5 belongs to the logged-in # desktop user. Reconnect only to existing user DBus sessions; never start a # daemon or fail the package transaction when no graphical session is active. for bus in /run/user/[0-9]*/bus; do [ -S "$bus" ] || continue runtime_dir=${bus%/bus} uid=${runtime_dir##*/} [ "$uid" != "0" ] || continue user=$(getent passwd "$uid" | cut -d: -f1) [ -n "$user" ] || continue runuser -u "$user" -- env \ XDG_RUNTIME_DIR="$runtime_dir" \ DBUS_SESSION_BUS_ADDRESS="unix:path=$bus" \ timeout 5s dbus-send --session --dest=org.fcitx.Fcitx5 --type=method_call /controller org.fcitx.Fcitx.Controller1.Restart >/dev/null 2>&1 || true done exit 0 EOF chmod 0755 "$POST_INSTALL" # 卸载同样要重启 fcitx5:文件被删掉后,运行中的输入法仍持有旧插件的映像。 # 用 D-Bus 的 controller Restart(立即返回),**不要**用 `fcitx5 -r`:它会替换 daemon # 并一直前台运行,导致 postinst/postrm 每次都要等满 timeout(安装卡顿),还会被 kill 掉新 daemon。 POST_REMOVE="$TARGET_DIR/openless-fcitx5-postrm" sed 's/Package installation runs as root/Package removal runs as root/' \ "$POST_INSTALL" > "$POST_REMOVE" chmod 0755 "$POST_REMOVE" # 插件指纹清单:装完包后一条命令就能核对「系统里的插件 == 包里的插件」。 PLUGIN_SHA=$(sha256sum "$PLUGIN_ROOT/libopenless.so" | awk '{print $1}') PLUGIN_MANIFEST="$TARGET_DIR/openless-fcitx5-manifest" cat > "$PLUGIN_MANIFEST" < "$DEB_ROOT/DEBIAN/control" < "$RPM_TOP/SPECS/openless.spec" </dev/null 2>&1 || true done exit 0 %post set +e for bus in /run/user/[0-9]*/bus; do [ -S "\$bus" ] || continue runtime_dir=\${bus%/bus}; uid=\${runtime_dir##*/} [ "\$uid" != 0 ] || continue user=\$(getent passwd "\$uid" | cut -d: -f1) [ -n "\$user" ] && timeout 5s runuser -u "\$user" -- env XDG_RUNTIME_DIR="\$runtime_dir" DBUS_SESSION_BUS_ADDRESS="unix:path=\$bus" dbus-send --session --dest=org.fcitx.Fcitx5 --type=method_call /controller org.fcitx.Fcitx.Controller1.Restart >/dev/null 2>&1 || true done exit 0 EOF rpmbuild --define "_topdir $RPM_TOP" --define "_dbpath $RPM_TOP/rpmdb" \ --define "_tmppath $RPM_TOP/tmp" -bb "$RPM_TOP/SPECS/openless.spec" mv "$RPM_TOP/RPMS/x86_64/openless-$RPM_VERSION-1.x86_64.rpm" \ "$OUTPUT/OpenLess-Linux-egui-${VERSION}-${ARCH}.rpm" find "$OUTPUT" -maxdepth 1 -type f -printf '%f\n' | sort