Skip to content
Merged
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
133 changes: 109 additions & 24 deletions .github/workflows/linux-installers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
raco rivet new Smoke
cd Smoke
mkdir -p assets/nested
printf %s packaged-resource > assets/nested/product.txt
printf %s packaged-resource-v1 > assets/nested/product.txt
python3 - <<'PY'
import struct, zlib
from pathlib import Path
Expand All @@ -60,7 +60,7 @@ jobs:
'(resources . ())',
'(resources . ("assets")) '
'(linux-icon . "assets/icon.png") '
'(linux-formats . ("deb" "rpm" "appimage"))')
'(linux-formats . ("deb" "rpm"))')
if updated == original:
raise SystemExit("failed to configure Linux installer smoke")
path.write_text(updated, encoding="utf-8")
Expand All @@ -69,11 +69,35 @@ jobs:
raco rivet verify
artifacts="$RUNNER_TEMP/rivet-linux-native-artifacts"
diagnostics="$RUNNER_TEMP/rivet-linux-build-diagnostics"
mkdir -p "$artifacts" "$diagnostics"
cp dist/*.deb dist/*.rpm dist/*.AppImage "$artifacts/"
sha256sum "$artifacts"/* > "$diagnostics/SHA256SUMS"
rpm -qpi "$artifacts"/*.rpm > "$diagnostics/rpm-info.txt"
rpm -qp --requires "$artifacts"/*.rpm > "$diagnostics/rpm-requires.txt"
mkdir -p "$artifacts/v1" "$artifacts/v2" "$diagnostics"
cp dist/*.deb dist/*.rpm "$artifacts/v1/"

python3 - <<'PY'
from pathlib import Path

resource = Path("assets/nested/product.txt")
resource.write_text("packaged-resource-v2", encoding="utf-8")
config = Path("rivet.rktd")
original = config.read_text(encoding="utf-8")
updated = (original
.replace('(version . "0.1.0")', '(version . "0.2.0")')
.replace('(build . 1)', '(build . 2)')
.replace('(linux-formats . ("deb" "rpm"))',
'(linux-formats . ("deb" "rpm" "appimage"))'))
if updated == original or '(version . "0.2.0")' not in updated:
raise SystemExit("failed to configure the upgrade candidate")
config.write_text(updated, encoding="utf-8")
PY
raco rivet clean
raco rivet release --development --without-updates
raco rivet verify
cp dist/*.deb dist/*.rpm dist/*.AppImage "$artifacts/v2/"
sha256sum "$artifacts"/v1/* "$artifacts"/v2/* \
> "$diagnostics/SHA256SUMS"
rpm -qpi "$artifacts"/v1/*.rpm "$artifacts"/v2/*.rpm \
> "$diagnostics/rpm-info.txt"
rpm -qp --requires "$artifacts"/v2/*.rpm \
> "$diagnostics/rpm-requires.txt"
raco rivet doctor --json > "$diagnostics/doctor.json"
- name: Upload installers
uses: actions/upload-artifact@v6
Expand Down Expand Up @@ -155,7 +179,7 @@ jobs:
sleep 0.05
done
test -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"
appimage="$(find "$RUNNER_TEMP/installers" -type f -name '*.AppImage' -print -quit)"
appimage="$(find "$RUNNER_TEMP/installers/v2" -type f -name '*.AppImage' -print -quit)"
test -n "$appimage"
chmod +x "$appimage"
"$appimage" >"$diagnostics/application.log" 2>&1 &
Expand Down Expand Up @@ -183,7 +207,7 @@ jobs:
if-no-files-found: warn

debian-deb-wayland:
name: deb install and launch on Debian 13 Wayland
name: deb install, upgrade, launch, and recover on Debian 13 Wayland
needs: build
runs-on: ubuntu-24.04
timeout-minutes: 20
Expand All @@ -201,24 +225,53 @@ jobs:
apt-get install --yes \
dbus-daemon desktop-file-utils libgtk-4-1 libsecret-1-0 \
procps weston
- name: Install, launch, and uninstall deb
- name: Install, recover, upgrade, reject downgrade, launch, and uninstall deb
shell: bash
run: |
set -euo pipefail
diagnostics=/tmp/rivet-debian-diagnostics
mkdir -p "$diagnostics"
deb_artifact="$(find /tmp/rivet-installers -type f -name '*.deb' -print -quit)"
test -n "$deb_artifact"
deb_package="$(dpkg-deb --field "$deb_artifact" Package)"
deb_v1="$(find /tmp/rivet-installers/v1 -type f -name '*.deb' -print -quit)"
deb_v2="$(find /tmp/rivet-installers/v2 -type f -name '*.deb' -print -quit)"
test -n "$deb_v1"
test -n "$deb_v2"
deb_package="$(dpkg-deb --field "$deb_v1" Package)"
test -n "$deb_package"
apt-get install --yes "$deb_artifact" >"$diagnostics/apt-install.log" 2>&1
apt-get install --yes "$deb_v1" >"$diagnostics/apt-install-v1.log" 2>&1
dpkg-query --show --showformat='${Package} ${Version} ${Architecture}\n' \
"$deb_package" >"$diagnostics/installed-version.txt"
"$deb_package" >"$diagnostics/installed-version-v1.txt"
test "$(dpkg-query --show --showformat='${Version}' "$deb_package")" = "0.1.0-1"
test -x /opt/Smoke/RivetHost
test -f /usr/share/applications/Smoke.desktop
test -f /usr/share/pixmaps/Smoke.png
desktop-file-validate /usr/share/applications/Smoke.desktop
test "$(cat /opt/Smoke/app/assets/nested/product.txt)" = packaged-resource
test "$(cat /opt/Smoke/app/assets/nested/product.txt)" = packaged-resource-v1

# A truncated upgrade candidate must fail before the package manager
# mutates the working installation.
cp "$deb_v2" /tmp/corrupt-upgrade.deb
truncate --size 64 /tmp/corrupt-upgrade.deb
if apt-get install --yes /tmp/corrupt-upgrade.deb \
>"$diagnostics/apt-corrupt-upgrade.log" 2>&1; then
echo "apt unexpectedly accepted a corrupt upgrade" >&2
exit 1
fi
test "$(dpkg-query --show --showformat='${Version}' "$deb_package")" = "0.1.0-1"
test "$(cat /opt/Smoke/app/assets/nested/product.txt)" = packaged-resource-v1

apt-get install --yes "$deb_v2" >"$diagnostics/apt-upgrade-v2.log" 2>&1
dpkg-query --show --showformat='${Package} ${Version} ${Architecture}\n' \
"$deb_package" >"$diagnostics/installed-version-v2.txt"
test "$(dpkg-query --show --showformat='${Version}' "$deb_package")" = "0.2.0-2"
test "$(cat /opt/Smoke/app/assets/nested/product.txt)" = packaged-resource-v2

if apt-get install --yes "$deb_v1" \
>"$diagnostics/apt-downgrade-attempt.log" 2>&1; then
echo "apt unexpectedly downgraded without --allow-downgrades" >&2
exit 1
fi
test "$(dpkg-query --show --showformat='${Version}' "$deb_package")" = "0.2.0-2"
test "$(cat /opt/Smoke/app/assets/nested/product.txt)" = packaged-resource-v2

export XDG_RUNTIME_DIR=/tmp/rivet-debian-wayland-runtime
export WAYLAND_DISPLAY=wayland-rivet-debian
Expand Down Expand Up @@ -295,7 +348,7 @@ jobs:
if-no-files-found: warn

fedora-rpm-wayland:
name: rpm install and launch on Fedora 44 Wayland
name: rpm install, upgrade, launch, and recover on Fedora 44 Wayland
needs: build
runs-on: ubuntu-24.04
timeout-minutes: 20
Expand All @@ -307,24 +360,56 @@ jobs:
path: /tmp/rivet-installers
- name: Install desktop runtime
run: dnf install --assumeyes dbus-daemon desktop-file-utils gtk4 procps-ng weston
- name: Install, launch, and uninstall rpm
- name: Install, recover, upgrade, roll back, re-upgrade, launch, and uninstall rpm
shell: bash
run: |
set -euo pipefail
diagnostics=/tmp/rivet-fedora-diagnostics
mkdir -p "$diagnostics"
rpm_artifact="$(find /tmp/rivet-installers -type f -name '*.rpm' -print -quit)"
test -n "$rpm_artifact"
rpm_package="$(rpm -qp --qf '%{NAME}' "$rpm_artifact")"
rpm_v1="$(find /tmp/rivet-installers/v1 -type f -name '*.rpm' -print -quit)"
rpm_v2="$(find /tmp/rivet-installers/v2 -type f -name '*.rpm' -print -quit)"
test -n "$rpm_v1"
test -n "$rpm_v2"
rpm_package="$(rpm -qp --qf '%{NAME}' "$rpm_v1")"
test -n "$rpm_package"
dnf install --assumeyes "$rpm_artifact" >"$diagnostics/dnf-install.log" 2>&1
dnf install --assumeyes "$rpm_v1" >"$diagnostics/dnf-install-v1.log" 2>&1
rpm -q --qf '%{NAME} %{VERSION}-%{RELEASE} %{ARCH}\n' "$rpm_package" \
>"$diagnostics/installed-version.txt"
>"$diagnostics/installed-version-v1.txt"
test "$(rpm -q --qf '%{VERSION}-%{RELEASE}' "$rpm_package")" = "0.1.0-1"
test -x /opt/Smoke/RivetHost
test -f /usr/share/applications/Smoke.desktop
test -f /usr/share/pixmaps/Smoke.png
desktop-file-validate /usr/share/applications/Smoke.desktop
test "$(cat /opt/Smoke/app/assets/nested/product.txt)" = packaged-resource
test "$(cat /opt/Smoke/app/assets/nested/product.txt)" = packaged-resource-v1

cp "$rpm_v2" /tmp/corrupt-upgrade.rpm
truncate --size 64 /tmp/corrupt-upgrade.rpm
if dnf install --assumeyes /tmp/corrupt-upgrade.rpm \
>"$diagnostics/dnf-corrupt-upgrade.log" 2>&1; then
echo "dnf unexpectedly accepted a corrupt upgrade" >&2
exit 1
fi
test "$(rpm -q --qf '%{VERSION}-%{RELEASE}' "$rpm_package")" = "0.1.0-1"
test "$(cat /opt/Smoke/app/assets/nested/product.txt)" = packaged-resource-v1

dnf install --assumeyes "$rpm_v2" >"$diagnostics/dnf-upgrade-v2.log" 2>&1
rpm -q --qf '%{NAME} %{VERSION}-%{RELEASE} %{ARCH}\n' "$rpm_package" \
>"$diagnostics/installed-version-v2.txt"
test "$(rpm -q --qf '%{VERSION}-%{RELEASE}' "$rpm_package")" = "0.2.0-2"
test "$(cat /opt/Smoke/app/assets/nested/product.txt)" = packaged-resource-v2

# DNF treats an explicitly selected older local RPM as an authorized
# rollback. Prove that its transaction restores the complete v1
# payload, then prove recovery to v2 before launching the app.
dnf install --assumeyes "$rpm_v1" \
>"$diagnostics/dnf-rollback-v1.log" 2>&1
test "$(rpm -q --qf '%{VERSION}-%{RELEASE}' "$rpm_package")" = "0.1.0-1"
test "$(cat /opt/Smoke/app/assets/nested/product.txt)" = packaged-resource-v1

dnf install --assumeyes "$rpm_v2" \
>"$diagnostics/dnf-reupgrade-v2.log" 2>&1
test "$(rpm -q --qf '%{VERSION}-%{RELEASE}' "$rpm_package")" = "0.2.0-2"
test "$(cat /opt/Smoke/app/assets/nested/product.txt)" = packaged-resource-v2

export XDG_RUNTIME_DIR=/tmp/rivet-fedora-wayland-runtime
export WAYLAND_DISPLAY=wayland-rivet-fedora
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased

- Graduate the Linux package-manager lifecycle gates from a single install to
two-version deb/rpm transactions: corrupt upgrades preserve the working
install, normal upgrades replace both metadata and payload, Debian rejects
implicit downgrade while Fedora verifies explicit local-RPM rollback and
re-upgrade, and uninstall remains complete under native Wayland.
- Make embedded diagnostics opt-in across Racket, C++, and Swift. GUI hosts no
longer write to standard error by default, preventing Windows applications
from allocating a black console window on their first diagnostic; explicit
Expand Down
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,22 @@ See [architecture](docs/architecture.md), [agent-native development](docs/agent-

| Capability | Windows | macOS | Linux |
|---|---|---|---|
| Native host | ✅ WinUI 3 + C++/WinRT | ✅ SwiftUI + Swift | 🧪 GTK4 + C++ |
| Native host | ✅ WinUI 3 + C++/WinRT | ✅ SwiftUI + Swift | ✅ GTK4 + C++ / Wayland |
| Embedded Racket CS | ✅ | ✅ | ✅ static runtime |
| RVT1 / Events / State / Cancel | ✅ | ✅ | ✅ |
| Typed generated client | ✅ C++ | ✅ Swift | ✅ C++ |
| `new` / `doctor` / `build` / `dev` | ✅ | ✅ | ✅ |
| `package` / `verify` | ✅ dependency audit | ✅ signing/rpath/plist audit | 🧪 directory + `ldd` audit |
| Production signing / installer | ✅ Authenticode + MSI | ✅ Developer ID + DMG | 🧪 signed tarball + deb/rpm/AppImage |
| System services / secure storage | ✅ | ✅ | 🧪 Linux adapter + StatusNotifierItem tray |
| `package` / `verify` | ✅ dependency audit | ✅ signing/rpath/plist audit | ✅ directory + `ldd` audit |
| Production signing / installer | ✅ Authenticode + MSI | ✅ Developer ID + DMG | ✅ signed tarball + deb/rpm/AppImage |
| System services / secure storage | ✅ | ✅ | ✅ Linux adapter + StatusNotifierItem tray |
| Real embedded-runtime CI | ✅ | ✅ | ✅ |

Windows and macOS remain the production release targets. Linux is a developer preview with the complete daily CLI path, signed tarball plus deb/rpm/AppImage artifacts, real embedded-runtime CI, and a first-party system adapter for single-instance, notifications, tray, autostart, secure storage, crash hooks, and graceful shutdown. Wayland is the primary Linux display target and a native Wayland launch is a CI gate; X11 and XWayland remain best-effort GTK compatibility paths. Production graduation still requires install/upgrade evidence across the supported distribution matrix.
Windows, macOS, and Linux are production release targets. Linux qualification
uses native Wayland and real deb/rpm package-manager transactions for install,
failed-upgrade recovery, upgrade, distro-native downgrade or rollback behavior,
launch, and uninstall;
AppImage launch is independently gated outside the build tree. X11 and
XWayland remain best-effort GTK compatibility paths, not release targets.

### Apple mobile foundation

Expand Down Expand Up @@ -316,7 +321,7 @@ CI runs the protocol implementation across Racket, C++, Swift, and Kotlin; exerc

## Honest gaps

- **Linux is a developer preview** — the complete daily CLI path, signed tarball, deb/rpm/AppImage formats, tray, first-party system adapter, and Wayland CI work; production install/upgrade evidence across supported distributions is not complete. X11/XWayland is best-effort compatibility, not a release gate.
- **Linux desktop qualification is intentionally bounded** — the release matrix proves native Wayland, package-manager lifecycle, AppImage launch, and system-adapter behavior on maintained distributions. Product-specific GPU, portal, scaling, input, GNOME, and KDE behavior still belongs in each application's release evidence. X11/XWayland is best-effort compatibility, not a release gate.
- **Apple mobile delivery is foundational** — the portable Swift and typed WatchConnectivity layers exist, but iOS/iPadOS/watchOS project generation, runtime packaging, signing, and store delivery are not complete.
- **Android remains foundational** — its Kotlin RVT1 codec, coroutine runtime client, typed client code generation, and pinned Gradle build are tested, but Jetpack Compose, JNI, portable Racket CS packaging, signing, and device delivery are not complete.
- **Architecture evidence covers build/package/verify, not production releases** — clean-runner gates exercise Windows x64/ARM64, macOS Apple-Silicon/Intel, and Linux x64/ARM64, but production release artifacts are still produced by the tag-driven release flow per application.
Expand Down
15 changes: 9 additions & 6 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,20 @@ Racket CS 运行在独立 runtime 线程。原生 UI 代码不会直接操作 Ra

| 能力 | Windows | macOS | Linux |
|---|---|---|---|
| 原生宿主 | ✅ WinUI 3 + C++/WinRT | ✅ SwiftUI + Swift | 🧪 GTK4 + C++ |
| 原生宿主 | ✅ WinUI 3 + C++/WinRT | ✅ SwiftUI + Swift | ✅ GTK4 + C++ / Wayland |
| Embedded Racket CS | ✅ | ✅ | ✅ 静态 runtime |
| RVT1 / Event / State / Cancel | ✅ | ✅ | ✅ |
| 类型化客户端生成 | ✅ C++ | ✅ Swift | ✅ C++ |
| `new` / `doctor` / `build` / `dev` | ✅ | ✅ | ✅ |
| `package` / `verify` | ✅ DLL 依赖审计 | ✅ 签名/rpath/plist | 🧪 目录 + `ldd` 审计 |
| 生产签名 / 安装包 | ✅ Authenticode + MSI | ✅ Developer ID + DMG | 🧪 签名 tarball + deb/rpm/AppImage |
| 系统服务 / 安全存储 | ✅ | ✅ | 🧪 Linux 适配器 + StatusNotifierItem 托盘 |
| `package` / `verify` | ✅ DLL 依赖审计 | ✅ 签名/rpath/plist | ✅ 目录 + `ldd` 审计 |
| 生产签名 / 安装包 | ✅ Authenticode + MSI | ✅ Developer ID + DMG | ✅ 签名 tarball + deb/rpm/AppImage |
| 系统服务 / 安全存储 | ✅ | ✅ | ✅ Linux 适配器 + StatusNotifierItem 托盘 |
| 真实嵌入运行时 CI | ✅ | ✅ | ✅ |

Windows 和 macOS 仍是生产发布目标。Linux 处于开发者预览:日常 CLI 全链路、签名 tarball 与 deb/rpm/AppImage、真实 embedded-runtime CI,以及覆盖单实例、通知、托盘、自启动、安全存储、崩溃钩子和优雅退出的一方系统适配器已经完成。Wayland 是 Linux 的首要显示目标,CI 会强制验证原生 Wayland 启动;X11/XWayland 仅保留 GTK 尽力兼容。进入生产级之前仍需补齐受支持发行版矩阵上的真实安装与升级证据。
Windows、macOS 与 Linux 都是生产发布目标。Linux 资格门禁使用原生
Wayland,并通过真实 deb/rpm 包管理器事务验证安装、失败升级恢复、升级、
拒绝隐式降级、启动与卸载;AppImage 还会在构建目录外独立启动验证。
X11/XWayland 仅保留 GTK 尽力兼容,不是发布目标。

### Apple 移动端基础

Expand Down Expand Up @@ -316,7 +319,7 @@ CI 会验证 Racket、C++、Swift 与 Kotlin 协议实现,在三个桌面平

## 诚实的局限

- **Linux 是开发者预览** —— 日常 CLI 全链路、签名 tarball、deb/rpm/AppImage、托盘、一方系统适配器与 Wayland CI 已经完成;受支持发行版矩阵上的生产安装/升级证据尚未补齐。X11/XWayland 是尽力兼容路径,不是发布门禁。
- **Linux 桌面资格验证有明确边界** —— 发布矩阵会在仍受维护的发行版上验证原生 Wayland、包管理器生命周期、AppImage 启动与系统适配器行为;GPU、Portal、缩放、输入、GNOME、KDE 等产品特定行为仍应由各应用自己的发布证据覆盖。X11/XWayland 是尽力兼容路径,不是发布门禁。
- **Apple 移动端仍是基础阶段** —— 可移植 Swift 与类型安全 WatchConnectivity 层已经存在,但 iOS/iPadOS/watchOS 项目生成、runtime 打包、签名和商店交付还未完成。
- **Android 仍处于基础阶段** —— Kotlin RVT1 codec、协程 runtime 客户端、类型化客户端生成与固定版本的 Gradle 构建已经过测试,但 Jetpack Compose、JNI、portable Racket CS 打包、签名和设备交付尚未完成。
- **架构证据覆盖构建/打包/验证,尚不覆盖生产发布** —— 干净 runner 门禁覆盖 Windows x64/ARM64、macOS Apple Silicon/Intel 与 Linux x64/ARM64,但生产发布产物仍由标签驱动的发布流程按应用生成。
Expand Down
Loading
Loading