FramePort 0.6.1: Linux ARM64 build (CI ubuntu-22.04-arm, PyInstaller one-folder fallback; the updater picks FramePort-linux-arm64.tar.gz on ARM); tested games list docs/GAMES.md generated from the catalog (scripts/compat_list.py)

This commit is contained in:
spoopyghosty0 committed 2026-10-03 12:05:59 -04:00
1 parent a6ca8385a9
commit 6ed95684dc
11 files changed
+119 -10

No files matched your search

+9 -5
View File
@@ -40,6 +40,8 @@ jobs:
archive: FramePort-macos-arm64.zip
- os: ubuntu-22.04 # oldest supported: the bundle needs the build host's GLib/glibc or newer
archive: FramePort-linux-x64.tar.gz
- os: ubuntu-22.04-arm # Linux ARM64 (same base as x64)
archive: FramePort-linux-arm64.tar.gz
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
@@ -50,9 +52,9 @@ jobs:
- run: uv sync --extra dev
- id: flet
run: uv run python scripts/package.py
continue-on-error: ${{ runner.os == 'macOS' }}
- name: macOS fallback (PyInstaller bundle)
if: runner.os == 'macOS' && steps.flet.outcome == 'failure'
continue-on-error: ${{ runner.os == 'macOS' || runner.arch == 'ARM64' }}
- name: macOS / Linux ARM64 fallback (PyInstaller bundle)
if: (runner.os == 'macOS' || runner.arch == 'ARM64') && steps.flet.outcome == 'failure'
run: uv run --with pyinstaller python scripts/package.py --pyinstaller
# ---- signing: self-signed certificate on Windows, ad-hoc on macOS ----
@@ -95,7 +97,9 @@ jobs:
run: ditto -c -k --keepParent "$(find dist -maxdepth 3 -name '*.app' -type d | head -1)" ${{ matrix.archive }}
- name: Package (Linux)
if: runner.os == 'Linux'
run: tar czf ${{ matrix.archive }} -C dist --transform 's,^linux,FramePort,' linux
run: | # flet build -> dist/linux; the PyInstaller fallback -> dist/FramePort (one folder)
if [ -d dist/linux ]; then tar czf ${{ matrix.archive }} -C dist --transform 's,^linux,FramePort,' linux
else tar czf ${{ matrix.archive }} -C dist FramePort; fi
# self-update with this archive: unpack, verify the layout (+ the signer on Windows), run the real swap script
- name: Update smoke test
run: uv run python scripts/update_smoke.py ${{ matrix.archive }}
@@ -137,7 +141,7 @@ jobs:
git fetch --force origin "refs/tags/${tag}:refs/tags/${tag}"
whatsnew=$(git tag -l --format='%(contents:body)' "$tag")
[ -n "$whatsnew" ] || whatsnew=$(git tag -l --format='%(contents:subject)' "$tag")
{ echo "FramePort ${tag}: Windows (x64), macOS (Apple Silicon) and Linux (x64)."; echo;
{ echo "FramePort ${tag}: Windows (x64), macOS (Apple Silicon) and Linux (x64, ARM64)."; echo;
if [ -n "$whatsnew" ] && [ "$(git cat-file -t "$tag")" = tag ]; then
echo "## What's new"; echo; echo "$whatsnew"; echo; fi
echo "Already have FramePort? It offers this update itself (Library → **Update now**)."
+3
View File
@@ -66,6 +66,8 @@ fix, so Steam's on-screen keyboard and Type on Frame work in them too.
If a game has already been tested with FramePort, it will automatically use the optimal game config. Otherwise, FramePort
will attempt to guess key patches. If you find a new config that works for an app you are testing, please consider submitting it to the community!
**[List of tested games](docs/GAMES.md)**
## Built on
[OVRPort](https://github.com/Android-XR-Bridge/OVRPort) (Quest → OpenXR, originally
@@ -81,6 +83,7 @@ What FramePort adds itself: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
| [INSTALL.md](docs/INSTALL.md) | Install, connect, update, PC VR, files, problem reports |
| [FRAME_SETUP.md](docs/FRAME_SETUP.md) | What setup changes on the Frame, networks and firewalls, undoing it |
| [COMPATIBILITY.md](docs/COMPATIBILITY.md) | What runs and how well |
| [GAMES.md](docs/GAMES.md) | Tested games and how well they run |
| [PLAYBOOK.md](docs/PLAYBOOK.md) | Symptoms and fixes per game |
| [FRAME_RUNTIME.md](docs/FRAME_RUNTIME.md) | Steam Frame runtime facts |
| [ARCHITECTURE.md](docs/ARCHITECTURE.md) | How the code is organised |
+1 -1
View File
@@ -1,7 +1,7 @@
# Compatibility
The built-in catalog has tested settings for games, each marked as working, working with known issues, or not
running on the Frame (the recipes are in [catalog/games](../catalog/games)).
running on the Frame: see the [list of tested games](GAMES.md) (recipes in [catalog/games](../catalog/games)).
Other games get suggested patches from detection rules; each suggestion states its reason, and every patch can be
switched on or off under **Customize**: described in plain words, with **Show technical details** for the exact
effect of each patch.
+46
View File
@@ -0,0 +1,46 @@
# Tested games
Games tested on the Steam Frame with FramePort's recipes. Games not listed here may work too: FramePort suggests patches for them, and a working config can be shared from the app (**Share working config…**).
Generated from [catalog/games](../catalog/games) by `scripts/compat_list.py`.
| Game | Platform | Status | Notes |
|---|---|---|---|
| 4XVR Video Player | Quest | ✅ Works | |
| Asgard's Wrath 2 | Quest | ✅ Works | |
| BAM | Quest | ✅ Works | |
| BARTENDER VR SIMULATOR | Quest | ✅ Works | |
| Batman: Arkham Shadow | Quest | ✅ Works | |
| Carve Snowboarding | Quest | ✅ Works | |
| Demeter | Quest | ✅ Works | |
| Espire 2 | Quest | ✅ Works | |
| Genotype | Quest | ✅ Works | |
| H.U.N.T | Quest | ✅ Works | |
| In Death: Unchained | Quest | ✅ Works | |
| LEGO® Bricktales | Quest | ✅ Works | |
| Marvel's Deadpool VR | Quest | ✅ Works | |
| Medieval Dynasty New Settlement | Quest | ✅ Works | |
| Mobile Suit Gundam: Silver Phantom | Quest | ✅ Works | |
| Nano | Quest | ✅ Works | |
| NEX Player | Quest | ✅ Works | |
| NOPE CHALLENGE | Quest | ✅ Works | |
| Path of the Warrior | Quest | ✅ Works | |
| PowerWash Simulator VR | Quest | ✅ Works | |
| Rick and Morty: Virtual Rick-ality | PC VR | ✅ Works | |
| Robo Recall | Quest | ✅ Works | |
| Sniper Elite VR: Winter Warrior | Quest | ✅ Works | |
| Stremio | Quest | ✅ Works | |
| The Climb 2 | Quest | ✅ Works | |
| Toy Master | Quest | ✅ Works | |
| Under Cover | Quest | ✅ Works | |
| Wallace & Gromit in The Grand Getaway | Quest | ✅ Works | |
| Arcsmith | Quest | ⚠️ Works with issues | Right eye distorts during movement (unresolved; swap, tracking, Valve layers, depth and pacing ruled out). |
| Assassin's Creed Nexus | Quest | ⚠️ Works with issues | Some launch warning text is still upside down; the rest of the UI is fixed by flip emulation. |
| Phantom: Covert Ops | Quest | ⚠️ Works with issues | DLC/store button crashes (no Meta store). |
| Silhouette | Quest | ⚠️ Works with issues | Hand-tracking game; the Frame synthesizes hands from controllers, so it is janky. |
| Time Stall | Quest | ⚠️ Works with issues | Both eyes distort during movement (unresolved). |
| Espire 1: VR Operative (Quest Edition) | Quest | ❌ Doesn't run | Mesa GL driver crash during texture upload. |
| HITMAN 3 VR: Reloaded | Quest | ❌ Doesn't run | Vulkan driver crash (freedreno), even without Valve layers. |
| Journey of the Gods | Quest | ❌ Doesn't run | 32-bit only; the Frame has no AArch32. |
| Shadow Point | Quest | ❌ Doesn't run | 32-bit only; the Frame has no AArch32. |
| Sniper Elite VR | Quest | ❌ Doesn't run | GPU hang (zink: DEVICE LOST) even with MSAA off. |
| Sports Scramble (Santa Cruz) | Quest | ❌ Doesn't run | 32-bit only; the Frame has no AArch32. |
+2 -1
View File
@@ -9,6 +9,7 @@ runtime, the OVRPort CLI and apksigner into its data folder (Settings → Tools
| Windows 10/11 (x64) | `FramePort-windows-x64.zip` | `FramePort.exe` |
| macOS (Apple Silicon) | `FramePort-macos-arm64.zip` | `FramePort.app` |
| Linux (x64, GTK 3; Ubuntu 22.04 or newer) | `FramePort-linux-x64.tar.gz` | `FramePort/FramePort` |
| Linux (ARM64, GTK 3; Ubuntu 22.04 or newer) | `FramePort-linux-arm64.tar.gz` | `FramePort/FramePort` |
| Command line only (Python 3.11+) | `frameport-<version>-py3-none-any.whl` | `frameport --help` |
The command-line version installs from the wheel's release link with `uv tool install <link>` (or pipx / pip).
@@ -22,7 +23,7 @@ start shows a warning:
`FramePort-selfsigned.cer` (attached to each release) into *Trusted Root Certification Authorities* (Current User) to
show FramePort as the publisher; the certificate can only sign code. Remove it with `certmgr.msc`.
- **macOS:** right-click `FramePort.app` → **Open** → **Open** (once), or `xattr -dr com.apple.quarantine FramePort.app`.
- **Linux:** `tar xzf FramePort-linux-x64.tar.gz && ./FramePort/FramePort`.
- **Linux:** `tar xzf FramePort-linux-x64.tar.gz && ./FramePort/FramePort` (ARM64: `FramePort-linux-arm64.tar.gz`).
## Connecting the Steam Frame
+1 -1
View File
@@ -2,7 +2,7 @@
- **Windows:** unzip `FramePort-windows-x64.zip`, run `FramePort.exe` (warning → **More info → Run anyway**).
- **macOS:** unzip `FramePort-macos-arm64.zip`, right-click `FramePort.app` → **Open**.
- **Linux:** `tar xzf FramePort-linux-x64.tar.gz && ./FramePort/FramePort`.
- **Linux:** `tar xzf FramePort-linux-x64.tar.gz && ./FramePort/FramePort` (ARM64: `FramePort-linux-arm64.tar.gz`).
Full guide: [docs/INSTALL.md](https://github.com/spoopyghosty0/frameport/blob/main/docs/INSTALL.md) · checksums in
`SHA256SUMS.txt`.
+42
View File
@@ -0,0 +1,42 @@
"""Write docs/GAMES.md: the tested games from the bundled catalog as a simple table to share.
Run after catalog changes: python scripts/compat_list.py"""
from __future__ import annotations
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
from frameport.recommend import catalog # noqa: E402
STATUS = {"works": "✅ Works", "issues": "⚠️ Works with issues", "unsupported": "❌ Doesn't run"}
ORDER = {"works": 0, "issues": 1, "unsupported": 2}
def short(note: str) -> str:
"""The note's first sentence, without technical asides."""
first = re.split(r"(?<=[.!?])\s", " ".join((note or "").split()), maxsplit=1)[0]
return first if len(first) <= 160 else first[:157].rstrip() + "…"
def render() -> str:
entries = [e for e in catalog.load().values() if e.origin == "bundled" and e.status in STATUS]
entries.sort(key=lambda e: (ORDER[e.status], e.title.lower()))
rows = ["| Game | Platform | Status | Notes |", "|---|---|---|---|"]
for e in entries:
platform = "PC VR" if e.kind == "rift" else "Quest"
note = "" if e.status == "works" else short(e.notes).replace("|", "/")
rows.append(f"| {e.title} | {platform} | {STATUS[e.status]} | {note} |")
return ("# Tested games\n\n"
"Games tested on the Steam Frame with FramePort's recipes. Games not listed here may work too: FramePort "
"suggests patches for them, and a working config can be shared from the app (**Share working config…**).\n"
"Generated from [catalog/games](../catalog/games) by `scripts/compat_list.py`.\n\n"
+ "\n".join(rows) + "\n")
if __name__ == "__main__":
out = ROOT / "docs" / "GAMES.md"
out.write_text(render(), encoding="utf-8")
print(f"wrote {out}")
+2
View File
@@ -70,6 +70,8 @@ def main() -> int:
# translations (data files PyInstaller doesn't pick up from the imports)
"--add-data", f"{ROOT / 'src/frameport/locales'}{sep}frameport/locales",
"--distpath", str(ROOT / "dist"), "--yes"]
if TARGET == "linux":
cmd.append("--onedir") # a folder: the bundle check sees the agent source, the archive the app
else:
# --yes: install the Flutter SDK etc. without prompting; --no-rich-output: plain logs (CI, Windows consoles)
cmd = ["flet", "build", TARGET, str(ROOT), "--project", "FramePort", "--product", "FramePort",
+1 -1
View File
@@ -1,2 +1,2 @@
# The FramePort version: the single source (pyproject reads it via hatch; CI checks a release tag matches it).
__version__ = "0.6.0"
__version__ = "0.6.1"
+4 -1
View File
@@ -18,6 +18,7 @@ import hashlib
import json
import logging
import os
import platform
import re
import shutil
import subprocess
@@ -39,7 +40,7 @@ REPO = REPO_URL.removeprefix("https://github.com/").strip("/")
LATEST_API = f"https://api.github.com/repos/{REPO}/releases/latest"
CHECK_EVERY = 6 * 3600 # seconds between automatic checks
ASSETS = {"win32": "FramePort-windows-x64.zip", "darwin": "FramePort-macos-arm64.zip",
"linux": "FramePort-linux-x64.tar.gz"}
"linux": "FramePort-linux-x64.tar.gz", "linux-arm64": "FramePort-linux-arm64.tar.gz"} # never rename
SUMS = "SHA256SUMS.txt"
@@ -75,6 +76,8 @@ def is_newer(candidate: str, current: str = __version__) -> bool:
def platform_asset() -> str | None:
key = "linux" if sys.platform.startswith("linux") else sys.platform
if key == "linux" and platform.machine().lower() in ("aarch64", "arm64"):
key = "linux-arm64" # the ARM64 bundle (from 0.6.0)
return ASSETS.get(key)
+8
View File
@@ -246,3 +246,11 @@ def test_apply_reports_a_script_that_never_starts(monkeypatch, tmp_path):
monkeypatch.setattr(updates.subprocess, "Popen", lambda *a, **k: type("P", (), {"poll": lambda self: None})())
with pytest.raises(updates.UpdateError):
updates.apply(staged, installed, relaunch=False, pid=1, platform="linux")
def test_platform_asset_picks_the_arm64_linux_bundle(monkeypatch):
monkeypatch.setattr(updates.sys, "platform", "linux")
monkeypatch.setattr(updates.platform, "machine", lambda: "aarch64")
assert updates.platform_asset() == "FramePort-linux-arm64.tar.gz"
monkeypatch.setattr(updates.platform, "machine", lambda: "x86_64")
assert updates.platform_asset() == "FramePort-linux-x64.tar.gz"