Compare commits

..
Author SHA1 Message Date
theupriser 0447f35e7d fix: boot check: the power-off fix is loaded, or was tried at boot and refused (journal); it grepped dmesg for systemd's error, which only sometimes lands there 2026-09-29 20:17:27 +02:00
theupriser d614539d9e docs: full run with Steamify release/2.9.1: 341 pass; the CEC driver cache done 2026-09-29 19:46:08 +02:00
theupriser 847b024803 fix: test prelude sets SCRIPT_DIR (the libs read patches/ from it); hw block 50 checks every run for errors 2026-09-29 19:24:25 +02:00
theupriser 8df2e23314 fix: hw block 50: the private cache folder is readable like /var/cache/steamify (mktemp's 700 hid it from the user) 2026-09-29 19:22:16 +02:00
theupriser e6119ca194 feat: hw block 50: the CEC driver cache (new pin downloads and removes the old file, cache hit, damaged file replaced), offline via file:// 2026-09-29 19:19:52 +02:00
theupriser 6aad17f55c feat: the VMs share the host's cache for Steamify's own downloads (/var/cache/steamify: the CEC driver once for every VM, GitHub rate-limits it) 2026-09-29 19:16:45 +02:00
theupriser e8c57827b1 docs: B1 boot entry check and the ISO's /var/log fix done; CEC driver download (GitHub 429) as the next item 2026-09-29 19:09:16 +02:00
theupriser 4209f9625b docs: TODO: WSL starting with Windows is not Steamify's work (removed R1d) 2026-09-29 18:58:10 +02:00
theupriser 07a7da1d67 docs: TODO: R1d (WSL with Windows) left for now 2026-09-29 18:57:57 +02:00
theupriser a34efb0547 fix: vminstall.sh stops waiting as soon as the live system reports a failed install (it waited 90 minutes) 2026-09-29 18:48:27 +02:00
theupriser 20373c9f05 fix: vmprogress.sh reports a failed install (it stayed at 'install' for 18 minutes) 2026-09-29 18:48:14 +02:00
theupriser 27b7445f63 feat: B1 check: booted through the loader's own EFI boot entry (BootCurrent, a real partition), after every reboot in the boot loader test 2026-09-29 18:13:16 +02:00
theupriser a82053d6bc docs: the first full run on the PC (328 pass, 18 min): TESTPLAN, TODO, AGENTS, wsl-build-host; vmsuite.sh writes its summary into its log 2026-09-29 18:05:10 +02:00
theupriser 37d65fc79d fix: vmprogress.sh: only a new step, a finished job or a FAIL is a change (not every PASS) 2026-09-29 17:50:41 +02:00
theupriser 5b867f8bd2 docs: progress-report: a stopped local command leaves its remote loop running (known_hosts during an install) 2026-09-29 17:31:35 +02:00
theupriser e583494aa6 fix: vmprogress.sh: a new install percentage alone is no change (one line per stage) 2026-09-29 17:23:42 +02:00
theupriser fd9ecf4b9f feat: progress-report skill and scripts/vmprogress.sh (a status line per change); referenced from the other skills and AGENTS.md 2026-09-29 17:07:52 +02:00
theupriser 4cc4046716 feat: vmview.py: a window with a headless VM's screen (QMP screendump every 2 s, view only) 2026-09-29 17:05:17 +02:00
theupriser 9d537406bb fix: vminstall.sh sets up the ISO's loop device under a lock (parallel installs collided) 2026-09-29 16:28:19 +02:00
theupriser 7dd659dc09 fix: vminstall.sh in parallel: a poweroff that drops ssh is no failure, wait only for its own VM 2026-09-29 16:17:53 +02:00
theupriser d5cfadf3bb fix: vminstall.sh never makes root the guest user (WSL runs as root): theupriser then 2026-09-29 15:49:29 +02:00
theupriser cb247b9cc3 docs: wsl-build-host skill, vminstall.sh without udisks (WSL), AGENTS.md/TODO WSL notes corrected
The WSL box has a desktop after all (WSLg windows on Windows), closing the
Arch window shuts it down, and vminstall.sh loop-mounts the ISO with losetup
as root when udisksctl is missing (R1c).
2026-09-29 15:40:03 +02:00
theupriser 6a53c957ba docs: AGENTS.md: state, where the tests run (PC, WSL2) and the ISO problems found 2026-09-29 15:31:57 +02:00
theupriser 63fd11829d docs: the agent hand-off file is AGENTS.md (was CLAUDE.md) 2026-09-29 15:29:49 +02:00
theupriser f03ad74295 docs: CLAUDE.md: this repo takes pushes straight to main 2026-09-29 15:29:08 +02:00
theupriser 0c84b8b926 feat: faster and parallel tests, a remote runner, and a CLAUDE.md hand-off
vmsuite.sh: the ssh-ready snapshot becomes one base image, every block gets a new qcow2 overlay
(no snapshot restore), each suite has its own VM directory and ssh port (2301-2305, boot loaders
2401-2403), the host's package cache is bound over pacman's, VM_MEM defaults to 4G for tests
(run.sh VM_MEM, default 8G). vmtest.sh runs up to MAX_PARALLEL=3 jobs at once, slowest first.
scripts/vmtest-remote.sh runs vmtest.sh on another machine (the PC) over ssh and prints the
summary. CLAUDE.md and the branch cleanup skill now live in the repo.

Not proven as a whole: three VMs at once and the base image work, but a complete parallel run to
the end has not finished yet (P1/P2 in TODO.md); the remote runner and WSL2 are untried (R1-R2).
2026-09-29 15:26:57 +02:00
theupriser ee20bd9825 feat: vmtest.sh runs everything: boot loaders plus the cli, menu, hw, installer and toggles suites
vmtest.sh [--install] [--window] [--screen] [boot|loader|suite...], a detached screen
session with --screen, the summary in ~/vms/last-test.txt, the rows that need a person
listed as SKIP. New: the installer suite (R2.1), the toggles suite (R1.8) and H3 (BIOS
dry-run, per-block '# env:' for the VM start). vmsuite.sh and vmbootloadertest.sh are
executable again.
2026-09-29 14:41:37 +02:00
theupriser 6d264acaf2 feat: the hw suite (H1, H4, H5, R1.7 CEC) and host-side suite blocks
vmsuite.sh runs NN-name.host.sh blocks on the host (reboot, scripts/vmcec.sh) with
prelude-host.sh; every block that applies Steamify says so first (INFO) so the shared
console is not silent for minutes. 68 checks pass.
2026-09-29 14:12:39 +02:00
theupriser b91ef9172e feat: the menu suite (R1.1-R1.6, R1.10) and one shared log Konsole for every test run
Scripted menu input and Steamify's own status functions, journals free of duplicates,
--backend status as JSON, an older item shown as (update). The test scripts append to
$TEST_LOG and share one Konsole (common.sh view_start) instead of opening one each;
vmsuite.sh streams its output (sed -u). 36 checks pass.
2026-09-29 13:53:35 +02:00
theupriser fe5251f758 feat: vmsuite.sh and the cli suite: G1-G3, F1-F7 of TESTPLAN.md as unattended checks
Each block in share/vmtest/<suite>/ starts from a fresh ssh-ready snapshot, gets a
Plasma session environment over ssh (the theme item needs one) and prints PASS/FAIL
lines. ONLY=<prefix> runs one block. 55 checks pass on the plain CachyOS VM.
2026-09-29 11:45:02 +02:00
theupriser 508eb68ed5 docs: vm-install skill, TESTPLAN section B and TODO for the boot loader test and the full automated suite 2026-09-29 11:23:50 +02:00
theupriser c91277abf0 feat: vmtest.sh: the boot loader test for every loader the ISO advertises
One VM per loader, Steam Machine (--fremont): Steamify's state as its menu sees it,
OS name untouched, loader identity, the DKMS modules for every kernel, Steamify's
loader-specific code (hdmi_remove_boot_param), a kernel update, and a reboot into
each of the two kernels. Headless by default with a log Konsole (--window shows
the VMs). Fails when an advertised loader has no test. Limine 41, systemd-boot 40,
GRUB 40 checks pass.
2026-09-29 11:23:50 +02:00
theupriser 8646b3c30a feat: vminstall.sh installs any boot loader and the Steamify ISO's Steamify step, with a package cache
VM_BOOTLOADER=limine|systemd-boot|grub, VM_STEAMIFY (steamify-install runs after
the headless installer, like Calamares does), VM_CACHE (host pacman cache shared
as 9p, default ~/vms/pkg-cache). run.sh --headless / VM_HEADLESS=1: no window.

Fixes found on the way: the live script deadlocked on is-system-running (it is
the running kernel-command-line.service) and now waits for pacman-init; the post
script kept forcing the Plasma login over Steamify's gaming-mode SDDM, gave
systemd-boot no timeout/default (it waited in the menu for ever), and left ufw
blocking the host's ssh; the first-boot check wanted plasmashell in gaming mode.
2026-09-29 11:23:50 +02:00
theupriser fa776b1761 docs: steam-machine-iso: vmreset.sh also stops the ISO VM 2026-09-28 18:27:52 +02:00
theupriser 0ae7cce488 fix: vmreset.sh works when the VM lives in the repo itself (cp of run.sh onto itself failed) 2026-09-28 18:27:27 +02:00
theupriser 7db82b6494 docs: steam-machine-iso: read an installed VM's disk while it runs; serial output 2026-09-28 18:12:18 +02:00
theupriser 52d5021d88 docs: steam-machine-iso: one log Konsole for every build 2026-09-28 17:45:00 +02:00
theupriser f19bcb9764 docs: steam-machine-iso: a package cache for the podman ISO build 2026-09-28 17:42:37 +02:00
theupriser 5954548247 fix: qmptype.py types > < ? { } 2026-09-28 17:37:51 +02:00
theupriser bc06bc2534 feat: vmisobuild.sh and the steam-machine-iso skill run build-calamares-modules.sh 2026-09-28 16:41:22 +02:00
theupriser 0052f1ba22 docs: note token-cheap ISO-VM testing (serial log, no screenshot polling) 2026-09-28 15:35:25 +02:00
theupriser 61442d7ef8 docs: packagechooser multi-select needs Shift/Ctrl-click 2026-09-28 13:45:09 +02:00
theupriser 6d73d49477 docs: steam-machine-iso: run Calamares in its own Konsole tab; watch for the live keyboard layout switching away from US 2026-09-28 13:42:23 +02:00
theupriser fe249e5108 docs: correction - packagechooser mode:optionalmultiple DOES multi-select (verified from global storage, not misleading label text) 2026-09-28 13:30:09 +02:00
theupriser 4398a2b85f docs: packagechooserq isn't installed on this Calamares build; packagechooser's mode:optionalmultiple is single-select regardless; QMP/Calamares live-testing gotchas 2026-09-28 13:24:36 +02:00
theupriser 081080b5f5 docs: ISO builds use 75% of host cores, not all-but-2 2026-09-28 13:04:04 +02:00
theupriser b6b35d41f1 docs: TESTPLAN: 2.8.0 --defaults --list tested 2026-09-28 12:44:50 +02:00
theupriser 3ccbb62bd2 feat: vmisobuild.sh (ISO build in the VM, package cache per VM), vmisoboot.sh (ISO VM for Calamares), qmpshot.py/qmpclick.py; run.sh VM_CACHE/VM_CPUS/VM_NOGL/VM_PORT 2026-09-28 11:40:21 +02:00
theupriser fec0ce5b8e docs: TESTPLAN: 2.7.0's Steam Machine checks carried over from 2.6.0 (code unchanged) 2026-09-28 10:56:17 +02:00
theupriser d0038916e9 docs: 2.7.0 regression results on the new VM; the first login's app is the newest release 2026-09-28 10:53:44 +02:00
theupriser 4905bba1f6 docs: vminstallsim.sh example uses --options 2026-09-28 10:08:27 +02:00
theupriser 60a2698d02 feat: scripts/vminstall.sh installs an ssh-ready test VM unattended; TESTPLAN.md; vm-install skill 2026-09-28 10:07:13 +02:00
theupriser 21547dd7bc docs: steam-machine-iso: one build at a time; vminstallsim.sh passes --defaults options 2026-09-27 22:00:32 +02:00
theupriser 96cae35c6b docs: steam-machine-iso: build-live-modules.sh in the build, the ISO's new name 2026-09-27 21:26:45 +02:00
theupriser e1dd75875b docs: steam-machine-iso: the Steamify step is added by calamares-online.sh 2026-09-27 20:06:26 +02:00
theupriser f8aac02d0a docs: steam-machine-iso: live-session quirks; scripts/qmptype.py types into the VM 2026-09-27 20:01:41 +02:00
theupriser 8d5cdaea99 docs: steam-machine-iso: the harmless chown error, the ISO test VM 2026-09-27 19:54:30 +02:00
theupriser f8e2473afa feat: steam-machine-iso skill (ISO build in podman on the Steam Machine, installer simulation, first-login test) and scripts/vminstallsim.sh 2026-09-27 19:53:23 +02:00
theupriser 088ee83350 VM on the Steam Machine: Arch OVMF paths, visible tests, and the steam-machine-testing skill 2026-09-27 17:00:10 +02:00
theupriser d626aafd84 docs: VM skill up to date with the menu (sub-options, HDMI-CEC, kernel pin) and today's pitfalls 2026-09-25 13:07:02 +02:00
theupriser 6da46fc10f docs: The test VM needs an x86_64 Linux host 2026-09-25 13:05:15 +02:00
theupriser 0464ede893 chore: Remove the Windows scripts; the test VM runs on Linux 2026-09-25 13:04:20 +02:00
theupriser 4aeb3de43f docs: run.sh --amd (venus on the AMD iGPU); gaming mode can't render in the VM, all tried 2026-09-25 12:54:27 +02:00
theupriser 04204f1b9d feat: vmcec.sh tests HDMI-CEC against a fake TV; vmwake.sh wakes the VM over QMP
run.sh opens a QMP socket; the skill documents the CEC test and its gotchas.
2026-09-25 11:14:00 +02:00
theupriser e04db1ec5f docs: VM skill: menu with sub-options, HDMI-CEC and faking it with vivid 2026-09-25 09:12:12 +02:00
theupriser a8d7554dce Merge pull request #1 from theupriser/feat/visible-vm-runs
Feat/visible vm runs
2026-09-24 13:09:16 +02:00
44 changed files with 108 additions and 1802 deletions

No files matched your search

-10
View File
@@ -49,16 +49,6 @@ against a VM that gets reinstalled saves the live ISO's host key in `$VM_DIR/kno
install then hangs at its first-boot ssh check ("REMOTE HOST IDENTIFICATION HAS CHANGED"). Never install then hangs at its first-boot ssh check ("REMOTE HOST IDENTIFICATION HAS CHANGED"). Never
ssh to a VM with its `VM_DIR` settings while it installs; fix it with `ssh-keygen -R "[localhost]:<port>" -f $VM_DIR/known_hosts`. ssh to a VM with its `VM_DIR` settings while it installs; fix it with `ssh-keygen -R "[localhost]:<port>" -f $VM_DIR/known_hosts`.
Monitor rules learned on 2026-09-29: **one monitor per job, stop the old one** (`TaskStop`) when a new run
starts, or every run is reported twice; a job that ends by itself (the ISO build on Gitea, `vmtest.sh`) is
best watched with a loop that exits on its terminal states and prints only *changes* (a new step, a finished
job, a FAIL, a new HTTP status), never on a percentage or a PASS count alone. For a Gitea Actions run, poll its
public log (`.../actions/runs/<run>/jobs/<job>/logs`, see `steamify-iso-release`) and grep for the step names.
A chain across systems (Steamify release -> ISO tag -> mirror sync -> Gitea build -> download link) is one
monitor that says each link when it happens and ends on the real check (the download URL answers 200).
When the tool that runs commands stops answering (auto-mode classifier "no verdict"), retry once, then say so
and carry on with reading/local work: nothing running on the PC is affected.
## What the user sees on the PC ## What the user sees on the PC
- **Their terminal**: stream the running job's log into it (the `showlog` unit, `wsl-build-host` - **Their terminal**: stream the running job's log into it (the `showlog` unit, `wsl-build-host`
+8 -11
View File
@@ -1,13 +1,10 @@
--- ---
name: steam-machine-iso name: steam-machine-iso
description: Use when working on the Steam Machine CachyOS ISO (repo steamify-cachyos-live-iso) or Steamify's install-time mode (steamify.sh --defaults, --first-login) - building the ISO in podman (in the test VM, or on the Steam Machine), the Calamares Steamify step, simulating the installer in the test VM, and testing the first desktop login. description: Use when working on the Steam Machine CachyOS ISO (repo steammachine-cachyos-live-iso) or Steamify's install-time mode (steamify.sh --defaults, --first-login) - building the ISO in podman (in the test VM, or on the Steam Machine), the Calamares Steamify step, simulating the installer in the test VM, and testing the first desktop login.
--- ---
# Steam Machine ISO and Steamify's install-time mode # Steam Machine ISO and Steamify's install-time mode
Releasing the ISO (GitHub tag, built and attached on the Gitea mirror, naming, runner and server settings) is
the `steamify-iso-release` skill; this one is for building and testing by hand.
A CachyOS live ISO for the Steam Machine that installs Steamify's default A CachyOS live ISO for the Steam Machine that installs Steamify's default
setup during the install. Two repos: setup during the install. Two repos:
@@ -18,7 +15,7 @@ setup during the install. Two repos:
first login, and `lib/first-login.sh` leaves a one-time autostart first login, and `lib/first-login.sh` leaves a one-time autostart
(`--first-login`: single user's launcher on the new layout, then the app, (`--first-login`: single user's launcher on the new layout, then the app,
next to CachyOS Hello). See AGENTS.md there. next to CachyOS Hello). See AGENTS.md there.
- `steamify-cachyos-live-iso` (fork of CachyOS-Live-ISO), branch work - `steammachine-cachyos-live-iso` (fork of CachyOS-Live-ISO), branch work
never on master: never on master:
- `archiso/airootfs/usr/local/bin/calamares-online.sh` (the installer - `archiso/airootfs/usr/local/bin/calamares-online.sh` (the installer
launcher) copies CachyOS's `settings_online.conf` over launcher) copies CachyOS's `settings_online.conf` over
@@ -114,14 +111,14 @@ VM: note what differs here. In the VM (`vm_ssh`, visibly in a Konsole there):
```bash ```bash
sudo pacman -S --needed --noconfirm podman git sudo pacman -S --needed --noconfirm podman git
git clone https://github.com/theupriser/steamify-cachyos-live-iso ~/projects/steamify-cachyos-live-iso git clone https://github.com/theupriser/steammachine-cachyos-live-iso ~/projects/steammachine-cachyos-live-iso
sudo mount -t 9p -o trans=virtio,version=9p2000.L repo /mnt # the Steamify checkout (REPO) sudo mount -t 9p -o trans=virtio,version=9p2000.L repo /mnt # the Steamify checkout (REPO)
cd ~/projects/steamify-cachyos-live-iso && git checkout master && ./steamify-prepare.sh /mnt cd ~/projects/steammachine-cachyos-live-iso && git checkout feat/steamify && ./steamify-prepare.sh /mnt
``` ```
then the same `podman run` as below (paths in the VM; `~/projects/iso-build.log`). then the same `podman run` as below (paths in the VM; `~/projects/iso-build.log`).
The ISO (~3.2 GB) fits the 60G disk; copy it out with The ISO (~3.2 GB) fits the 60G disk; copy it out with
`vm_scp "$VM_USER@$VM_HOST:~/projects/steamify-cachyos-live-iso/out/desktop/*.iso" .` `vm_scp "$VM_USER@$VM_HOST:~/projects/steammachine-cachyos-live-iso/out/desktop/*.iso" .`
(`scripts/common.sh`), then install it with `scripts/vminstall.sh --iso`. (`scripts/common.sh`), then install it with `scripts/vminstall.sh --iso`.
Give the VM more room with a bigger disk if `out/`, `build/` and the package Give the VM more room with a bigger disk if `out/`, `build/` and the package
cache grow over several builds (`sudo rm -rf build out` between builds). cache grow over several builds (`sudo rm -rf build out` between builds).
@@ -130,11 +127,11 @@ cache grow over several builds (`sudo rm -rf build out` between builds).
Only podman is installed on the Steam Machine itself; the build tools live in Only podman is installed on the Steam Machine itself; the build tools live in
the container. Rootful (loop devices, mounts), so `sudo podman ps` shows it, the container. Rootful (loop devices, mounts), so `sudo podman ps` shows it,
not a rootless podman GUI. Clone: `~/projects/steamify-cachyos-live-iso`. not a rootless podman GUI. Clone: `~/projects/steammachine-cachyos-live-iso`.
```bash ```bash
cd ~/projects/steamify-cachyos-live-iso cd ~/projects/steammachine-cachyos-live-iso
git checkout master && ./steamify-prepare.sh ~/projects/steamify-cachyos git checkout feat/steamify && ./steamify-prepare.sh ~/projects/steamify-cachyos
sudo rm -rf build out sudo rm -rf build out
systemd-run --user --collect -q -u isobuild-$(date +%s) --working-directory=$PWD bash -c \ systemd-run --user --collect -q -u isobuild-$(date +%s) --working-directory=$PWD bash -c \
"sudo podman run --rm -t --pids-limit=-1 --ulimit nofile=65536:65536 --privileged --network=host \ "sudo podman run --rm -t --pids-limit=-1 --ulimit nofile=65536:65536 --privileged --network=host \
@@ -6,17 +6,12 @@ description: Clean up git branches in steamify-cachyos (and sibling steamify rep
# Steamify branch cleanup # Steamify branch cleanup
Rules: Rules:
- ALWAYS keep `main`. A `release/X.Y.Z` branch is deleted (local and remote) once its release is published and its tip is contained in a tag or in `main` (the user said so on 2026-10-02: the tags keep the history). Check each one first (`git tag --contains <tip>`, `git merge-base --is-ancestor`): a release branch with a commit that is in no tag and not in `main` (`release/2.9.2`: a docs commit) is kept and reported. The open release's branch stays until its PR is merged. - ALWAYS keep `main` and every `release/X.Y.Z` branch (local and remote). Never delete them.
- Delete `feature/*` and `bugfix/*` branches (local and remote) only when merged into `origin/main` or the current `origin/release/*` branch. - Delete `feature/*` and `bugfix/*` branches (local and remote) only when merged into `origin/main` or the current `origin/release/*` branch.
- Delete local branches whose remote is gone (`[origin/...: gone]`) only if they are `feature/*` or `bugfix/*`; local stale `release/*` branches are kept unless the user says otherwise. - Delete local branches whose remote is gone (`[origin/...: gone]`) only if they are `feature/*` or `bugfix/*`; local stale `release/*` branches are kept unless the user says otherwise.
- Never touch unmerged branches; list them and report instead. - Never touch unmerged branches; list them and report instead.
- Never commit or merge to main. - Never commit or merge to main.
Merging a feature/bugfix PR yourself (into a release branch, or `master` in the ISO repo): delete the
branch at once, remote and local (`gh pr merge N --merge --delete-branch`, then `git branch -D`, `git fetch
--prune`); the user asked for this on 2026-09-29. Release branches are deleted after their release (see the rules above; the tag keeps the history). Unmerged old branches (`feat/vram-booster`, `refactor/qml-screens`, `backup/*`) are reported, not
touched. The GitHub-side `release delete <tag> --cleanup-tag` is how test ISO releases go.
Steps: Steps:
1. `git fetch --all --tags --prune` 1. `git fetch --all --tags --prune`
2. `git checkout main && git merge --ff-only origin/main` 2. `git checkout main && git merge --ff-only origin/main`
@@ -1,134 +0,0 @@
---
name: steamify-iso-release
description: Use when releasing, debugging or changing how the Steamify ISO is built and published - the GitHub tag that names it, the Gitea mirror (git.upriser.nl) that builds and attaches the 3.2 GB ISO, the Steamify workflows that start it, and every trap met while setting it up (413, privileged runner, sudo dropping variables, mirror syncs deleting tags, upload-artifact@v3, GitHub 429). Also for the Gitea side of steamify-cachyos' own releases.
---
# Steamify ISO release pipeline (set up 2026-09-29, first ISO out the same night)
Tested end to end with the first naming (`v2.9.3-dev.2026.09.29-2044`); since 2026-09-30 the names are by the day, like
CachyOS (below; workflow changes in ISO repo PR #15, steamify-cachyos PR #69). The workflows always run from the ISO
repo's `master`; `feat/steamify` is no longer needed.
## The flow (one tag names everything)
1. **GitHub, `iso-1-github-tag.yml`** (ISO repo `theupriser/steamify-cachyos-live-iso`, `workflow_dispatch` only, input
`kind`: `release`, `dev` or `auto` = release on master, dev elsewhere): takes Steamify's newest release (`X.Y.Z`),
the day **now in UTC** and makes an **annotated tag** plus a GitHub release with the changelog notes and the
*direct* download link on Gitea. Like CachyOS by the day, no time: real `vX.Y.Z-YYMMDD`, dev (pre-release)
`vX.Y.Z-dev-YYMMDD`. GitHub is the only place that reads a clock.
- **One ISO per day and kind** (space): a second build the same day deletes the earlier GitHub release and tag,
syncs the mirror and waits until the mirror dropped the tag (it drops tag, release and ISO together), then tags
again (else the new tag is only an update there and starts no build).
- **Retention:** at most 10 dev and 10 real releases (`gh release delete --cleanup-tag`); the mirror follows.
2. **The mirror syncs** (git.upriser.nl is a pull mirror of GitHub with **no interval of its own**: every repo's
`git-upriser-sync.yml` calls its mirror-sync API on every branch and tag push, and iso-1's last step after the tag;
secrets `GIT_UPRISER_URL`, `GIT_UPRISER_TOKEN`): the tag arrives.
3. **Gitea, `iso-2-gitea-build.yml`** (`workflow_dispatch` only, started by iso-1's last step through Gitea's dispatch API with
`{"ref": "refs/tags/<tag>"}` (the **full** ref: a bare tag name answers 404) and the input `steamify_ref`; a tag push
starts nothing; skipped on GitHub by `github.server_url`): parses the
tag, builds the ISO on the runner with exactly that Steamify (`STEAMIFY_VERSION`), the tag's time (label) and
the tag without its `v` (`STEAMIFY_ISO_VERSION`: file name, boot menu, `/etc/steammachine-iso-build`), then
attaches ISO + `.sha256` + `.sha1` + `.pkgs.txt` to the mirror's release for that tag
(`akkuman/gitea-release-action`, the run's own token). A leftover release of the same tag is replaced. Two jobs,
build and release; no tests there (the runner is small): VM tests run locally, `scripts/vmtest.sh`.
4. **Trigger:** by hand (*Actions -> ISO 1/2 · Tag and release (GitHub) -> Run workflow*, or
`gh workflow run iso-1-github-tag.yml -R theupriser/steamify-cachyos-live-iso --ref master -f kind=dev|release`), or
by a new version: `steamify-cachyos`' `bundle.yml` step "Start the Steamify ISO's release" (a version published
from `main` -> `kind=release`, an unreleased version pushed to `release/**` -> `kind=dev`; secret
`ISO_DISPATCH_TOKEN` there: fine-grained token, only the ISO repo, *Actions: read and write*; without it the
step is skipped). A push does **not** release (a build is 20 minutes and 3.2 GB).
5. Names, for tag `v2.9.6-dev-260930`: file `steamify-cachyos-2.9.6-dev-260930-x86_64.iso`,
label `STEAMIFY_2_9_6_260930` (ISO 9660: 32 chars, `A-Z 0-9 _`). By hand, without a tag: `local`
(`steamify-cachyos-local-x86_64.iso`, `STEAMIFY_<version>_LOCAL`). The build reads no clock.
Total from a Steamify release to the ISO on Gitea: about 20 minutes (release, tag, sync in seconds, build ~18).
## Which Steamify goes on the ISO, and the build badge
- `iso-1` input **`steamify_ref`** (a Steamify branch or tag; empty = the newest *published* release). A dev ISO from a
release branch passes the branch (`bundle.yml` does), so `release/2.9.6` gives a 2.9.6 ISO before `v2.9.6` exists;
without it every dev ISO carries the last published version (it kept producing 2.9.5). iso-1 reads `VERSION` from
that ref's `steamify.sh`; iso-2 clones the ref and runs `steamify-prepare.sh <checkout>`.
- **Build badge in the GitHub release:** iso-1 writes `![ISO build](...running-yellow)` at the top of the notes; iso-2's
last job `report` (`if: always()`) turns it into succeeded / failed / cancelled (shields.io static badge, linked to the
Gitea run) through the GitHub API. Secret **`GH_RELEASE_TOKEN`** on the Gitea repo (Settings -> Actions -> Secrets):
fine-grained GitHub token, only `steamify-cachyos-live-iso`, *Contents: read and write*; expires, so renew it. Without
it the badge stays "running". Gitea's own `badge.svg` says "no status" for these runs (they run on a tag ref).
- Secrets in all: GitHub `steamify-cachyos`: `ISO_DISPATCH_TOKEN`, `GIT_UPRISER_URL`, `GIT_UPRISER_TOKEN`; GitHub ISO
repo: `GIT_UPRISER_URL`, `GIT_UPRISER_TOKEN`; Gitea ISO repo: `GH_RELEASE_TOKEN`.
## Why it is split like this (learned the hard way)
- **A release made only on the mirror disappears** at the next sync: the pull mirror removes tags GitHub doesn't
have, and the release goes with it (one that has files may linger, tagless, and sorts oddly: delete it). A
release on a tag that **comes from GitHub survives** every sync, files included (tested with a 0-byte asset).
Hence: GitHub makes the tag, Gitea only attaches the file. You can't delete a tag on a mirror by hand either.
- GitHub releases take **2 GB per file**; the ISO is 3.2 GB. So GitHub gets notes and a link, Gitea the file.
- **A replaced tag starts no run on the mirror.** One ISO per day and kind: iso-1 deletes the day's GitHub release and
tag and tags again; the mirror takes the new tag object (same name) but Gitea starts nothing and keeps the old release
(same id, new creation time) with the old ISO. So the build is started by dispatch, after the mirror has the new tag
object (`GET .../tags/<tag>` id == `git rev-parse refs/tags/<tag>`); iso-2 deletes a leftover release of the tag before
it publishes. Retention: at most 10 dev + 10 real releases.
- **Gitea runner and parallel jobs:** it ignores `max-parallel`, and jobs that start in the same second clone the same
action into one shared folder and break each other (`actions/cache` failed): keep parallel jobs free of `uses:`.
(The VM tests no longer run there at all: they run locally, `scripts/vmtest.sh`.)
- **Annotated tags**: a lightweight tag has only its commit's date, so tags on the same commit sort randomly
on Gitea. UTC is fine (the release name says "UTC"; it is 2 h behind Dutch summer time, and nobody cared).
- The GitHub release's link is built from the tag alone (`.../releases/download/<tag>/steamify-cachyos-<tag
without v>-x86_64.iso`), so the Gitea file name must be that: check it with `curl -I -L` (the monitor in the
session did: HTTP 200 = the whole chain is right).
## Gitea side: what the runner and the server need
- **Runner** (act_runner in Docker Compose, network `gitea_gitea`): `config.yaml` -> `container: privileged: true`
(the workflow's `options: --privileged` is **ignored** otherwise; symptom: `mount: .../airootfs/proc: permission
denied`, `failed to setup chroot`; the job log shows `Privileged:false`). ~20 GB free disk.
- **Upload limit: `[repository.release] FILE_MAX_SIZE = 10240`** in `app.ini` (MB; default 2048), restart Gitea.
`[attachment] MAX_SIZE` is for issues, **not** releases: symptom `status: 413` on `.../releases/<id>/assets`.
The release is created *before* the upload, so a failed run leaves an empty release: delete it.
- Logs of a public repo's run are readable without login: `.../actions/runs/<run>/jobs/<job>/logs` (job numbers
count up; re-runs get a new job number). Good for a monitor; no `gh` for Gitea.
- Mirror sync is set to 10 min by the user with their own token; "Synchronize Now" or the API
(`POST /api/v1/repos/<repo>/mirror-sync`) for now. `GIT_UPRISER_TOKEN` (secret in the ISO repo) would make GitHub
trigger it; not needed.
- **Build traps in the container:** `sudo mkarchiso` in `util-iso.sh` **drops environment variables**:
it needs `sudo --preserve-env=STEAMIFY_ISO_VERSION,STEAMIFY_BUILD_STAMP` (the first Gitea ISO came out as
`-local-`). Step names are evaluated **before** earlier steps set env: don't put `${{ env.X }}` in a name.
Lots of `error: command failed to execute correctly` (pacman hooks wanting systemd) are harmless.
The bash test of the naming must go through `sudo` too, not only call `profiledef.sh` directly.
- Kernel: the ISO is built from the generic `x86_64` repo (must boot on any PC), which lags the `v3/v4/znver4`
repos by a day or so: 7.2.7 on the ISO while installed systems get 7.2.8. Normal, not a bug: the installer
switches to the CPU's optimised repo (znver4 on the 9800X3D) and the first update brings the newer kernel.
## Steamify's own workflow (`steamify-cachyos/.github/workflows/bundle.yml`), one file for both servers
- Build + check run everywhere. shellcheck through `ludeeus/action-shellcheck@2.0.0` (brings its own binary:
act_runner's image has none; GitHub's has). The artifact: `actions/upload-artifact@v4` on GitHub,
`christopherhx/gitea-upload-artifact@v4` elsewhere. **Never write `upload-artifact@v3`**: GitHub fails the
whole workflow for merely mentioning a deprecated version, even in a skipped step.
- Publishing: GitHub release (`gh`, tag + `latest`) only on GitHub; on Gitea the release is `akkuman/gitea-release-action`
with a Gitea-API version check (`RELEASE_TOKEN` secret optional, else the run's token). Steps are guarded by
`github.server_url == 'https://github.com'` (or `!=`). YAML check without PyYAML on the Mac:
`ruby -ryaml -e 'YAML.load_file(...)'`.
- Steamify releases still follow the AGENTS.md flow (`release/X.Y.Z`, `bugfix/`/`feature/` branch, PR into the
release branch, merged and **deleted** by the agent, the release PR into `main` is the user's). Even a CI-only
change is a release (2.9.2, 2.9.3).
## GitHub rate limit that started this (HTTP 429)
`raw.githubusercontent.com` refused Valve's CEC driver source after a day of test installs (each CEC turn-on
downloaded it again; the limit is per address, ~1 per 5 minutes seen). Fixed in Steamify 2.9.1: the driver is
cached in `/var/cache/steamify/cros-ec-cec-<sha256:12>.c` (named after its checksum: a new pin downloads once and
deletes the old file; a damaged file is fetched again). The test VMs share the host's `~/vms/pkg-cache/steamify`
(bound over `/var/cache/steamify` by `vmsuite.sh`, `vmbootloadertest.sh`, `vminstall-live.sh`); hw block 50 tests
the cache offline (`file://`). The source repo `evlaV/linux-integration` is an unofficial mirror without releases:
if it ever vanishes, ship the file with Steamify.
## Checklist for a release
1. Steamify released? (`gh release list -R theupriser/steamify-cachyos`). The ISO gets the newest.
2. `gh workflow run iso-1-github-tag.yml -R theupriser/steamify-cachyos-live-iso --ref master -f kind=dev` (test) or
`-f kind=release`. Normally `bundle.yml` does this when a version is published (a release branch push -> dev).
3. Watch: tag on Gitea (`.../api/v1/repos/theupriser/steamify-cachyos-live-iso/tags`), the run's log (above),
then `curl -I -L` the GitHub link. Retention deletes the oldest automatically (10 dev, 10 real). By hand: `gh release delete <tag>
--cleanup-tag -y`; the sync removes the tag on Gitea; delete a leftover tagless Gitea release by hand.
+1 -18
View File
@@ -138,14 +138,6 @@ lose the executable bit (`chmod --reference` or call scripts with `bash`); a sui
be a `.host.sh` block; ssh has no Plasma session, the prelude exports one; a one-off network failure can fail be a `.host.sh` block; ssh has no Plasma session, the prelude exports one; a one-off network failure can fail
an install step (LED driver from the AUR), rerun the block before suspecting Steamify. an install step (LED driver from the AUR), rerun the block before suspecting Steamify.
Gotchas found on 2026-09-29: never ssh to a VM with its `VM_DIR` settings while it installs (a leftover loop, also
one started from a laptop command that was stopped, saves the *live ISO's* host key in `$VM_DIR/known_hosts` and
the install then hangs at its first-boot ssh check: `ssh-keygen -R "[localhost]:<port>" -f $VM_DIR/known_hosts`);
`vminstall.sh` now never uses root as the guest user (WSL runs as root: `theupriser`), takes the ISO's loop
device under a lock (parallel installs collided), tolerates the poweroff dropping ssh, waits only for its own
qemu and stops at once when the live system reports `== failed: ...`. A failed install used to look like
"still installing" for 90 minutes: `vmprogress.sh` shows `INSTALL FAILED: <why>`.
Following a run for the user (a table per VM with a bar and a total, only on a change; their terminal and Following a run for the user (a table per VM with a bar and a total, only on a change; their terminal and
the VM's screen): the `progress-report` skill, with `scripts/vmprogress.sh` as the monitor. the VM's screen): the `progress-report` skill, with `scripts/vmprogress.sh` as the monitor.
@@ -157,16 +149,7 @@ loader has no test). One VM per loader in `~/vms/bl-<loader>` (`--install` build
Steamify ISO, ~8 min each, ~3 min per test), headless with a Konsole on the logs (`--window` shows the VMs), Steamify ISO, ~8 min each, ~3 min per test), headless with a Konsole on the logs (`--window` shows the VMs),
one summary line per loader, `~/vms/bl-<loader>.test.log`, exit status = failed checks. Checks live in one summary line per loader, `~/vms/bl-<loader>.test.log`, exit status = failed checks. Checks live in
`share/bootloader-test/*.sh` (one PASS/FAIL line each); the driver is `scripts/vmbootloadertest.sh`. `share/bootloader-test/*.sh` (one PASS/FAIL line each); the driver is `scripts/vmbootloadertest.sh`.
Last result (2026-09-29, ISO with Steamify 2.9.1): Limine 43, systemd-boot 42, GRUB 42 checks, 0 failed. Last result: Limine 41, systemd-boot 40, GRUB 40 checks, 0 failed.
The boot entry check (B1, `boot-check.sh`, after every reboot): the system booted through the loader's **own**
EFI entry (`BootCurrent`, not `auto_created_boot_option`, a real partition GUID not `HD(0,GPT,0000...)`). It
found that the installer never registered systemd-boot (bootctl skips the EFI variables in a chroot): OVMF then
tried PXE/HTTP boot/EFI shell first, 4-5 minutes per boot, or "no bootable device". The ISO's `steamify-install`
now runs `efibootmgr` from the live system (a real partition, first in the order) and mounts the new system's
`/var/log` (`@log` subvolume) first so its logs (`steamify-install.log`, `steamify-bootentry.log`) survive.
To read a VM's boot entries without booting it: copy `vars.fd`, `virt-fw-vars -i <copy> --print` (package
`virt-firmware`). The power-off check accepts "loaded" or "tried at boot: No such device" (journal, not dmesg:
the VM has no AMD GPIO controller, the module refuses on purpose).
What only shows up per loader: Limine keeps its images under `/boot/<machine-id>/<kernel>/` and copies one What only shows up per loader: Limine keeps its images under `/boot/<machine-id>/<kernel>/` and copies one
only when its content changed (same version = untouched), and `remember_last_entry: yes` overrides only when its content changed (same version = untouched), and `remember_last_entry: yes` overrides
`default_entry` (the test turns it off and restores it); GRUB's other kernel is picked with `default_entry` (the test turns it off and restores it); GRUB's other kernel is picked with
+6 -20
View File
@@ -1,6 +1,6 @@
--- ---
name: wsl-build-host name: wsl-build-host
description: Use when building or testing the Steam Machine ISO, or running the VM tests (vmtest.sh), on the user's Windows PC - Arch Linux in WSL2 (`ssh wsl`), with podman, QEMU + KVM and WSLg windows on the Windows desktop. Covers access, syncing working trees from the laptop, the podman ISO build as root, VM windows vs headless, showing output in the user's WSL terminal, freeing disk space, and the WSL pitfalls (closing the window kills it, no GPU passthrough). description: Use when building or testing the Steam Machine ISO, or running the VM tests (vmtest.sh), on the user's Windows PC - Arch Linux in WSL2 (`ssh wsl`), with podman, QEMU + KVM and WSLg windows on the Windows desktop. Covers access, syncing working trees from the laptop, the podman ISO build as root, VM windows vs headless, showing output in the user's WSL terminal, and the WSL pitfalls (closing the window kills it, no udisks, no GPU passthrough).
--- ---
# WSL build host (Arch on WSL2, the Windows PC) # WSL build host (Arch on WSL2, the Windows PC)
@@ -52,7 +52,7 @@ pulls the pushed branch on the remote, so it needs a GitHub key there first
```bash ```bash
cd ~/Projects && rsync -az --exclude out --exclude build \ cd ~/Projects && rsync -az --exclude out --exclude build \
steamify-cachyos-live-iso steamify-cachyos steamify-cachyos-dev wsl:projects/ steammachine-cachyos-live-iso steamify-cachyos steamify-cachyos-dev wsl:projects/
ssh wsl 'find ~/projects -name __pycache__ -prune -exec rm -rf {} +; chown -R root: ~/projects' ssh wsl 'find ~/projects -name __pycache__ -prune -exec rm -rf {} +; chown -R root: ~/projects'
``` ```
@@ -63,16 +63,14 @@ Both fixups are needed: rsync keeps the laptop's uid (git then refuses with
## Building the ISO (~8 minutes) ## Building the ISO (~8 minutes)
```bash ```bash
ssh wsl 'cd ~/projects/steamify-cachyos-live-iso && ./steamify-prepare.sh ~/projects/steamify-cachyos' ssh wsl 'cd ~/projects/steammachine-cachyos-live-iso && ./steamify-prepare.sh ~/projects/steamify-cachyos'
ssh wsl 'cd ~/projects/steamify-cachyos-live-iso && rm -rf build out && systemd-run --collect -q -u isobuild-$(date +%s) --working-directory=$PWD bash -c "podman run --rm -t --pids-limit=-1 --ulimit nofile=65536:65536 --privileged --network=host -v /root/projects/iso-cache:/var/cache/pacman/pkg -v $PWD:/iso -w /iso docker.io/cachyos/cachyos:latest bash -c '\''pacman-key --init && pacman-key --populate && pacman -Syu --noconfirm --needed archiso mkinitcpio-archiso git squashfs-tools grub sudo && ./build-live-modules.sh && ./build-calamares-modules.sh && { ./buildiso.sh -p desktop -w || ./buildiso.sh -p desktop -c -w; }'\'' > /root/projects/iso-build.log 2>&1"' ssh wsl 'cd ~/projects/steammachine-cachyos-live-iso && rm -rf build out && systemd-run --collect -q -u isobuild-$(date +%s) --working-directory=$PWD bash -c "podman run --rm -t --pids-limit=-1 --ulimit nofile=65536:65536 --privileged --network=host -v /root/projects/iso-cache:/var/cache/pacman/pkg -v $PWD:/iso -w /iso docker.io/cachyos/cachyos:latest bash -c '\''pacman-key --init && pacman-key --populate && pacman -Syu --noconfirm --needed archiso mkinitcpio-archiso git squashfs-tools grub sudo && ./build-live-modules.sh && ./build-calamares-modules.sh && { ./buildiso.sh -p desktop -w || ./buildiso.sh -p desktop -c -w; }'\'' > /root/projects/iso-build.log 2>&1"'
``` ```
Wait in one background command, not a polling loop: Wait in one background command, not a polling loop:
`ssh wsl 'until ! podman ps -q | grep -q .; do sleep 20; done; tail -c 1500 ~/projects/iso-build.log; ls -la ~/projects/steamify-cachyos-live-iso/out/desktop/'`. `ssh wsl 'until ! podman ps -q | grep -q .; do sleep 20; done; tail -c 1500 ~/projects/iso-build.log; ls -la ~/projects/steammachine-cachyos-live-iso/out/desktop/'`.
Never start a build while `podman ps` shows one. Output: Never start a build while `podman ps` shows one. Output:
`/root/projects/steamify-cachyos-live-iso/out/desktop/steamify-cachyos-local-x86_64.iso` (a build by hand `/root/projects/steammachine-cachyos-live-iso/out/desktop/*.iso` (from
has no release tag, so it's named `local`, label `STEAMIFY_<version>_LOCAL`; releases are built on the Gitea
mirror from a GitHub tag, see the ISO repo's `iso-1-github-tag.yml`) (from
Windows Explorer: `\\wsl$\<distro>\root\projects\...`). The trailing Windows Explorer: `\\wsl$\<distro>\root\projects\...`). The trailing
`chown: missing operand` / "unknown error" is harmless. mksquashfs shows no `chown: missing operand` / "unknown error" is harmless. mksquashfs shows no
progress in the log; the growing `build/iso/arch/x86_64/airootfs.sfs` progress in the log; the growing `build/iso/arch/x86_64/airootfs.sfs`
@@ -96,18 +94,6 @@ Windows desktop. SSH sessions and units lack the variables, so set them:
run.sh's default virgl (`virtio-vga-gl`, `gl=on`) renders through WSLg's run.sh's default virgl (`virtio-vga-gl`, `gl=on`) renders through WSLg's
d3d12 Mesa on it, but QMP screenshots then give "no surface". d3d12 Mesa on it, but QMP screenshots then give "no surface".
## Freeing disk space
Everything here is re-creatable (VMs, `~/vms/pkg-cache`, `~/projects/iso-cache`, `build/`, the podman image):
`rm -rf` them when the VMs aren't running (`pgrep -f "^[q]emu"`), `podman system prune -a -f`,
`pacman -Scc --noconfirm`, `fstrim -av`. That freed 68 GB inside WSL (81 -> 13 GB), but Windows only gets it
back when the vhdx is sparse: in an admin PowerShell `wsl --shutdown`, `wsl --manage archlinux --set-sparse
true` (seconds), reopen the Arch window, `fstrim -av` again; else `compact vdisk` in diskpart. A rebuild costs
the caches: a full `vmtest.sh --install` (three loader VMs ~12 min in parallel, the suite VM ~12 min from a
CachyOS ISO the user keeps in `/root/`), and an ISO build ~6-8 min. Don't `wsl --shutdown` while a job runs.
Hand-built ISOs are called `steamify-cachyos-local-x86_64.iso`; releases are built on the Gitea mirror
(`steamify-iso-release` skill). Windows starting WSL at logon is the user's own business, not Steamify's.
## Running the tests here ## Running the tests here
`scripts/vmtest.sh --screen` from `~/projects/steamify-cachyos-dev` (as root, `scripts/vmtest.sh --screen` from `~/projects/steamify-cachyos-dev` (as root,
-26
View File
@@ -1,26 +0,0 @@
name: Sync git.upriser.nl mirror
# Trigger the Gitea pull-mirror immediately instead of waiting for its interval,
# so Gitea builds/tests right after any branch or tag lands on GitHub (the mirror has no sync interval of its own).
on:
push:
branches: ['**']
tags: ['*']
workflow_dispatch:
jobs:
sync:
# Gitea also reads .github/workflows on the mirror; only run on GitHub
if: github.server_url == 'https://github.com'
runs-on: ubuntu-latest
steps:
- name: Trigger mirror-sync
env:
GITEA_URL: ${{ secrets.GIT_UPRISER_URL }}
GIT_UPRISER_TOKEN: ${{ secrets.GIT_UPRISER_TOKEN }}
# Gitea "owner/repo"; defaults to the same path as on GitHub
GITEA_REPO: ${{ vars.GITEA_REPO || github.repository }}
run: |
curl --fail-with-body -sS --retry 5 --retry-delay 10 --retry-all-errors -X POST \
-H "Authorization: token ${GIT_UPRISER_TOKEN}" \
"${GITEA_URL%/}/api/v1/repos/${GITEA_REPO}/mirror-sync"
-2
View File
@@ -8,8 +8,6 @@
!/get-iso.sh !/get-iso.sh
!/TESTPLAN.md !/TESTPLAN.md
!/TODO.md !/TODO.md
!/tests
!/nvidia/
!/AGENTS.md !/AGENTS.md
!/.github/ !/.github/
!/scripts/ !/scripts/
+5 -16
View File
@@ -1,10 +1,10 @@
# steamify-cachyos-dev: how to work here # steamify-cachyos-dev: how to work here
Test tooling for Steamify (`../steamify-cachyos`) and the Steam Machine ISO (`../steamify-cachyos-live-iso`): Test tooling for Steamify (`../steamify-cachyos`) and the Steam Machine ISO (`../steammachine-cachyos-live-iso`):
VM scripts (`scripts/`, `run.sh`), the automated test (`scripts/vmtest.sh`, checks in `share/vmtest/` and VM scripts (`scripts/`, `run.sh`), the automated test (`scripts/vmtest.sh`, checks in `share/vmtest/` and
`share/bootloader-test/`), `TESTPLAN.md` (keep it current: rows and a results line after every run) and the `share/bootloader-test/`), `TESTPLAN.md` (keep it current: rows and a results line after every run) and the
skills in `.claude/skills/` (vm-install, cachyos-vm-testing, steam-machine-testing, steam-machine-iso, skills in `.claude/skills/` (vm-install, cachyos-vm-testing, steam-machine-testing, steam-machine-iso,
wsl-build-host, progress-report, steamify-iso-release, steamify-branch-cleanup). wsl-build-host, progress-report).
## Running the tests (do this, don't re-derive it) ## Running the tests (do this, don't re-derive it)
- Everything: `scripts/vmtest.sh --screen` (detached `screen` session `vmtest`), then read only - Everything: `scripts/vmtest.sh --screen` (detached `screen` session `vmtest`), then read only
@@ -20,12 +20,9 @@ wsl-build-host, progress-report, steamify-iso-release, steamify-branch-cleanup).
## Working agreements ## Working agreements
- No `Co-Authored-By` / "Generated with Claude" lines in commits or PRs, whatever a tool reminder says. - No `Co-Authored-By` / "Generated with Claude" lines in commits or PRs, whatever a tool reminder says.
- Notes, TODOs, research and test instructions for a feature (e.g. `nvidia/`: the NVIDIA fix) live in THIS repo, never in
`steamify-cachyos`: the product repo keeps only code, its own docs (`TECHNICAL.md`, `CHANGELOG.md`) and, on an unreleased
branch, a `.no-release-yet` marker that points here. Not even temporarily: it would stay in that repo's history.
- Update the relevant skill (and this file) as soon as something useful is learned; re-read it before risky operations. - Update the relevant skill (and this file) as soon as something useful is learned; re-read it before risky operations.
- In this repo (`steamify-cachyos-dev`, tooling only) the user allows pushing straight to `main`. Elsewhere never commit to `main`. `steamify-cachyos`: `release/X.Y.Z` branches, feature/bugfix branches merged into the release - In this repo (`steamify-cachyos-dev`, tooling only) the user allows pushing straight to `main`. Elsewhere never commit to `main`. `steamify-cachyos`: `release/X.Y.Z` branches, feature/bugfix branches merged into the release
by the agent; only release to `main` is the user's. `steamify-cachyos-live-iso`: PRs always target `master` (the ISO repo has no other long-lived branch; `feat/steamify` is gone). by the agent; only release to `main` is the user's. `steammachine-cachyos-live-iso`: PRs always target `feat/steamify`.
Branch cleanup: the `steamify-branch-cleanup` skill in `.claude/skills/`. Branch cleanup: the `steamify-branch-cleanup` skill in `.claude/skills/`.
- Follow and update `TESTPLAN.md`; every check in `share/vmtest/` says which row it covers. - Follow and update `TESTPLAN.md`; every check in `share/vmtest/` says which row it covers.
- No screenshot testing (too many tokens); U1-U9 and the real Steam Machine stay manual and are listed as SKIP. - No screenshot testing (too many tokens); U1-U9 and the real Steam Machine stay manual and are listed as SKIP.
@@ -35,18 +32,10 @@ wsl-build-host, progress-report, steamify-iso-release, steamify-branch-cleanup).
suites cli, menu, hw, installer, toggles (`vmsuite.sh`, checks in `share/vmtest/`). Each passed in its own run: limine 43, suites cli, menu, hw, installer, toggles (`vmsuite.sh`, checks in `share/vmtest/`). Each passed in its own run: limine 43,
systemd-boot 42, grub 42 (with the B1 boot entry check), cli 55, menu 36, hw 77, installer 4, toggles 42 checks. systemd-boot 42, grub 42 (with the B1 boot entry check), cli 55, menu 36, hw 77, installer 4, toggles 42 checks.
- 2026-09-29, on the PC (WSL2): the first complete parallel run, **328 pass, 0 fail in 18 minutes** (base image + - 2026-09-29, on the PC (WSL2): the first complete parallel run, **328 pass, 0 fail in 18 minutes** (base image +
qcow2 overlays, own ssh port per job, package cache, `VM_MEM=4G`, `MAX_PARALLEL=3`; 5 worked on the 9800X3D). On a new machine: one full qcow2 overlays, own ssh port per job, package cache, `VM_MEM=4G`, `MAX_PARALLEL=3`). On a new machine: one full
`scripts/vmtest.sh --screen`, read `~/vms/last-test.txt`, fix what fails. `scripts/vmtest.sh --screen`, read `~/vms/last-test.txt`, fix what fails.
- Open work: `TODO.md` (the CEC driver download, the GitHub Actions workflow, P3). - Open work: `TODO.md` (the CEC driver download, the GitHub Actions workflow, P3).
## Releases (2026-09-29; details in the `steamify-iso-release` skill)
- Steamify: `release/X.Y.Z` -> PR into `main` (the user's) -> GitHub release, and the same workflow on the Gitea
mirror. A new Steamify release starts the ISO's release (`ISO_DISPATCH_TOKEN`); a CEC driver download is cached.
- The ISO: GitHub makes the tag `vX.Y.Z-[dev.]YYYY.MM.DD-HHMM` (UTC) and a release with the notes; the Gitea
mirror (git.upriser.nl, pull mirror, 10 min) builds on that tag and attaches the 3.2 GB ISO, named after the tag.
A release that exists only on the mirror is deleted by its next sync: the tag must come from GitHub.
- Merged feature/bugfix branches are deleted at once (`gh pr merge --delete-branch`). Push every commit.
## Where the tests run ## Where the tests run
The tests run on the user's PC (Ryzen 9800X3D, 64 GB, **WSL2**), started from a laptop over ssh (the user's own skill, or The tests run on the user's PC (Ryzen 9800X3D, 64 GB, **WSL2**), started from a laptop over ssh (the user's own skill, or
`scripts/vmtest-remote.sh <ssh-host> [args]`: pulls the pushed branch there, starts `vmtest.sh --screen`, waits for `scripts/vmtest-remote.sh <ssh-host> [args]`: pulls the pushed branch there, starts `vmtest.sh --screen`, waits for
@@ -54,7 +43,7 @@ The tests run on the user's PC (Ryzen 9800X3D, 64 GB, **WSL2**), started from a
down, syncing from the laptop, the podman ISO build, VM windows through WSLg, output in the user's WSL terminal, no GPU down, syncing from the laptop, the podman ISO build, VM windows through WSLg, output in the user's WSL terminal, no GPU
passthrough): `.claude/skills/wsl-build-host/SKILL.md`. `vminstall.sh` works there without udisks (losetup as root). passthrough): `.claude/skills/wsl-build-host/SKILL.md`. `vminstall.sh` works there without udisks (losetup as root).
## Problems found in the Steamify ISO (fix in `steamify-cachyos-live-iso`, PRs into `master`) ## Problems found in the Steamify ISO (fix in `steammachine-cachyos-live-iso`, PRs into `feat/steamify`)
- Fixed (PR #2): cachyos-installer's `bootctl install` runs in a chroot and writes no EFI boot entry, so systemd-boot - Fixed (PR #2): cachyos-installer's `bootctl install` runs in a chroot and writes no EFI boot entry, so systemd-boot
only booted through the disk's fallback path (in OVMF after 4-5 minutes of network boot, every boot). only booted through the disk's fallback path (in OVMF after 4-5 minutes of network boot, every boot).
`steamify-install` now registers it with efibootmgr from the live system. `steamify-install` now registers it with efibootmgr from the live system.
-20
View File
@@ -4,26 +4,6 @@ All notable changes, per version and per commit. Versions follow
[Semantic Versioning](https://semver.org/) and were numbered from the start [Semantic Versioning](https://semver.org/) and were numbered from the start
of the history. of the history.
## 0.5.0 - 2026-10-02
The whole automated test (boot loaders plus suites), the Steamify ISO pipeline,
the NVIDIA notes, and the Steamify unit tests and hardware test moved here.
- **feat: `scripts/vmtest.sh` runs everything: the boot loader test for every loader the ISO advertises, the plain-PC install check and the `cli`, `menu`, `hw`, `installer`, `toggles` and `nvidia` suites** (`ee20bd9`, `c91277a`, `fe5251f`, `b91ef91`, `6d264ac`, `343ff1e`, `5d168ea`)
- `vmbootloadertest.sh --quick [--generic]`: only what the ISO's install must get right, a Steam Machine gets all its items and modules, a plain PC none (`fafa38f`, `459832c`)
- faster and parallel runs (`MAX_PARALLEL`), a remote runner, progress lines while the tests run (`0c84b8b`, `540139b`)
- the boot entry check (BootCurrent) after every reboot (`27b7445`), the CEC driver cache blocks (`e6119ca`), `vmview.py` (`4cc4046`)
- **feat: `scripts/vminstall.sh` installs any boot loader and runs the Steamify ISO's Steamify step, with a package cache** (`8646b3c`)
- parallel installs under a lock, never root as the guest user, headless in CI, stops at once when the live system reports a failed install (`9d53740`, `d5cfadf`, `e929444`, `a34efb0`)
- **feat: `scripts/vmisobuild.sh` builds the Steamify ISO in the test VM** (`bc06bc2`)
- **feat: the `progress-report` skill and `scripts/vmprogress.sh`** (`fd9ecf4`)
- **docs: skills and AGENTS.md: the ISO release pipeline, the WSL build host, the VM tests, token-cheap ISO-VM testing** (`0d0279b`, `a1f8089`, `cb247b9`, `0052f1b`)
- **docs: `nvidia/`: TODO, research and test instructions for the NVIDIA fix, with the results of the hardware test on the RTX 5080 PC** (`d045b82`, `0052566`, `fa67d65`)
- **test: the Steamify unit tests (`controllers-test.sh`, `nvidia-test.sh`) and the NVIDIA hardware test move here from steamify-cachyos, in `tests/`; `REPO` points at the checkout** (`899e9af`)
- **test: `vmstate.sh` reports Steam's autostart entry (silent, plain, none) instead of the old systemd unit** (`66ec27d`)
- Steamify 2.11.0 tested here before its release: three ISO installs (defaults, everything on, Fremont everything on) and the full suite on a plain CachyOS VM, 269 checks passed, 0 failed, 1 known skip. Found on the way: the first-login skip failed in a bundle (`SCRIPT_DIR` unbound) and the Steam Machine serial step failed in the installer's chroot (read-only `/sys`); both fixed in Steamify.
- Known: `vmtest.sh` and `vmsuite.sh` write fixed files in `/tmp` (`/tmp/vmtest-*.out`, `/tmp/vmsuite-*-reset.out`, `/tmp/vminstall-iso.lock`): a second user on the same host gets "Permission denied" on files the first one left.
## 0.4.0 - 2026-09-28 ## 0.4.0 - 2026-09-28
An unattended VM install, a test plan, and the lessons from testing An unattended VM install, a test plan, and the lessons from testing
-30
View File
@@ -64,34 +64,6 @@ change only shows right after Enter).
## F. Features per release ## F. Features per release
### F2.10.0 Gaming on NVIDIA (`feature/nvidia-gaming-fix`; suite `nvidia`, fake NVIDIA PC)
gamescope's session is broken on NVIDIA (see `nvidia/HARDWARE-RESULTS.md`), so on an NVIDIA PC the conversion is replaced by
"Gaming on NVIDIA" (Steam on the Plasma desktop, started at login, optionally in Big Picture) and single user mode logs in to Plasma.
The VM has no NVIDIA GPU: `fake_nvidia` (`share/vmtest/prelude.sh`) fakes one through Steamify's `NVIDIA_DRM_DIR` test hook and installs
fake NVIDIA modules for every kernel. What no VM can show (real driver, the picture, Big Picture on a real GPU) is checked on the real PC.
| # | Step | Expect |
|---|---|---|
| N1 | `--defaults --list` and `--backend status` with the fake GPU (`nvidia/10-n1-menu.sh`) | `nvidia`, `bigpicture` (parent `nvidia`), `single` offered and preselected; `gaming`, `boot`, `glyphs` hidden; without the fake GPU the conversion is offered and `nvidia` is not |
| N2 | `--defaults --options nvidia,bigpicture,single` (`nvidia/20-n2-n5-lifecycle.host.sh`) | exit 0; Steam installed; unit with `-gamepadui` enabled; `loginMode=emptySession`; parameters in `/etc/default/limine` and `/boot/limine.conf`; early-load file, hook and script; modules in the initramfs; SDDM enabled and `zzz-steamify-autologin.conf` (Plasma session) |
| N3 | reboot | Plasma straight in (no greeter), parameters on `/proc/cmdline`, the Steam unit started, `loginMode` kept |
| N4 | `--defaults --options notify` | exit 0; Steam removed (Steamify installed it), unit and autologin file gone, `loginMode` back to not set, parameters/early-load/hook gone, plasma-login-manager the login manager again |
| N5 | reboot | Plasma up through the test autologin, no NVIDIA parameters |
| N6 | real RTX 5080 PC | **done by hand 2026-10-01** (earlier prototype): Big Picture on the Plasma desktop smooth and clean; the apply/boot of the final component is the user's next step |
### F2.10.0 Extended controller support (`feature/extended-controller-support`; suite `cli`, block `60-c1-controllers.sh`)
Opt-in item: `xpadneo-dkms`, `xone-dkms` and `xone-dongle-firmware` from the CachyOS repo, DKMS-built for every kernel. The VM has no
dongle or controller: it covers the packages, the headers and the builds, not the hardware.
| # | Step | Expect |
|---|---|---|
| C1 | `--defaults --options extended_controller_support` | exit 0; the three packages installed; `dkms status` shows xone and xpadneo installed for every kernel; the modules (`xone_dongle`, `hid_xpadneo`) exist for each |
| C2 | the same again | quiet, nothing installed again |
| C3 | `--defaults --options notify` | exit 0; the three packages removed again |
| C4 | real PC with the Xbox dongle | **by hand, 2026-10-01 earlier**: the dongle works with the AUR `xone-dkms-git` (modules `xone_dongle`, `xone_gip_gamepad` loaded); status counts `xone-dkms-git` as on |
### F2.7.0 `--defaults --options` / `--boot` ### F2.7.0 `--defaults --options` / `--boot`
| # | Step | Expect | | # | Step | Expect |
@@ -170,5 +142,3 @@ and started with `--fremont`. Automated: `scripts/vmtest.sh` (see the vm-install
| 2026-09-29 | Steamify 2.9.0 (`steamify-cachyos` main), ISO `feat/steamify` with PR #2 (2026.09.29), dev `main` 5b867f8; on the PC (WSL2) | everything automated: B1-B8 (3 loaders), cli, menu, hw, installer, toggles (`scripts/vmtest.sh`, parallel, `MAX_PARALLEL=3`) | **328 pass, 0 fail** (toggles 42 +1 expected skip, hw 70, cli 55, menu 36, installer 4, Limine 41, systemd-boot 40, GRUB 40) in 18 minutes: the first complete parallel run. Found on the way: systemd-boot had no EFI boot entry (installer's `bootctl` in a chroot), fixed in the ISO (PR #2); `vminstall.sh` fixes for parallel installs and a root host user | | 2026-09-29 | Steamify 2.9.0 (`steamify-cachyos` main), ISO `feat/steamify` with PR #2 (2026.09.29), dev `main` 5b867f8; on the PC (WSL2) | everything automated: B1-B8 (3 loaders), cli, menu, hw, installer, toggles (`scripts/vmtest.sh`, parallel, `MAX_PARALLEL=3`) | **328 pass, 0 fail** (toggles 42 +1 expected skip, hw 70, cli 55, menu 36, installer 4, Limine 41, systemd-boot 40, GRUB 40) in 18 minutes: the first complete parallel run. Found on the way: systemd-boot had no EFI boot entry (installer's `bootctl` in a chroot), fixed in the ISO (PR #2); `vminstall.sh` fixes for parallel installs and a root host user |
| 2026-09-29 | ISO `feat/steamify` + PR #3 (`/var/log`), dev `main` with the B1 boot entry check | B1-B8 | Limine 43 / GRUB 42 pass (boot entry check passes: `Limine`, `cachyos`). systemd-boot (reinstalled): 36 pass, 8 fail, all CEC: the driver download got HTTP 429 from GitHub (rate limit); boot entry check passes (`Linux Boot Manager`); `/var/log/steamify-install.log` and `steamify-bootentry.log` readable after boot | | 2026-09-29 | ISO `feat/steamify` + PR #3 (`/var/log`), dev `main` with the B1 boot entry check | B1-B8 | Limine 43 / GRUB 42 pass (boot entry check passes: `Limine`, `cachyos`). systemd-boot (reinstalled): 36 pass, 8 fail, all CEC: the driver download got HTTP 429 from GitHub (rate limit); boot entry check passes (`Linux Boot Manager`); `/var/log/steamify-install.log` and `steamify-bootentry.log` readable after boot |
| 2026-09-29 | Steamify `release/2.9.1` (CEC driver cache), ISO `feat/steamify` (PRs #2, #3), dev `main` | everything automated incl. hw block 50 (CEC cache) and the B1 boot entry check | **341 pass, 0 fail** in 19 minutes (toggles 42 +1 expected skip, hw 77, cli 55, menu 36, installer 4, Limine 43, systemd-boot 42, GRUB 42) | | 2026-09-29 | Steamify `release/2.9.1` (CEC driver cache), ISO `feat/steamify` (PRs #2, #3), dev `main` | everything automated incl. hw block 50 (CEC cache) and the B1 boot entry check | **341 pass, 0 fail** in 19 minutes (toggles 42 +1 expected skip, hw 77, cli 55, menu 36, installer 4, Limine 43, systemd-boot 42, GRUB 42) |
| 2026-09-30 | Steamify 2.9.6 (`steamify-cachyos` main `d93f9ea`), the official ISO `steamify-cachyos-2.9.6-260930-x86_64.iso` from git.upriser.nl (sha256 `4a94f308…aec41`), dev `main`; on the PC, headless (`CI=1`/`VM_HEADLESS=1`), `MAX_PARALLEL=5` | everything automated: B1-B8 (3 loaders, `--install` from the new ISO), cli, menu, hw, installer, toggles (base VM from the same ISO with `VM_STEAMIFY=skip`) | **341 pass, 0 fail** (toggles 42 +1 expected skip, hw 77, cli 55, menu 36, installer 4, Limine 43, systemd-boot 42, GRUB 42). Found on the way (test setup, not Steamify or the ISO): with five VMs at once the loaders' first boot took longer than `waitssh` waited (5 min), so all three stopped with `FAIL no SSH` although the VMs were up; `waitssh` now waits up to 15 minutes and the loaders were run again (install kept) |
| 2026-10-01 | Steamify `feature/extended-controller-support` (7dae14c; on top of `feature/nvidia-gaming-fix` d0e4d8c, 2.9.7), plain CachyOS ISO 260809 (VM `~/vms/steamify-vm`, `vminstall.sh --iso`), dev `main` + the nvidia suite; on the PC (native CachyOS, KVM), headless | N1-N5 (new suite `nvidia`), C1-C3 (cli block 60), R1.1-R1.6/R1.10, H1-H5 and the CEC/CEC cache blocks, R2.1, toggles | **269 pass, 0 fail**: cli 65, nvidia 43, menu 37, hw 78, installer 4, toggles 42. Found on the way (test side, not Steamify): R1.1 and H3 pressed menu rows by number and the new Extended controller support row shifted them (now by name); the N4 check for the parameter in `/boot/limine.conf` must ignore snapper's snapshot entries, which keep the command line of their time. The kernel part (parameters, early-load file, hook, initramfs) behaves in a real Limine VM; the real driver and picture are only checked on the RTX 5080 PC |
+21 -15
View File
@@ -1,20 +1,27 @@
# TODO # TODO
## Tests in CI: dropped 2026-09-30, the VM tests run locally ## GitHub Actions: run the boot loader test in CI
Decision: `scripts/vmtest.sh` runs on the PC before the release PR, not on GitHub or Gitea runners (too slow, too heavy, and Goal: every push/PR (and on demand) runs the whole automated test (`scripts/vmtest.sh`, see the next
not reliable: some GitHub runners had no `/dev/kvm`, nested virtualization made installs slow, the 3 GB ISO had to come section: boot loaders plus every suite) on runners, one job per suite or loader, and fails on any `FAIL` line.
from the mirror on every run; the Gitea machine has 8 threads). The attempt (steamify-cachyos PR #66, closed, branch Public repos, so the free Linux runners with KVM apply. Work on `feature/ci-bootloader-test`,
`feature/vmtest-ci`) left these behind: `vminstall.sh` reads the ISO with `bsdtar` (no loop devices), `CI=1` makes it PR into the release branch. Tick items off as they're done.
headless, the VM's ssh key is looked up at every call, an aborted test counts as a failure, progress lines while a test runs.
- [x] TESTPLAN.md: section B for the boot loader test (B1-B8), pointing at `scripts/vmtest.sh` - [x] 1. `run.sh`: headless mode (`VM_HEADLESS=1` -> `-display none`, plain virtio-vga); still to do: make `vmtest.sh` and `vmbootloadertest.sh` headless by default (`--window` opts out, a log Konsole opens on a desktop, CI opens none) and copy the repo's run.sh into `$VM_DIR` before starting
- [x] `vmbootloadertest.sh --quick [--generic]`: only what the ISO's install must get right (B1-B5) on a Steam Machine VM - [ ] 2. `vminstall.sh`: read the ISO's kernel/initramfs without `udisksctl` (bsdtar/7z when there is no session)
(`--fremont`) or a plain PC (`--generic`: the Steam Machine items and modules must be absent) - [ ] 3. `vminstall.sh` / `vmbootloadertest.sh`: no `/dev/tty` prompts and no `pgrep` on the whole host in CI (`CI=1`)
- [ ] A helper that fetches the newest ISO release of the mirror (git.upriser.nl) into - [ ] 4. Where the ISO comes from: newest artifact of `steammachine-cachyos-live-iso`'s `build.yml`
`../steamify-cachyos-live-iso/out/desktop/`, for `vmtest.sh --install` (the ISO is no longer kept locally) (`gh run download -R theupriser/steammachine-cachyos-live-iso`), or a `workflow_dispatch` input (run id / URL)
- [ ] Document `--quick` / `--generic` in the `vm-install` skill and TESTPLAN.md (which B rows it covers) - [ ] 5. `.github/workflows/bootloader-test.yml`: matrix over `limine, systemd-boot, grub`; steps: install
- [ ] Steamify `AGENTS.md`: "run `scripts/vmtest.sh` before the release PR into main" (decided 2026-09-30) qemu + ovmf, enable /dev/kvm, fetch the ISO, run the script, upload `~/vms/bl-<loader>.test.log` as an artifact
- [ ] 6. Runner limits: disk (sparse 60 GB qcow2, free space check), RAM (8 GB VM on a 16 GB runner), timeout (60 min)
- [ ] 7. Cache the pacman packages between runs (`actions/cache` on `~/vms/pkg-cache`, `VM_CACHE`)
- [ ] 8. Trigger it once on the branch, fix what the runner shows; note the run time in the skill
- [ ] 8b. Second job, `--fremont` hardware tests from TESTPLAN.md section H (LED driver, CEC with vivid,
BIOS dry-run, boot into desktop/gaming and reboot, power-off module): guest-side, so they run on a runner too;
needs a way to check without screenshots (gaming mode doesn't render)
- [ ] 9. `vm-install` skill + `vmtest.sh` header: mention the workflow, and that the advertised-loader check fails CI when a loader has no test
- [x] 10. TESTPLAN.md: section B for the boot loader test (B1-B8), pointing at `scripts/vmtest.sh`
## Full automated test: everything in TESTPLAN.md that needs no person ## Full automated test: everything in TESTPLAN.md that needs no person
@@ -63,8 +70,7 @@ Where the time goes: boot loader VMs ~3 min each, every suite block restores a s
(`view_start` returns early; watch with `screen -r vmtest` or `tail -f ~/vms/test.log`), a VM for the user gets a (`view_start` returns early; watch with `screen -r vmtest` or `tail -f ~/vms/test.log`), a VM for the user gets a
window through WSLg (see the wsl-build-host skill). Still to do: a first full `vmtest.sh` run there window through WSLg (see the wsl-build-host skill). Still to do: a first full `vmtest.sh` run there
- [x] R1c. `vminstall.sh` without udisks (WSL): `losetup -P` + `mount` when `udisksctl` is missing and it runs as root - [x] R1c. `vminstall.sh` without udisks (WSL): `losetup -P` + `mount` when `udisksctl` is missing and it runs as root
- [x] R2. `MAX_PARALLEL=5` ran fine on the PC (9800X3D, 8c/16t, 64 GB; reported 2026-09-30). The default stays 3 for smaller - [ ] R2. With 64 GB and 8c/16t: try `MAX_PARALLEL=4` and 8 GB VMs (`VM_MEM=8G`)
machines. Not tried: 8 GB VMs (`VM_MEM=8G`).
## Found 2026-09-29 (first full run on the PC) ## Found 2026-09-29 (first full run on the PC)
+2 -4
View File
@@ -4,8 +4,7 @@
set -euo pipefail set -euo pipefail
cd "$(dirname "$0")" cd "$(dirname "$0")"
# CachyOS's own mirror: nl.mirror.cx gave about 1 MB/s here, this one 80 MB/s (MIRROR=... to use another). mirror="${MIRROR:-https://nl.mirror.cx/cachyos}"
mirror="${MIRROR:-https://mirror.cachyos.org}"
# The download page embeds the current ISO URL; take the release from it. # The download page embeds the current ISO URL; take the release from it.
release="$(curl -fsL https://cachyos.org/download/ | release="$(curl -fsL https://cachyos.org/download/ |
@@ -23,9 +22,8 @@ if [[ -f cachyos.iso && "$(cat cachyos.iso.version 2>/dev/null)" != "$name" ]];
rm -f cachyos.iso rm -f cachyos.iso
fi fi
# The version is written before the download: a partial file of this release is resumed, not deleted as "an older release".
echo "$name" > cachyos.iso.version
curl -fL -C - -o cachyos.iso "$url" curl -fL -C - -o cachyos.iso "$url"
echo "$name" > cachyos.iso.version
echo "Verifying checksum..." echo "Verifying checksum..."
curl -fsL "$url.sha256" | awk '{print $1" cachyos.iso"}' | sha256sum -c curl -fsL "$url.sha256" | awk '{print $1" cachyos.iso"}' | sha256sum -c
-70
View File
@@ -1,70 +0,0 @@
# NVIDIA fix: results on the RTX 5080 PC (2026-10-01)
PC: RTX 5080 (GB203, 10de:2c02) + AMD Granite Ridge iGPU, MSI MAG 341C OLED on the NVIDIA DP-1 (3440x1440, 175 Hz),
CachyOS, Limine, kernel 7.2.8-1-cachyos, nvidia-open 615.71.09, gamescope 3.16.31-1, gamescope-session-cachyos 1.1.6-1.
## Result: the kernel-parameter fix does not cure the picture
- Baseline (before): gaming mode corrupted without HDR; with HDR it worked but not at 175 Hz and not full screen.
- After `apply` + reboot (`modeset = Y`, `fbdev = Y`, NVIDIA modules in the initramfs): flickers heavily, artifacts, still no 175 Hz.
`modeset`/`fbdev` were already `Y` live before the fix (driver 615), so the fix changed little.
- Cause is not the parameters: NVIDIA's open bug "display modes above 2560x1440@120 with HDR cause flickering/corruption within
gamescope-session" (internal bug 5240452, April 2025, no fix as of March 2026; 555 up to 595+, RTX 3070 Ti/4090/5090; corruption is
display-side only):
https://forums.developer.nvidia.com/t/display-modes-above-2560x1440p-120hz-with-hdr-enabled-cause-flickering-corruption-within-gamescope-session/295314
## Bugs found in the fix on this PC (fixed in commit a5e9015 on feature/nvidia-gaming-fix)
- CachyOS writes Limine's line as `KERNEL_CMDLINE[default]+="..."`; the sed only matched `=`, so apply stopped with "Couldn't add the NVIDIA parameters".
- The kernel treats `nvidia_drm.modeset=1` and `nvidia-drm.modeset=1` alike; the check only knew the hyphen.
- tests/nvidia-hardware-test.sh did not set `SCRIPT_DIR`, so patch_file read `/patches/...`.
## What gamescope did (journal, gaming mode)
- Picks the RTX 5080 (card1), DP-1, mode list has 3440x1440@175/144/100. It selected @60 by default; with
`-W 3440 -H 1440 -r 175` added (a copy of /usr/lib/steamos/gamescope-session, whose last line is `-O ... \` so args need a copy)
it selected @175 and did not crash, but the picture was the same. Steam then sets the Xwayland size to 1920x1080 (not full screen).
- `vkGetPhysicalDeviceFormatProperties2 returned zero modifiers` for 2 16-bit formats: harmless here.
- A gamescope SIGABRT (`double free or corruption`) at session exit, backtrace through libnvidia-eglcore and libvulkan_radeon.
- The PC froze once and was reset after a gaming-mode session (no kernel error logged).
## What works: nested gamescope inside KDE (Wayland), borderless
- `gamescope -W 3440 -H 1440 -r 175 -f ...`: artifacts once the window is focused and fullscreen (unfocused it is fine).
Hypothesis, not proven: KWin's direct scanout of the fullscreen window.
- `gamescope -W 3440 -H 1440 -r 175 -b -e --hdr-enabled -- steam -gamepadui` (borderless): clean, full screen, about 175 Hz.
- So on NVIDIA the separate gamescope session (the SteamOS conversion) is blocked by the driver bug; a nested borderless gamescope works.
Whether Steamify gets a nested option for NVIDIA is the user's decision.
## Cause confirmed: KWin's direct scanout; a session without Plasma works (2026-10-01, later)
- A TTY session of only `kwin_wayland --drm` + `gamescope -b` + `steam -gamepadui` (no Plasma shell, no panel) artifacted too
(KWin hands the covering window straight to the display).
- Same session with `KWIN_DRM_NO_DIRECT_SCANOUT=1` (exists in KWin 6.7.5's libkwin; not in the kwin_wayland binary's strings):
NO artifacts, full screen, about 175 Hz (gamescope logs `Changed refresh to: 174.962hz`). Animations felt a bit clunky (cause unknown:
an extra composition step without scanout, no CAP_SYS_NICE for gamescope, no VRR, nested frame pacing; other switches to try:
`KWIN_DRM_DISABLE_TRIPLE_BUFFERING`, `KWIN_DRM_PREFER_COLOR_DEPTH`).
- Launcher used for the test: `~/.local/bin/steamify-nvidia-session-test` (run from a free TTY; KWin as the compositor,
`gamescope -W 3440 -H 1440 -r 175 -b -e --hdr-enabled -- steam -gamepadui`, a watchdog ends it after 90 s).
Lesson: the first version's timeout left KWin/gamescope/Steam running (the cleanup came after a pipeline that waits for them);
`pkill -f '<pattern>'` in a command that contains the pattern kills the caller.
- Open: a real session entry (`/usr/share/wayland-sessions/`, needs sudo) and the product decision: an NVIDIA session mode in Steamify
instead of the gamescope DRM session. Not built, not in the product repo.
## Smoothness: the TTY-only KWin session stutters, Plasma does not; Big Picture in Plasma is perfect (2026-10-01, evening)
- TTY session (`kwin_wayland --drm` under `dbus-run-session`, no Plasma): the mouse and windows stutter every 1-2 s, with gamescope
(clunky) and without it (`steam -gamepadui` as KWin's only client). Same in both: not gamescope, not Steam. Plasma's own KWin is smooth.
- Tried without effect: `KWIN_DRM_DISABLE_TRIPLE_BUFFERING=1`. Measured: the test KWin used about 2% CPU, no spikes, but its main thread
dropped to 0% about every 2 s (blocked, not busy); scheduling (RR main thread, nice -12) and capabilities equal to Plasma's KWin;
NVIDIA is boot_vga; no relevant environment differences found. Cause of the stutter not found.
- Plain `steam -gamepadui` (Big Picture, no gamescope) in the normal Plasma session: **perfect** (smooth, no artifacts, no env var needed).
- Direction: for NVIDIA PCs a Plasma session that shows only Steam's Big Picture (no desktop, no panel) instead of the gamescope
session. Gives up gamescope's features (HDR handling, FSR, overlay; most `-steamos3` menus need gamescope).
## Plasma session with Big Picture started at login: works (2026-10-01, night)
- `~/.config/autostart/steamify-bigpicture.desktop` (`Exec=steam -gamepadui`) in the normal Plasma Wayland session, SDDM autologin set to
`plasma.desktop` (`pkexec /usr/lib/steamos/steam-set-session plasma.desktop`): Big Picture opens by itself, smooth, no artifacts, no
KWin environment variables needed. Exiting Steam leaves the Plasma desktop, no relogin.
- A separate session entry (prototype in the scratchpad, not installed: `steamify-bigpicture-session` marker + autostart helper, session
file in `/usr/share/wayland-sessions`) only decides WHEN Big Picture starts; the plain autostart gave the same result.
## State of the PC after the test (to undo)
- The fix is applied (Limine file backup `/etc/default/limine.bak-gamescope-wizard`, early-load file, pacman hook): undo in INSTRUCTIONS.md.
- Test launcher `~/.local/bin/steamify-nvidia-session-test`, log `~/steamify-nvidia-session-test.log`.
- User override of the gaming session: `~/.config/systemd/user/gamescope-session.service.d/steamify-nvidia-test.conf` and
`~/.local/share/steamify/nvidia-test/`.
-87
View File
@@ -1,87 +0,0 @@
# Testing the NVIDIA fix on a real PC
For the PC with an RTX 5080 (or any RTX 20 series or newer card) that shows a corrupted picture when
gaming mode (gamescope) starts. Product branch: `feature/nvidia-gaming-fix` in steamify-cachyos, not released.
This file lives in steamify-cachyos-dev (`nvidia/INSTRUCTIONS.md`); background and open work: `nvidia/TODO.md`; research
with sources: `nvidia/RESEARCH.md` (github.com/theupriser/steamify-cachyos-dev).
## What the fix does
With a supported NVIDIA GPU (RTX 20 series or newer, same check as the VRAM booster) and its driver installed it
- adds `nvidia-drm.modeset=1 nvidia-drm.fbdev=1` to the kernel command line (Limine, systemd-boot or GRUB;
the file is backed up as `<file>.bak-gamescope-wizard`),
- loads the NVIDIA modules in the initramfs, but only while every installed kernel has them (a pacman hook,
`/etc/pacman.d/hooks/85-steamify-nvidia-initramfs.hook`, decides again at every kernel change),
- rebuilds the initramfs and boot entries and checks that the parameters really are in them.
It takes effect after a reboot. Older cards (GTX 10 series and older) are left alone.
## Before you start
- CachyOS with the NVIDIA driver installed (`nvidia-open`), a user with sudo, `git`.
- Be at the PC and able to see the screen: the last step needs your eyes.
- Know the way back (below) in case the PC doesn't boot to a picture: a TTY (Ctrl+Alt+F3) or the boot menu's
other kernel entry.
## Steps
1. Get the code:
```
git clone -b feature/nvidia-gaming-fix https://github.com/theupriser/steamify-cachyos
cd steamify-cachyos
```
(An existing checkout: `git fetch && git checkout feature/nvidia-gaming-fix && git pull`.)
2. Look first, change nothing:
```
bash tests/nvidia-hardware-test.sh check
```
3. Apply the fix (asks for your sudo password), then **reboot**:
```
bash tests/nvidia-hardware-test.sh apply
```
4. After the reboot, check again:
```
bash tests/nvidia-hardware-test.sh check
```
5. Start gaming mode, look at the screen, then (from the desktop or a TTY):
```
bash tests/nvidia-hardware-test.sh visual
```
It asks whether the picture was clean and for one line on what you saw.
6. Send back `~/steamify-nvidia-report.txt` (every run adds to it), or start a new session in the checkout and
say "read nvidia/TODO.md in steamify-cachyos-dev and the report".
(`./steamify.sh` from the same checkout also applies the fix, automatically, as part of the SteamOS conversion:
there is no menu option for it yet. The script above shows more.)
## Reading `check`
| Line | Good result | If not |
|---|---|---|
| `PASS a supported NVIDIA GPU ... is detected` | your 5080 | `FAIL ... older than RTX 20`: the card is skipped on purpose. `FAIL no NVIDIA GPU with its driver`: driver not installed (nouveau?) |
| `NVIDIA PCI device id 0x... (not on a legacy list ...)` | RTX 20 or newer | on a legacy list: older card |
| `no chwd legacy lists found` | (note only) | the check cannot tell old from new cards; every card counts as supported. Tell me, it's open item 4 in `nvidia/TODO.md` |
| `PASS nvidia-drm.modeset=1 is on the running kernel's command line` (and `fbdev=1`) | after step 3 and the reboot | "in the boot loader's file but not booted yet": reboot, or the boot entry used is another one |
| `PASS nvidia_drm modeset = Y` (and `fbdev = Y`) | after the reboot | `unreadable`: it needs sudo, enter the password |
| `... early-load drop-in is used / skipped` | note only: skipped means a kernel without the NVIDIA modules is installed | nothing to do |
## Reading the result
- Parameters on, `modeset = Y`, `fbdev = Y`, picture clean: the fix works.
- Parameters on and `Y`, picture still corrupted: not this fix. The report has the gamescope and NVIDIA log lines and
the versions of `nvidia-open`, `nvidia-utils`, `gamescope` and `gamescope-session-cachyos`: the cause is probably the
gamescope/driver combination for Blackwell (see `nvidia/RESEARCH.md`).
- No picture at all after the reboot: undo it (next section) from a TTY or the other kernel entry, and send the report.
## Undo
Turning the SteamOS conversion off in the wizard removes the fix too. On its own, from the checkout:
```
SCRIPT_DIR=$PWD bash -c 'source lib/common.sh; source lib/hdmi-refresh.sh; source lib/vram-booster.sh; source lib/nvidia.sh; nvidia_disable'
```
It removes the parameters, the early-load file, the pacman hook and its script, and rebuilds the boot entries (reboot after).
By hand: delete ` nvidia-drm.modeset=1 nvidia-drm.fbdev=1` from `/etc/default/limine` (`KERNEL_CMDLINE[default]`),
`/etc/sdboot-manage.conf` (`LINUX_OPTIONS`) or `/etc/default/grub` (`GRUB_CMDLINE_LINUX_DEFAULT`), or restore the
`.bak-gamescope-wizard` copy next to it; remove `/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf`,
`/etc/pacman.d/hooks/85-steamify-nvidia-initramfs.hook` and `/usr/local/libexec/steamify-nvidia-initramfs`; then
`sudo limine-mkinitcpio` (Limine) or `sudo mkinitcpio -P` and `sudo sdboot-manage gen` / `sudo grub-mkconfig -o /boot/grub/grub.cfg`.
## What is and isn't proven
Proven in VMs (QEMU/KVM, CachyOS ISO 260809): the parameters land and survive a reboot on Limine, systemd-boot and GRUB;
disabling restores the original; on Limine the pacman hook keeps the early-load file right across kernel reinstalls
(with fake NVIDIA modules). Not proven: that the fix cures the 5080's picture, the real NVIDIA modules in the
initramfs, DKMS hook order, systemd-boot/GRUB with a pacman transaction, and the RTX 20+ check against a real chwd list.
Unit test (no hardware): `bash tests/nvidia-test.sh`.
-64
View File
@@ -1,64 +0,0 @@
# NVIDIA and gamescope: research notes (for steamify-cachyos branch `feature/nvidia-gaming-fix`)
Written 2026-10-01 for the NVIDIA fix (`lib/nvidia.sh`). Delete the `nvidia/` folder when the feature is released, or move what is still useful to steamify-cachyos' `TECHNICAL.md`.
"Verified" means seen in this session (a VM run or a fetched page); the rest is from search results or memory.
## The user's PC
RTX 5080 (Blackwell). Blackwell only works with NVIDIA's open kernel modules (`nvidia-open`), driver 570+.
The corrupted picture at gaming mode start is not diagnosed yet: the fix adds kernel parameters, the real cause
may be the gamescope/driver combination. `tests/nvidia-hardware-test.sh` collects the facts.
## What the fix changes and why (reasoning, not proven for gamescope)
- gamescope runs the display itself through DRM/KMS. With NVIDIA that needs `nvidia-drm.modeset=1`; `nvidia-drm.fbdev=1`
gives the console a framebuffer from `nvidia-drm`, so the hand-off from the console to gamescope is clean.
- Loading the NVIDIA modules in the initramfs (early KMS) only changes how early boot looks (native resolution,
splash, passphrase prompt), not speed. It is not needed for gamescope, which starts after login (believed, not proven).
- No source found that mentions these parameters for gamescope; the fix is a standard NVIDIA/Wayland setup, applied
automatically. Whether it cures the 5080's picture is only known after `tests/nvidia-hardware-test.sh`.
## Which cards (decision 2026-10-01: RTX 20 series or newer only, same check as the VRAM booster)
The code skips a card whose PCI id is on chwd's legacy lists (`vram_nvidia_legacy_id`); the notes below are why older
cards are not promised.
- Open kernel modules: Turing (RTX 20, GTX 16) and newer only; they need the GSP processor first built into Turing.
Maxwell, Pascal and Volta (GTX 900/10 series, Titan V) only work with the proprietary driver, whose legacy branch
is 580. Source: [NVIDIA README, open kernel modules](https://download.nvidia.com/XFree86/Linux-x86_64/560.35.03/README/kernel_open.html),
[NVIDIA datacenter driver guide, kernel modules](https://docs.nvidia.com/datacenter/tesla/driver-installation-guide/kernel-modules.html).
- `nvidia-drm.modeset` and `fbdev` are options of the `nvidia-drm` module in both flavours, so the fix applies to
any card whose driver provides `nvidia_drm` (what `nvidia_present` tests: GPU vendor 0x10de + `modinfo nvidia_drm`).
Whether gamescope then works on an older card is a different question, and no source says the fix helps there.
- Not NVIDIA's proprietary driver: nouveau has no `nvidia_drm`, so the fix does nothing. The CachyOS handheld ISO boots
GTX 10xx and older with nouveau until the NVIDIA driver is installed
([CachyOS forum](https://discuss.cachyos.org/t/information-experimental-cachyos-handheld-edition/203)).
## Known gamescope problems that are not this fix
- **GTX 1050 Ti (Pascal), driver 580.119.02, gamescope newer than 3.16.16:** the CachyOS handheld session fails to start when
a display is on the GPU's HDMI port (back to the TTY, loop). Workarounds: downgrade `gamescope` and `lib32-gamescope`
to 3.16.16, or use the motherboard's video port. Fixed upstream? unknown.
[CachyOS forum](https://discuss.cachyos.org/t/no-display-on-cachyos-handheld-edition-on-nvidia-with-gamescope-3-16-16/20935)
- **VRS on PCs:** CachyOS' gamescope-session enables variable rate shading (`STEAM_USE_DYNAMIC_VRS=1`,
`RADV_FORCE_VRS_CONFIG_FILE`, `echo 1x1 > ...` in `/usr/lib/steamos/gamescope-session`). It broke rendering (missing
floors/lighting, bad post-processing) on a PC with an AMD RX 6800 XT. RADV is AMD's driver: not the NVIDIA corruption.
Workaround: comment those three lines, or per game `env -u RADV_FORCE_VRS_CONFIG_FILE STEAM_USE_DYNAMIC_VRS=0 %command%`.
[CachyOS forum](https://discuss.cachyos.org/t/cachyos-gamescope-session-asset-missing-vrs-fix/34971)
- gamescope's DRM backend needs Vulkan DRM format modifiers; with the open Mesa driver NVK that came in Mesa 24.1
([GamingOnLinux](https://www.gamingonlinux.com/2024/05/nvk-driver-gets-drm-format-modifiers-to-work-with-gamescope-in-mesa-24-1)).
The proprietary driver had format-modifier trouble with gamescope too (GitHub issues
[ValveSoftware/gamescope#1662](https://github.com/ValveSoftware/gamescope/issues/1662),
[#1516](https://github.com/ValveSoftware/gamescope/issues/1516): titles seen only, not read).
## What was verified in VMs (QEMU/KVM, CachyOS ISO 260809)
- Kernel parameters land and survive a reboot on Limine, systemd-boot and GRUB; disable restores the original.
- Limine's tool is a compiled program: an appended `KERNEL_CMDLINE[default]+=" ..."` line was pasted into the kernel
command line as text; the fix edits the existing `KERNEL_CMDLINE[default]="..."` line.
- `mkinitcpio` fails on a MODULES entry a kernel lacks; `limine-mkinitcpio` then skips that kernel's initramfs and boot
entry (parameters included); systemd-boot/GRUB image: "may not be complete" (not run with failing modules).
- Real CachyOS/Limine pacman hooks: `10-limine-snapper-lock`, `60-limine-mkinitcpio-remove-pre`, `60-mkinitcpio-remove`,
`80-limine-efi-deploy`, `90-limine-mkinitcpio-remove-post`, `90-mkinitcpio-install`; Steamify's hook is `85-`
(DKMS's is `71-dkms-install`, not installed in the VM). A mirror older than the ISO "downgrades" kernels on reinstall.
- The hook keeps the early-load drop-in right across kernel changes (see `nvidia/TODO.md`).
## Still unknown (needs the hardware)
- Does the fix cure the 5080's corrupted picture? Which gamescope/driver versions does the PC have?
- Does it help, harm or do nothing on Maxwell/Pascal/Volta cards? No older card available yet.
- Real NVIDIA modules in the initramfs (VMs only had renamed fake modules); DKMS hook order on a PC with
`nvidia-open-dkms`.
-136
View File
@@ -1,136 +0,0 @@
# TODO: NVIDIA (status 2026-10-01, evening: READ THIS FIRST, the sections below are the older plan)
Version: bumped to **2.10.0** (2026-10-01, "chore: Version 2.10.0" on `feature/nvidia-gaming-fix`; `feature/extended-controller-support` is stacked on it).
The old plan below says 2.9.7 / `release/2.9.7`: read that as 2.10.0 / `release/2.10.0`, which doesn't exist yet (create it from `main` when releasing).
The kernel-parameter fix did not cure gamescope's picture; the cause is NVIDIA's own bug (see `HARDWARE-RESULTS.md`). The product now does this
instead (branch `feature/nvidia-gaming-fix`, 2.10.0, still behind `.no-release-yet`):
- On an NVIDIA PC the SteamOS conversion (`gaming`, `boot`, `glyphs`) is hidden (shown only while already on, so it can be turned off).
- `nvidia` "Gaming on NVIDIA": Steam installed when missing, started at login on the Plasma desktop (user unit); sub-option `bigpicture`
(a checkbox, not yet the choice row "How should Steam start up: Normal / Big Picture"): `-gamepadui` + Plasma starts with an empty session.
RTX 20+ also keeps the old kernel parameters and early-load initramfs (user asked for it: "it may need it").
- `single` (single user mode) also works there: its own SDDM autologin into the Plasma session (`zzz-steamify-autologin.conf`).
- Tested in VMs (QEMU/KVM, fake NVIDIA GPU through `NVIDIA_DRM_DIR`, fake modules): suite `nvidia`, TESTPLAN N1-N5, 43 pass; regression suites green.
- Tested by hand on the RTX 5080 (earlier prototypes): Big Picture in the normal Plasma session smooth and clean; autologin and the final component NOT yet run there.
Open: run the final wizard on the RTX 5080 PC (turn the conversion off, tick Gaming on NVIDIA + Single user mode, reboot); the real choice row
instead of the checkbox (the choice row is hard-wired to `boot` in the TUI, the app and `--boot`); games don't start fullscreen under Plasma
(idea: a KWin rule forcing fullscreen for `steam_app_*`, may bring the direct-scanout artifacts back: test on the PC); apps that start later
(CachyOS Hello) may still cover Big Picture; hint in the menu on why the item is missing while the conversion is on; changelog hashes;
screenshot retake and the ISO's installer page for the new rows before a release. Separate branch `feature/extended-controller-support`
(xone dongle + xpadneo, opt-in) is stacked on this one.
# TODO: NVIDIA fix for gaming mode
Product code: branch `feature/nvidia-gaming-fix` in steamify-cachyos (github.com/theupriser/steamify-cachyos). These notes live here, in
steamify-cachyos-dev, on purpose: they are not part of the product and must not be in its history. The branch only has
a `.no-release-yet` marker pointing here.
When the feature is released, move what is still useful to steamify-cachyos' `TECHNICAL.md` and delete this folder (`nvidia/`).
Written 2026-10-01 so a new session can pick this up.
## Problem
A PC with an NVIDIA RTX 5080 (Blackwell, open kernel modules) shows a corrupted
image when gaming mode (gamescope) starts. Cause not confirmed. Steamify had no
NVIDIA handling at all. Likely: `nvidia-drm.modeset=1` / `nvidia-drm.fbdev=1`
missing, or a gamescope/driver mismatch.
## What is built (all on the product branch, pushed)
- `lib/nvidia.sh`: part of the SteamOS conversion (`gaming_enable` / `gaming_disable`).
With an NVIDIA GPU + driver it adds `nvidia-drm.modeset=1 nvidia-drm.fbdev=1` to the
kernel command line (Limine, systemd-boot, GRUB; backup first) and, only when every
installed kernel has `nvidia_drm`, `/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf`.
Rebuilds initramfs + boot entries, verifies the config and the generated entries
have the parameters (error otherwise). Undone on disable. Description: `TECHNICAL.md`.
- `FEATURE_VERSION[gaming]=2.9.7` so installs that already have the conversion re-apply it.
- Test hooks `NVIDIA_DRM_DIR`, `NVIDIA_MODULES_DIR`, `NVIDIA_LIMINE_CONF`,
`NVIDIA_SDBOOT_DIR`, `NVIDIA_GRUB_CFG`: only set by tests; defaults are the real paths.
## Tested
- `bash tests/nvidia-test.sh`: 22 checks against a fake RTX 5080 beside an iGPU, a stub
`modinfo`, temp boot loader files. All pass.
- Real VMs (QEMU/KVM, Limine, systemd-boot, GRUB): enable, reboot, kernel command line
has the parameters; disable, reboot, original command line. All pass. Found and fixed:
Limine pasted an appended `+=` line as text into the cmdline; a kernel without the
NVIDIA modules made limine-mkinitcpio skip its boot entry.
## Kernel changes (done, VM-tested 2026-10-01, Limine)
The early-load drop-in is kept right by a pacman hook (`patches/steamify-nvidia-initramfs.{sh,hook}`,
installed by `nvidia_enable`, `/etc/pacman.d/hooks/85-steamify-nvidia-initramfs.hook`). Real pacman
transactions in a Limine VM: the hook runs between "Updating module dependencies" and "Updating
linux initcpios" (real hook names there: 60-*-remove, 80-limine-efi-deploy, 90-limine-mkinitcpio-remove-post,
90-mkinitcpio-install; DKMS would be 71-): the drop-in is present while every kernel has the modules, removed
when the LTS kernel loses them, back when they return; initramfs nvidia files 1 -> 5 -> 1 -> 5 -> 1
(1 = baseline, 4 modules added); no ERROR/skipping in any rebuild; cmdline kept the parameters after a reboot;
disable removes hook, script and drop-in. Found on the way: an empty hook/script was installed when
`patch_file` failed (now an error). Only tested with fake modules (renamed copies of a small module), on
Limine; systemd-boot and GRUB use the same hook but were not run with pacman transactions.
## Opt-out toggle (new request, 2026-10-01: not built yet)
When a compatible NVIDIA card is listed there should be a menu option, on by default (opt-out), named
"NVIDIA compatibility", as a sub-option of the SteamOS conversion, directly under "Boot into" (`boot`).
Today the fix runs unconditionally inside `gaming_enable`/`gaming_disable`.
- [ ] New component `nvidia` in `lib/menu.sh`: `COMPONENTS` right after `boot`, `PARENT[nvidia]=gaming`, `LABEL`,
`FEATURE_VERSION[nvidia]`, `component_available` -> `nvidia_available` (= `nvidia_present`); not in
`NO_PRESELECT` (ticked by default, `feature_new` ticks it for installs that already have the conversion).
`nvidia_status` from the system (hook + parameters present), `nvidia_enable`/`nvidia_disable` = the code now
called from `gaming_enable`/`gaming_disable` (remove those calls). Check `toggle_component` dependencies,
`feature_record_unticked`, the plan texts ("This will: ...").
- [ ] The app: `ui/qml/Texts.qml` item texts, `AppState.qml`, the screen rows, `lib/backend.sh` if it lists ids;
`--defaults [--options <ids>]` (the Steam Machine ISO's installer pages: it names the ids) and `--skip`.
- [ ] README rows/screenshot rule (a release that adds a menu row retakes `assets/screenshot-menu.png`), TECHNICAL.md,
CHANGELOG, `tests/nvidia-test.sh` (status/enable/disable through the component), VM menu test in
steamify-cachyos-dev (`share/vmtest/menu`, TESTPLAN.md row).
- [x] What "compatible" means (decided 2026-10-01): RTX 20 series or newer, the same check as the VRAM booster
(`vram_nvidia_legacy_id`: a card on chwd's legacy lists `/var/lib/chwd/ids/nvidia-*.ids` is older; without the
lists every card counts). `nvidia_present` = such a card + `nvidia_drm` available; the toggle's `nvidia_available`
is the same function. Older cards (GTX 10/9 series) are left alone: no promise they work with gamescope.
## Older cards and other research
Decision 2026-10-01: support RTX 20 series or newer only, with the VRAM booster's check (done in `lib/nvidia.sh`).
The user had hoped older GPUs would work too; not promised. Full notes with sources: **`nvidia/RESEARCH.md`**.
Short version: open kernel modules need Turing+, Maxwell/Pascal/Volta need the proprietary driver (580 legacy branch);
`nvidia-drm.modeset/fbdev` exist in both, so the fix applies to any card with `nvidia_drm`, but nothing found says gamescope
works better or worse there (a GTX 1050 Ti has a known gamescope>3.16.16 start problem on HDMI, not ours). Cannot be promised:
needs a test on a real older card. Design for the toggle: on by default for any card with `nvidia_drm`, the label says
what it does, `tests/nvidia-hardware-test.sh` reports GPU and driver so results can be compared.
## Still to do
1. [ ] **On the NVIDIA PC** (only place the real fix can be judged; the full steps, how to read the results and the undo
are in `nvidia/INSTRUCTIONS.md`), from the steamify-cachyos checkout on that branch:
`bash tests/nvidia-hardware-test.sh check` (before), `... apply` (asks sudo), reboot,
`... check` (after: `nvidia_drm modeset/fbdev = Y`), boot gaming mode, then
`... visual` (was the picture clean?). Everything goes to `~/steamify-nvidia-report.txt`:
paste it into the session.
2. [ ] If the parameters apply but the picture is still corrupted: look at the report's
gamescope/NVIDIA log lines and the versions (`nvidia-open`, `nvidia-utils`, `gamescope`,
`gamescope-session-cachyos`); the cause is then probably a gamescope/driver mismatch
for Blackwell, not this fix.
3. [ ] Not covered by any test: the early-load drop-in with the REAL NVIDIA modules (the VMs only have renamed
fake ones), and systemd-boot/GRUB with a pacman transaction (only Limine was run).
4. [ ] **Verify the RTX 20+ check on the PC** (decided 2026-10-01, only unit-tested with a fake chwd list; this laptop and the VMs
have no `/var/lib/chwd/ids`): `bash tests/nvidia-hardware-test.sh check` prints the NVIDIA PCI id, whether it is on a chwd legacy
list, and whether the lists exist at all. Expect for the RTX 5080: "supported", "not on a legacy list". If it says "no chwd
legacy lists found" the check cannot tell old from new there (every card then counts as supported, like the VRAM booster):
look at what `chwd`/`/var/lib/chwd/ids` provide on CachyOS and whether another source (the open-driver device list, the
`nvidia-open` package) would be better for both the VRAM booster and this fix. Also try `check` on any older NVIDIA card
that turns up (GTX 10/9 series): it must say "older than RTX 20 ... not applied" and `apply` must change nothing.
5. [ ] Changelog: fill in the commit hashes of this feature's lines (next commit, per AGENTS.md).
6. [ ] Build the opt-out toggle (section above) before release.
7. [ ] When complete and tested: ask the user whether it may be released. On a yes delete
`.no-release-yet` (and this folder in the dev repo), open the PR into `release/2.9.7` (local branch only so
far, one commit `chore: Version 2.9.7`; push it then) and merge it. The user merges
`release/2.9.7` into `main`.
## Working rules (steamify-cachyos `AGENTS.md` and this repo's `AGENTS.md`)
- Never work on `main` or a `release/*` branch; no `Co-Authored-By` / "Generated with
Claude" lines in commits or PRs. No git identity on the dev laptop: use
`git -c user.name="Rick Peters" -c user.email="rickpeters@upriser.nl" commit`.
- Don't push `release/2.9.7` before the PR (a push can start a dev ISO build).
## If the VM tests need repeating (dev laptop, Ubuntu)
- `scripts/vminstall.sh` (steamify-cachyos-dev) with `VM_BOOTLOADER=limine|systemd-boot|grub`,
`CI=1`, a short `VM_DIR` (`~/vms/...`: socket paths over 108 bytes fail) and `VM_SSH_KEY`.
- This session's shell lacked the `kvm` group: run QEMU steps as `sg kvm -c "..."`.
- `/tmp/vminstall-iso.lock` belongs to another user and there is no `bsdtar`: use a copy of
`vminstall.sh` with another lock path and a `bsdtar` shim (Python `pycdlib` in a venv).
- Delete everything afterwards (`~/vms` incl. `pkg-cache`, the 3 GB ISO, keys).
+2 -9
View File
@@ -17,15 +17,8 @@ VM_USER="${VM_USER:-theupriser}"
VM_SSH_OPTS=(-o BatchMode=yes -o "UserKnownHostsFile=$VM_DIR/known_hosts" -o StrictHostKeyChecking=accept-new) VM_SSH_OPTS=(-o BatchMode=yes -o "UserKnownHostsFile=$VM_DIR/known_hosts" -o StrictHostKeyChecking=accept-new)
if [[ -z "${VM_SSH_KEY:-}" && -f "$VM_DIR/ssh-key" ]]; then VM_SSH_KEY="$(cat "$VM_DIR/ssh-key")"; fi if [[ -z "${VM_SSH_KEY:-}" && -f "$VM_DIR/ssh-key" ]]; then VM_SSH_KEY="$(cat "$VM_DIR/ssh-key")"; fi
[[ -n "${VM_SSH_KEY:-}" ]] && VM_SSH_OPTS+=(-i "$VM_SSH_KEY" -o IdentitiesOnly=yes) [[ -n "${VM_SSH_KEY:-}" ]] && VM_SSH_OPTS+=(-i "$VM_SSH_KEY" -o IdentitiesOnly=yes)
# The VM's key, looked up at every call: vminstall.sh and vmbootloadertest.sh source this file before the key vm_ssh() { ssh -p "$VM_PORT" "${VM_SSH_OPTS[@]}" "$VM_USER@$VM_HOST" "$@"; }
# exists or is chosen (a clean host has no other key the VM would accept). vm_scp() { scp -q -P "$VM_PORT" "${VM_SSH_OPTS[@]}" "$@"; }
vm_key() {
if [[ -n "${VM_SSH_KEY:-}" ]]; then printf '%s\n' "$VM_SSH_KEY"
elif [[ -f "$VM_DIR/ssh-key" ]]; then cat "$VM_DIR/ssh-key"; fi
}
_vm_key_opts() { local k; k="$(vm_key)"; [[ -n "$k" ]] && printf '%s\n' -i "$k" -o IdentitiesOnly=yes; }
vm_ssh() { local -a k; mapfile -t k < <(_vm_key_opts); ssh -p "$VM_PORT" "${VM_SSH_OPTS[@]}" "${k[@]}" "$VM_USER@$VM_HOST" "$@"; }
vm_scp() { local -a k; mapfile -t k < <(_vm_key_opts); scp -q -P "$VM_PORT" "${VM_SSH_OPTS[@]}" "${k[@]}" "$@"; }
# One Konsole for every test run: it follows $TEST_LOG, which the test scripts append to. # One Konsole for every test run: it follows $TEST_LOG, which the test scripts append to.
# Started only when there is a desktop, the run is headless and no such window exists. # Started only when there is a desktop, the run is headless and no such window exists.
+16 -44
View File
@@ -5,54 +5,45 @@
# the old drm.edid_firmware parameter), a kernel update, and a reboot into # the old drm.edid_firmware parameter), a kernel update, and a reboot into
# each of the two kernels. Guest checks: share/bootloader-test/*.sh. # each of the two kernels. Guest checks: share/bootloader-test/*.sh.
# All loaders in one go: scripts/vmtest.sh. # All loaders in one go: scripts/vmtest.sh.
# scripts/vmbootloadertest.sh <limine|systemd-boot|grub> [--install] [--quick [--generic]] [--window] # scripts/vmbootloadertest.sh <limine|systemd-boot|grub> [--install] [--window]
# --install first install the VM (scripts/vminstall.sh, from the Steamify ISO) # --install first install the VM (scripts/vminstall.sh, from the Steamify ISO)
# --quick only what the ISO's install must get right (B1-B5: boots through its own entry, OS name, loader,
# Steamify's state and modules as the install left them): no steamify.sh run, kernel update or reboots
# --generic with --quick: a plain PC, not a Steam Machine (no --fremont): its Steam Machine items must stay off
# --window show the VM's window (default: headless, with a Konsole on the log when there is a desktop) # --window show the VM's window (default: headless, with a Konsole on the log when there is a desktop)
# Env: VM_DIR (default ~/vms/bl-<loader>), VM_ISO (default: the newest ISO in # Env: VM_DIR (default ~/vms/bl-<loader>), VM_ISO (default: the newest ISO in
# ../steamify-cachyos-live-iso/out/desktop), REPO (steamify-cachyos, 9p `repo`). # ../steammachine-cachyos-live-iso/out/desktop), REPO (steamify-cachyos, 9p `repo`).
# Exit status: the number of failed checks. Only one VM runs at a time (port 2222). # Exit status: the number of failed checks. Only one VM runs at a time (port 2222).
set -uo pipefail set -uo pipefail
here="$(cd "$(dirname "$0")" && pwd)" here="$(cd "$(dirname "$0")" && pwd)"
repo="$(cd "$here/.." && pwd)" repo="$(cd "$here/.." && pwd)"
loader="${1:?usage: $0 <limine|systemd-boot|grub> [--install]}" loader="${1:?usage: $0 <limine|systemd-boot|grub> [--install]}"
install=false; window=false; quick=false; generic=false install=false; window=false
for a in "${@:2}"; do for a in "${@:2}"; do
case "$a" in --install) install=true ;; --quick) quick=true ;; --generic) generic=true ;; --window) window=true ;; *) echo "Unknown argument: $a" >&2; exit 2 ;; esac case "$a" in --install) install=true ;; --window) window=true ;; *) echo "Unknown argument: $a" >&2; exit 2 ;; esac
done done
# No human is needed: headless (no VM window) unless --window. On a desktop a Konsole follows the log. # No human is needed: headless (no VM window) unless --window. On a desktop a Konsole follows the log.
$window || export VM_HEADLESS=1 $window || export VM_HEADLESS=1
case "$loader" in limine|systemd-boot|grub) ;; *) echo "Unknown boot loader: $loader" >&2; exit 2 ;; esac case "$loader" in limine|systemd-boot|grub) ;; *) echo "Unknown boot loader: $loader" >&2; exit 2 ;; esac
$generic && ! $quick && { echo "--generic needs --quick" >&2; exit 2; } export VM_DIR="${VM_DIR:-$HOME/vms/bl-$loader}"
hw=fremont; $generic && hw=generic
suffix=""; $generic && suffix="-generic"
export VM_DIR="${VM_DIR:-$HOME/vms/bl-$loader$suffix}"
hwflags=(--fremont); $generic && hwflags=()
# Own ssh port per loader, so the loaders can run side by side (vmtest.sh). # Own ssh port per loader, so the loaders can run side by side (vmtest.sh).
off=0; $generic && off=10 case "$loader" in limine) export VM_PORT="${VM_PORT:-2401}" ;; systemd-boot) export VM_PORT="${VM_PORT:-2402}" ;; grub) export VM_PORT="${VM_PORT:-2403}" ;; esac
case "$loader" in limine) export VM_PORT="${VM_PORT:-$((2401 + off))}" ;; systemd-boot) export VM_PORT="${VM_PORT:-$((2402 + off))}" ;; grub) export VM_PORT="${VM_PORT:-$((2403 + off))}" ;; esac
. "$here/common.sh" . "$here/common.sh"
guest="$repo/share/bootloader-test" guest="$repo/share/bootloader-test"
log="$VM_DIR.test.log" log="$VM_DIR.test.log"
step() { printf '\n##### %s %s\n' "$(date +%T)" "$*"; } step() { printf '\n##### %s %s\n' "$(date +%T)" "$*"; }
gssh() { vm_ssh -o ConnectTimeout=6 -o LogLevel=ERROR "$@"; } gssh() { vm_ssh -o ConnectTimeout=6 -o LogLevel=ERROR "$@"; }
gscript() { gssh env "LOADER=$loader" "HARDWARE=$hw" bash -s < "$guest/$1"; } # the guest's login shell is fish gscript() { gssh env "LOADER=$loader" bash -s < "$guest/$1"; } # the guest's login shell is fish
# Up to ~15 minutes for the first ssh: a first boot is slow when several VMs run at once. waitssh() { local _; for _ in $(seq 60); do gssh 'uptime -p' 2>/dev/null | grep -q '^up' && return 0; sleep 5; done; echo "FAIL no SSH"; return 1; }
waitssh() { local _; for _ in $(seq 180); do gssh 'uptime -p' 2>/dev/null | grep -q '^up' && return 0; sleep 5; done; echo "FAIL no SSH"; return 1; }
running() { pgrep -f "[h]ostfwd=tcp::$VM_PORT-" >/dev/null; } running() { pgrep -f "[h]ostfwd=tcp::$VM_PORT-" >/dev/null; }
stop_vm() { stop_vm() {
running || return 0 running || return 0
# Whichever VM runs: its key and host key are not this VM's. # Whichever VM runs: its key and host key are not this VM's.
ssh -p "$VM_PORT" -i "$(vm_key)" -o BatchMode=yes -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR \ ssh -p "$VM_PORT" -i "$VM_SSH_KEY" -o BatchMode=yes -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR \
"$VM_USER@$VM_HOST" 'sudo systemctl poweroff' >/dev/null 2>&1 "$VM_USER@$VM_HOST" 'sudo systemctl poweroff' >/dev/null 2>&1
local _; for _ in $(seq 30); do running || return 0; sleep 2; done local _; for _ in $(seq 30); do running || return 0; sleep 2; done
pkill -f "[h]ostfwd=tcp::$VM_PORT-"; sleep 2 pkill -f "[h]ostfwd=tcp::$VM_PORT-"; sleep 2
} }
export VM_CACHE="${VM_CACHE-$HOME/vms/pkg-cache}" # 9p tag `cache`: Steamify's downloads once for every VM export VM_CACHE="${VM_CACHE-$HOME/vms/pkg-cache}" # 9p tag `cache`: Steamify's downloads once for every VM
start_vm() { stop_vm; cp "$repo/run.sh" "$VM_DIR/run.sh"; (cd "$VM_DIR" && VM_MEM="${VM_MEM:-4G}" REPO="${REPO:-$repo/../steamify-cachyos}" setsid nohup ./run.sh "${hwflags[@]}" > vm.log 2>&1 &); } start_vm() { stop_vm; cp "$repo/run.sh" "$VM_DIR/run.sh"; (cd "$VM_DIR" && VM_MEM="${VM_MEM:-4G}" REPO="${REPO:-$repo/../steamify-cachyos}" setsid nohup ./run.sh --fremont > vm.log 2>&1 &); }
reboot_vm() { gssh 'sudo systemctl reboot' >/dev/null 2>&1; sleep 25; waitssh; } reboot_vm() { gssh 'sudo systemctl reboot' >/dev/null 2>&1; sleep 25; waitssh; }
view_start # one shared Konsole (common.sh) view_start # one shared Konsole (common.sh)
# The guest checks print PASS/FAIL lines; the summary at the end counts them in the log. # The guest checks print PASS/FAIL lines; the summary at the end counts them in the log.
@@ -63,7 +54,7 @@ printf '\n===== vmbootloadertest.sh %s (%s) =====\n' "$loader" "$(date +%T)"
if $install; then if $install; then
step "install $loader VM in $VM_DIR" step "install $loader VM in $VM_DIR"
stop_vm stop_vm
iso="${VM_ISO:-$(ls -t "$repo"/../steamify-cachyos-live-iso/out/desktop/*.iso 2>/dev/null | head -n 1)}" iso="${VM_ISO:-$(ls -t "$repo"/../steammachine-cachyos-live-iso/out/desktop/*.iso 2>/dev/null | head -n 1)}"
[[ -f "$iso" ]] || { echo "No ISO: set VM_ISO"; exit 2; } [[ -f "$iso" ]] || { echo "No ISO: set VM_ISO"; exit 2; }
if [[ -f "$VM_DIR/disk.qcow2" ]]; then if [[ -f "$VM_DIR/disk.qcow2" ]]; then
[[ -t 0 ]] || { echo "$VM_DIR has a disk; run this from a terminal to replace it"; exit 2; } [[ -t 0 ]] || { echo "$VM_DIR has a disk; run this from a terminal to replace it"; exit 2; }
@@ -71,32 +62,19 @@ if $install; then
[[ "$a" == YES ]] || exit 1 [[ "$a" == YES ]] || exit 1
rm -f "$VM_DIR/disk.qcow2" rm -f "$VM_DIR/disk.qcow2"
fi fi
# --quick checks what the ISO's install leaves on a Steam Machine: install on a VM that reports Fremont hardware VM_BOOTLOADER="$loader" "$here/vminstall.sh" --iso "$iso" < /dev/null || exit 1
# (a generic PC rightly gets no poweroff/CEC items and no DKMS modules).
fremont=(); $quick && ! $generic && fremont=(--fremont)
VM_BOOTLOADER="$loader" "$here/vminstall.sh" --iso "$iso" "${fremont[@]}" < /dev/null || exit 1
fi fi
step "start $loader as a $($generic && echo plain PC || echo Steam Machine)" step "start $loader as a Steam Machine"
start_vm; waitssh || exit 1 start_vm; waitssh || exit 1
gssh 'sudo cat /sys/class/dmi/id/product_name' 2>/dev/null gssh 'sudo cat /sys/class/dmi/id/product_name' 2>/dev/null
if $quick; then
step "the installed system: Steamify's state, OS name, boot loader"; gscript steamify-state.sh | check
step "Steamify's modules"; gscript modules.sh | check
step "boot and its boot entry"; gscript boot-check.sh | check
else
step "Steamify's Steam Machine options" step "Steamify's Steam Machine options"
gssh 'sudo mkdir -p /var/cache/steamify-pkg /var/cache/steamify; sudo mountpoint -q /var/cache/steamify-pkg || sudo mount -t 9p -o trans=virtio,version=9p2000.L cache /var/cache/steamify-pkg && { sudo mkdir -p /var/cache/steamify-pkg/steamify; sudo mount --bind /var/cache/steamify-pkg/steamify /var/cache/steamify; }' 2>/dev/null gssh 'sudo mkdir -p /var/cache/steamify-pkg /var/cache/steamify; sudo mountpoint -q /var/cache/steamify-pkg || sudo mount -t 9p -o trans=virtio,version=9p2000.L cache /var/cache/steamify-pkg && { sudo mkdir -p /var/cache/steamify-pkg/steamify; sudo mount --bind /var/cache/steamify-pkg/steamify /var/cache/steamify; }' 2>/dev/null
gssh 'sudo mountpoint -q /mnt || sudo mount -t 9p -o trans=virtio,version=9p2000.L repo /mnt; cd /mnt && nohup bash steamify.sh --defaults --options gaming,theme,glyphs,single,launcher,notify,vram,cec,machine,poweroff --boot gamescope > /tmp/steamify.log 2>&1 < /dev/null &' gssh 'sudo mountpoint -q /mnt || sudo mount -t 9p -o trans=virtio,version=9p2000.L repo /mnt; cd /mnt && nohup bash steamify.sh --defaults --options gaming,theme,glyphs,single,launcher,notify,vram,cec,machine,poweroff --boot gamescope > /tmp/steamify.log 2>&1 < /dev/null &'
sleep 20 sleep 20
# [s]: the pattern must not match this ssh command itself. # [s]: the pattern must not match this ssh command itself.
n=0 for _ in $(seq 90); do gssh 'pgrep -f "[s]teamify.sh --defaults" >/dev/null && echo R || echo D' 2>/dev/null | grep -q D && break; sleep 10; done
for _ in $(seq 90); do
gssh 'pgrep -f "[s]teamify.sh --defaults" >/dev/null && echo R || echo D' 2>/dev/null | grep -q D && break
(( n++ % 6 == 0 )) && echo " .. steamify.sh running: $(gssh 'tail -n 1 /tmp/steamify.log' 2>/dev/null | sed 's/\x1b\[[0-9;]*m//g' | cut -c1-110)"
sleep 10
done
gssh 'sed "s/\x1b\[[0-9;]*m//g" /tmp/steamify.log | grep -E "^\[(WARN|ERROR)\]" | cut -c1-140' gssh 'sed "s/\x1b\[[0-9;]*m//g" /tmp/steamify.log | grep -E "^\[(WARN|ERROR)\]" | cut -c1-140'
gssh 'tail -n 1 /tmp/steamify.log | sed "s/\x1b\[[0-9;]*m//g"' | grep -q '^\[OK\] Done' && echo "PASS Steamify finished" || { echo "FAIL Steamify did not finish"; } gssh 'tail -n 1 /tmp/steamify.log | sed "s/\x1b\[[0-9;]*m//g"' | grep -q '^\[OK\] Done' && echo "PASS Steamify finished" || { echo "FAIL Steamify did not finish"; }
@@ -134,16 +112,10 @@ b="$(gssh uname -r)"
[[ "$loader" == grub ]] && gssh "sudo sed -i 's/^GRUB_DEFAULT=.*/GRUB_DEFAULT=$gg/' /etc/default/grub && sudo grub-mkconfig -o /boot/grub/grub.cfg >/dev/null 2>&1" [[ "$loader" == grub ]] && gssh "sudo sed -i 's/^GRUB_DEFAULT=.*/GRUB_DEFAULT=$gg/' /etc/default/grub && sudo grub-mkconfig -o /boot/grub/grub.cfg >/dev/null 2>&1"
[[ "$loader" == limine ]] && gssh "sudo sed -i -e 's/^default_entry: .*/default_entry: $cur/' -e 's/^remember_last_entry: .*/remember_last_entry: ${rem:-yes}/' /boot/limine.conf" [[ "$loader" == limine ]] && gssh "sudo sed -i -e 's/^default_entry: .*/default_entry: $cur/' -e 's/^remember_last_entry: .*/remember_last_entry: ${rem:-yes}/' /boot/limine.conf"
fi
step "DONE" step "DONE"
stop_vm stop_vm
} 2>&1 | tee "$log" | tee >(sed -u "s/^/[$loader] /" >> "$TEST_LOG") | sed -u 's/\x1b\[[0-9;]*m//g' } 2>&1 | tee "$log" | tee >(sed -u "s/^/[$loader] /" >> "$TEST_LOG") | sed 's/\x1b\[[0-9;]*m//g'
rc=${PIPESTATUS[0]} # the test itself: an abort (exit inside the braces) must not pass as 0 failed checks
fails="$(grep -c '^FAIL' "$log")" fails="$(grep -c '^FAIL' "$log")"
if [[ $rc -ne 0 && $fails -eq 0 ]]; then echo; echo "== $loader: $(grep -c '^PASS' "$log") passed, $fails failed (log: $log)"
echo "FAIL $loader aborted (exit $rc): $(grep -v '^$' "$log" | tail -n 1)" | tee -a "$log"; fails=1
fi
echo; echo "== $loader$suffix: $(grep -c '^PASS' "$log") passed, $fails failed (log: $log)"
grep '^FAIL' "$log" grep '^FAIL' "$log"
exit "$fails" exit "$fails"
+3 -21
View File
@@ -11,7 +11,6 @@
# Env: VM_DIR (see common.sh), VM_USER (default: your host username; theupriser when that is root), # Env: VM_DIR (see common.sh), VM_USER (default: your host username; theupriser when that is root),
# VM_PASSWORD (steamify), VM_SSH_KEY (private key to authorize; default # VM_PASSWORD (steamify), VM_SSH_KEY (private key to authorize; default
# ~/.ssh/steamify-vm_ed25519, asked once when that doesn't exist), REPO (shared as 9p `repo`, default ../steamify-cachyos), # ~/.ssh/steamify-vm_ed25519, asked once when that doesn't exist), REPO (shared as 9p `repo`, default ../steamify-cachyos),
# VM_INSTALL_MEM (RAM of the live installer VM only; default VM_MEM, else run.sh's 8G),
# VM_CACHE (host dir for the packages, default ~/vms/pkg-cache; empty = none), # VM_CACHE (host dir for the packages, default ~/vms/pkg-cache; empty = none),
# VM_STEAMIFY (a Steamify ISO also runs steamify-install: the Steamify page's ids, # VM_STEAMIFY (a Steamify ISO also runs steamify-install: the Steamify page's ids,
# empty = defaults, skip = plain CachyOS), VM_BOOTLOADER (limine; or systemd-boot, grub), VM_TIMEZONE (the host's), VM_HOST_IP (the host as the guest sees it, # empty = defaults, skip = plain CachyOS), VM_BOOTLOADER (limine; or systemd-boot, grub), VM_TIMEZONE (the host's), VM_HOST_IP (the host as the guest sees it,
@@ -19,7 +18,6 @@
# The VM's user and key are recorded in $VM_DIR (vm-user, ssh-key) for the # The VM's user and key are recorded in $VM_DIR (vm-user, ssh-key) for the
# other scripts. Never touches your existing SSH keys or ~/.ssh/known_hosts. # other scripts. Never touches your existing SSH keys or ~/.ssh/known_hosts.
set -euo pipefail set -euo pipefail
[[ -n "${CI:-}" ]] && export VM_HEADLESS=1 # no display in CI (QEMU fails with "OpenGL is not supported by the display")
here="$(cd "$(dirname "$0")" && pwd)" here="$(cd "$(dirname "$0")" && pwd)"
repo="$(cd "$here/.." && pwd)" repo="$(cd "$here/.." && pwd)"
[[ -n "${VM_USER:-}" ]] || VM_USER="$(id -un)" [[ -n "${VM_USER:-}" ]] || VM_USER="$(id -un)"
@@ -89,15 +87,7 @@ boot="$VM_DIR/iso-boot"
mkdir -p "$boot" mkdir -p "$boot"
# One loop setup at a time: parallel installs (vmtest.sh --install) otherwise get "Device or resource busy". # One loop setup at a time: parallel installs (vmtest.sh --install) otherwise get "Device or resource busy".
exec 9> /tmp/vminstall-iso.lock; flock 9 exec 9> /tmp/vminstall-iso.lock; flock 9
if command -v bsdtar >/dev/null; then if ! command -v udisksctl >/dev/null && [[ $EUID -eq 0 ]]; then
# No loop device, no udisks, no root: libarchive reads the ISO9660 image directly (a CI container has no
# loop partition nodes, so the mounts below can't work there).
mnt="$(mktemp -d)"
cleanup_loop() { chmod -R u+rwX "$mnt" 2>/dev/null || true; rm -rf "$mnt" 2>/dev/null || true; } # the ISO's files come out read-only
trap cleanup_loop EXIT
bsdtar -xf "$iso" -C "$mnt" arch/boot/x86_64/vmlinuz-linux-cachyos arch/boot/x86_64/initramfs-linux-cachyos.img boot EFI 2>/dev/null || true
[[ -f "$mnt/arch/boot/x86_64/vmlinuz-linux-cachyos" ]] || { echo "bsdtar found no kernel in the ISO." >&2; exit 1; }
elif ! command -v udisksctl >/dev/null && [[ $EUID -eq 0 ]]; then
# No udisks (the WSL box, as root): a plain loop device and mount. # No udisks (the WSL box, as root): a plain loop device and mount.
loop="$(losetup -f -r -P --show "$iso")" loop="$(losetup -f -r -P --show "$iso")"
mnt="$(mktemp -d)" mnt="$(mktemp -d)"
@@ -172,27 +162,19 @@ append="$search archisobasedir=arch cow_spacesize=10G module_blacklist=pcspkr co
append+=" systemd.run=\"/usr/bin/bash -c 'curl -fsS --retry 150 --retry-all-errors --retry-delay 2 http://$host_ip:$port/vminstall-live.sh | bash'\"" append+=" systemd.run=\"/usr/bin/bash -c 'curl -fsS --retry 150 --retry-all-errors --retry-delay 2 http://$host_ip:$port/vminstall-live.sh | bash'\""
append+=" systemd.run_success_action=none systemd.run_failure_action=none systemd.wants=kernel-command-line.service" append+=" systemd.run_success_action=none systemd.run_failure_action=none systemd.wants=kernel-command-line.service"
echo "Installing: watch the VM's window, or follow the console with tail -f $VM_DIR/serial.log (install log: $www/install.log)..." echo "Installing: watch the VM's window, or follow the console with tail -f $VM_DIR/serial.log (install log: $www/install.log)..."
VM_MEM="${VM_INSTALL_MEM:-${VM_MEM:-}}" VM_SERIAL="$VM_DIR/serial.log" VM_ISO="$iso" VM_KERNEL="$boot/vmlinuz-linux-cachyos" VM_INITRD="$boot/initramfs-linux-cachyos.img" VM_APPEND="$append" \ VM_SERIAL="$VM_DIR/serial.log" VM_ISO="$iso" VM_KERNEL="$boot/vmlinuz-linux-cachyos" VM_INITRD="$boot/initramfs-linux-cachyos.img" VM_APPEND="$append" \
./run.sh install "${run_flags[@]}" > vm-install.log 2>&1 & ./run.sh install "${run_flags[@]}" > vm-install.log 2>&1 &
qemu=$! qemu=$!
# 90 minutes; a failure reported by the live system ends it at once (it stays up after one) # 90 minutes; a failure reported by the live system ends it at once (it stays up after one)
waited=0
for _ in $(seq 1080); do for _ in $(seq 1080); do
kill -0 $qemu 2>/dev/null || break kill -0 $qemu 2>/dev/null || break
# A sign of life every minute (CI shows nothing else for ~10 minutes): where the live installer is.
(( waited++ % 12 == 0 )) && echo " .. installing, $((waited * 5 / 60)) min: $(tail -n 1 "$www/install.log" 2>/dev/null | cut -c1-110)"
[[ -s "$www/status" && "$(cat "$www/status")" != ok ]] && { sleep 5; kill $qemu 2>/dev/null; wait $qemu 2>/dev/null; break; } [[ -s "$www/status" && "$(cat "$www/status")" != ok ]] && { sleep 5; kill $qemu 2>/dev/null; wait $qemu 2>/dev/null; break; }
sleep 5 sleep 5
done done
kill -0 $qemu 2>/dev/null && { echo "The install didn't finish in 90 minutes; see $www/install.log." >&2; kill $qemu; exit 1; } kill -0 $qemu 2>/dev/null && { echo "The install didn't finish in 90 minutes; see $www/install.log." >&2; kill $qemu; exit 1; }
status="$(cat "$www/status" 2>/dev/null || echo "no status (the live system never reported back)")" status="$(cat "$www/status" 2>/dev/null || echo "no status (the live system never reported back)")"
cp "$www/install.log" install.log 2>/dev/null || true cp "$www/install.log" install.log 2>/dev/null || true
if [[ "$status" != ok ]]; then [[ "$status" == ok ]] || { echo "Install failed: $status. Log: $VM_DIR/install.log" >&2; exit 1; }
echo "Install failed: $status. Log: $VM_DIR/install.log" >&2
# In CI only the job log survives: show why.
for f in vm-install.log serial.log install.log; do [[ -s "$f" ]] && { echo "--- $f (last 40 lines)" >&2; tail -n 40 "$f" | sed 's/\x1b\[[0-9;]*[A-Za-z]//g' >&2; }; done
exit 1
fi
qemu-img snapshot -c clean disk.qcow2 && cp vars.fd vars.clean.fd qemu-img snapshot -c clean disk.qcow2 && cp vars.fd vars.clean.fd
echo "Installed; snapshot 'clean' taken." echo "Installed; snapshot 'clean' taken."
+3 -3
View File
@@ -9,7 +9,7 @@
# --fresh a new, empty disk (else the existing one: e.g. boot the # --fresh a new, empty disk (else the existing one: e.g. boot the
# installed system with `./run.sh` in $VM_DIR afterwards) # installed system with `./run.sh` in $VM_DIR afterwards)
# --iso <file> default: the newest out/desktop/*.iso of the host's # --iso <file> default: the newest out/desktop/*.iso of the host's
# steamify-cachyos-live-iso checkout # steammachine-cachyos-live-iso checkout
# Env: ISO_VM_DIR (~/vms/iso-vm), ISO_VM_PORT (2223: runs next to the test # Env: ISO_VM_DIR (~/vms/iso-vm), ISO_VM_PORT (2223: runs next to the test
# VM on 2222). # VM on 2222).
set -euo pipefail set -euo pipefail
@@ -26,8 +26,8 @@ while [[ $# -gt 0 ]]; do
esac esac
done done
if [[ -z "$iso" ]]; then if [[ -z "$iso" ]]; then
iso="$(ls -t "$(dirname "$repo")"/steamify-cachyos-live-iso/out/desktop/*.iso 2>/dev/null | head -1)" iso="$(ls -t "$(dirname "$repo")"/steammachine-cachyos-live-iso/out/desktop/*.iso 2>/dev/null | head -1)"
[[ -n "$iso" ]] || { echo "No ISO in steamify-cachyos-live-iso/out/desktop: build one (vmisobuild.sh) or pass --iso." >&2; exit 1; } [[ -n "$iso" ]] || { echo "No ISO in steammachine-cachyos-live-iso/out/desktop: build one (vmisobuild.sh) or pass --iso." >&2; exit 1; }
fi fi
iso="$(cd "$(dirname "$iso")" && pwd)/$(basename "$iso")" iso="$(cd "$(dirname "$iso")" && pwd)/$(basename "$iso")"
mkdir -p "$dir" mkdir -p "$dir"
+8 -8
View File
@@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# Build the Steam Machine ISO (steamify-cachyos-live-iso) in the test VM # Build the Steam Machine ISO (steammachine-cachyos-live-iso) in the test VM
# (directly, no container: build-live-modules.sh and build-calamares-modules.sh with sudo, buildiso.sh as # (directly, no container: build-live-modules.sh and build-calamares-modules.sh with sudo, buildiso.sh as
# the user, it sudos itself), # the user, it sudos itself),
# visibly (a Konsole on the VM's desktop follows the log), with the package # visibly (a Konsole on the VM's desktop follows the log), with the package
@@ -10,17 +10,17 @@
# The VM must run with its cache shared (one per VM, next to its disk), and # The VM must run with its cache shared (one per VM, next to its disk), and
# gets all host cores but 2 for the build: start it with # gets all host cores but 2 for the build: start it with
# VM_CACHE=$VM_DIR/iso-cache VM_CPUS=$(( $(nproc) * 3 / 4 )) scripts/vmreset.sh --fremont # VM_CACHE=$VM_DIR/iso-cache VM_CPUS=$(( $(nproc) * 3 / 4 )) scripts/vmreset.sh --fremont
# Env: VM_DIR (see common.sh), ISO_BRANCH (master), ISO_OUT (the host's # Env: VM_DIR (see common.sh), ISO_BRANCH (feat/steamify), ISO_OUT (the host's
# steamify-cachyos-live-iso/out/desktop). # steammachine-cachyos-live-iso/out/desktop).
set -euo pipefail set -euo pipefail
here="$(cd "$(dirname "$0")" && pwd)" here="$(cd "$(dirname "$0")" && pwd)"
. "$here/common.sh" . "$here/common.sh"
steamify="" steamify=""
[[ "${1:-}" == --steamify ]] && steamify="${2:?--steamify needs a guest path}" [[ "${1:-}" == --steamify ]] && steamify="${2:?--steamify needs a guest path}"
branch="${ISO_BRANCH:-master}" branch="${ISO_BRANCH:-feat/steamify}"
# Where the ISO lands: out/desktop of the host's live-ISO checkout (next to # Where the ISO lands: out/desktop of the host's live-ISO checkout (next to
# this repo), where a local build would put it. # this repo), where a local build would put it.
out="${ISO_OUT:-$(dirname "$(cd "$here/.." && pwd)")/steamify-cachyos-live-iso/out/desktop}" out="${ISO_OUT:-$(dirname "$(cd "$here/.." && pwd)")/steammachine-cachyos-live-iso/out/desktop}"
mkdir -p "$out" mkdir -p "$out"
{ printf 'branch=%q steamify=%q\n' "$branch" "$steamify"; cat << 'REMOTE' { printf 'branch=%q steamify=%q\n' "$branch" "$steamify"; cat << 'REMOTE'
@@ -41,9 +41,9 @@ mountpoint -q /var/cache/pacman/pkg || sudo mount --bind /var/cache/steamify-iso
# build fails. mkarchiso uses this mirrorlist, so skip it here. # build fails. mkarchiso uses this mirrorlist, so skip it here.
sudo sed -i '/archlinux\.cachyos\.org/s/^Server/#Server/' /etc/pacman.d/mirrorlist sudo sed -i '/archlinux\.cachyos\.org/s/^Server/#Server/' /etc/pacman.d/mirrorlist
sudo pacman -Sy --needed --noconfirm archiso mkinitcpio-archiso git squashfs-tools grub >/dev/null sudo pacman -Sy --needed --noconfirm archiso mkinitcpio-archiso git squashfs-tools grub >/dev/null
iso=~/projects/steamify-cachyos-live-iso iso=~/projects/steammachine-cachyos-live-iso
if [ -d "$iso/.git" ]; then git -C "$iso" fetch -q && git -C "$iso" checkout -q "$branch" && git -C "$iso" reset -q --hard "origin/$branch" if [ -d "$iso/.git" ]; then git -C "$iso" fetch -q && git -C "$iso" checkout -q "$branch" && git -C "$iso" reset -q --hard "origin/$branch"
else mkdir -p ~/projects && git clone -q -b "$branch" https://github.com/theupriser/steamify-cachyos-live-iso "$iso"; fi else mkdir -p ~/projects && git clone -q -b "$branch" https://github.com/theupriser/steammachine-cachyos-live-iso "$iso"; fi
cd "$iso" && echo "ISO repo: $(git log --oneline -1)" cd "$iso" && echo "ISO repo: $(git log --oneline -1)"
./steamify-prepare.sh $steamify | tail -1 ./steamify-prepare.sh $steamify | tail -1
sudo rm -rf build out sudo rm -rf build out
@@ -61,7 +61,7 @@ REMOTE
} | vm_ssh bash -s } | vm_ssh bash -s
echo "Building in the VM (its Konsole shows the log)..." echo "Building in the VM (its Konsole shows the log)..."
until vm_ssh 'grep -q "^== BUILD EXIT" ~/projects/iso-build.log'; do sleep 30; done until vm_ssh 'grep -q "^== BUILD EXIT" ~/projects/iso-build.log'; do sleep 30; done
iso="$(vm_ssh "bash -c 'ls -t ~/projects/steamify-cachyos-live-iso/out/desktop/*.iso 2>/dev/null | head -1'")" iso="$(vm_ssh "bash -c 'ls -t ~/projects/steammachine-cachyos-live-iso/out/desktop/*.iso 2>/dev/null | head -1'")"
[[ -n "$iso" ]] || { echo "No ISO was built; see ~/projects/iso-build.log in the VM." >&2; exit 1; } [[ -n "$iso" ]] || { echo "No ISO was built; see ~/projects/iso-build.log in the VM." >&2; exit 1; }
vm_scp "$VM_USER@$VM_HOST:$iso" "$out/" vm_scp "$VM_USER@$VM_HOST:$iso" "$out/"
echo "ISO: $out/$(basename "$iso") (cache: $VM_DIR/iso-cache)" echo "ISO: $out/$(basename "$iso") (cache: $VM_DIR/iso-cache)"
+4 -4
View File
@@ -1,20 +1,20 @@
#!/bin/bash #!/bin/bash
# Progress of test jobs, one line per change (for a monitor; see the progress-report skill). # Progress of test jobs, one line per change (for a monitor; see the progress-report skill).
# scripts/vmprogress.sh [--once] [job...] jobs: limine systemd-boot grub limine-generic systemd-boot-generic grub-generic cli menu hw installer toggles nvidia (default all) # scripts/vmprogress.sh [--once] [job...] jobs: limine systemd-boot grub cli menu hw installer toggles (default all)
# Per job (only a new step, a finished job or a FAIL counts as a change): state (install NN% <stage> / <test step> / done / -), checks passed/expected, failed. # Per job (only a new step, a finished job or a FAIL counts as a change): state (install NN% <stage> / <test step> / done / -), checks passed/expected, failed.
# Line: "HH:MM total P/E F failed ;; job | state | P/E | F failed ;; ...". Prints nothing while nothing # Line: "HH:MM total P/E F failed ;; job | state | P/E | F failed ;; ...". Prints nothing while nothing
# changes (checked every 30 s), "ALLDONE" and exits once every started job has its summary line. # changes (checked every 30 s), "ALLDONE" and exits once every started job has its summary line.
# Expected counts: the last complete run of each (AGENTS.md "State"); update them when checks are added. # Expected counts: the last complete run of each (AGENTS.md "State"); update them when checks are added.
set -uo pipefail set -uo pipefail
declare -A expect=([limine]=43 [systemd-boot]=42 [grub]=42 [cli]=65 [menu]=37 [hw]=78 [installer]=4 [toggles]=42 [nvidia]=43 [limine-generic]=25 [systemd-boot-generic]=25 [grub-generic]=25) declare -A expect=([limine]=43 [systemd-boot]=42 [grub]=42 [cli]=55 [menu]=36 [hw]=77 [installer]=4 [toggles]=42)
once=false; jobs=() once=false; jobs=()
for a in "$@"; do [[ "$a" == --once ]] && once=true || jobs+=("$a"); done for a in "$@"; do [[ "$a" == --once ]] && once=true || jobs+=("$a"); done
[[ ${#jobs[@]} -gt 0 ]] || jobs=(limine systemd-boot grub limine-generic systemd-boot-generic grub-generic cli menu hw installer toggles nvidia) [[ ${#jobs[@]} -gt 0 ]] || jobs=(limine systemd-boot grub cli menu hw installer toggles)
vms="$HOME/vms" vms="$HOME/vms"
status() { # status <job>: "state|pass|fail|finished(0/1)|started(0/1)" status() { # status <job>: "state|pass|fail|finished(0/1)|started(0/1)"
local j="$1" log dir local j="$1" log dir
case "$j" in limine|systemd-boot|grub|limine-generic|systemd-boot-generic|grub-generic) log="$vms/bl-$j.test.log" dir="$vms/bl-$j" ;; *) log="$vms/run-$j.log" dir="$vms/run-$j" ;; esac case "$j" in limine|systemd-boot|grub) log="$vms/bl-$j.test.log" dir="$vms/bl-$j" ;; *) log="$vms/run-$j.log" dir="$vms/run-$j" ;; esac
[[ -f "$log" ]] || { echo "-|0|0|0|0"; return; } [[ -f "$log" ]] || { echo "-|0|0|0|0"; return; }
local p f st run local p f st run
# The logs are appended to: only the latest run counts (from its "===== vm..." header on). # The logs are appended to: only the latest run counts (from its "===== vm..." header on).
+1 -1
View File
@@ -16,7 +16,7 @@ export XDG_RUNTIME_DIR=/run/user/$uid DBUS_SESSION_BUS_ADDRESS=unix:path=/run/us
rm -f /tmp/vmshot.png rm -f /tmp/vmshot.png
if [ -n "$clean" ]; then if [ -n "$clean" ]; then
# Steam as a whole: closing only its web helper leaves a black window. # Steam as a whole: closing only its web helper leaves a black window.
# It comes back at the next login (Steam autostart entry). # It comes back at the next login (steam-desktop-autostart).
steam -shutdown >/dev/null 2>&1 & sleep 5; pkill -u "$USER" -x steam; pkill -u "$USER" -x steamwebhelper steam -shutdown >/dev/null 2>&1 & sleep 5; pkill -u "$USER" -x steam; pkill -u "$USER" -x steamwebhelper
pkill -u "$USER" cachyos-hello; pkill -u "$USER" -x konsole pkill -u "$USER" cachyos-hello; pkill -u "$USER" -x konsole
sleep 2 sleep 2
+1 -1
View File
@@ -8,7 +8,7 @@ yn() { [ -e "$1" ] && echo y || echo n; }
echo "DM=$(systemctl show -p Id --value display-manager) sddm-autologin=$(yn /etc/sddm.conf.d/10-gamescope-autologin.conf) sync=$(yn /etc/systemd/system/sync-steamos-session.path) sudoers=$(sudo test -f /etc/sudoers.d/gamescope-session-switch && echo y || echo n)" echo "DM=$(systemctl show -p Id --value display-manager) sddm-autologin=$(yn /etc/sddm.conf.d/10-gamescope-autologin.conf) sync=$(yn /etc/systemd/system/sync-steamos-session.path) sudoers=$(sudo test -f /etc/sudoers.d/gamescope-session-switch && echo y || echo n)"
echo "plasmalogin [Autologin]: $(sed -n '/^\[Autologin\]/,/^\[/p' /etc/plasmalogin.conf | grep -v '^\[' | tr '\n' ' ')" echo "plasmalogin [Autologin]: $(sed -n '/^\[Autologin\]/,/^\[/p' /etc/plasmalogin.conf | grep -v '^\[' | tr '\n' ' ')"
echo "shortcut=$(grep -h Exec= ~/Desktop/*Gaming*.desktop 2>/dev/null || echo none)" echo "shortcut=$(grep -h Exec= ~/Desktop/*Gaming*.desktop 2>/dev/null || echo none)"
echo "steam-autostart=$(grep -qs "^Exec=.*-silent" ~/.config/autostart/steam.desktop && echo silent || ([ -f ~/.config/autostart/steam.desktop ] && echo plain || echo none)) glyphs=$(yn ~/.config/environment.d/99-gamescope-steam-glyphs.conf)" echo "steam-unit=$([ -f ~/.config/systemd/user/steam-desktop-autostart.service ] && systemctl --user is-enabled steam-desktop-autostart.service || echo none) glyphs=$(yn ~/.config/environment.d/99-gamescope-steam-glyphs.conf)"
echo "lnf=$(kreadconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage) dark-lnf=$(kreadconfig6 --file kdeglobals --group KDE --key DefaultDarkLookAndFeel) scheme=$(kreadconfig6 --file kdeglobals --group General --key ColorScheme) cursor=$(kreadconfig6 --file kcminputrc --group Mouse --key cursorTheme) vapor-pkg=$(pacman -Q cachyos-vapor 2>/dev/null || echo none) layout-backup=$(yn ~/.local/state/steamify/theme-layout)" echo "lnf=$(kreadconfig6 --file kdeglobals --group KDE --key LookAndFeelPackage) dark-lnf=$(kreadconfig6 --file kdeglobals --group KDE --key DefaultDarkLookAndFeel) scheme=$(kreadconfig6 --file kdeglobals --group General --key ColorScheme) cursor=$(kreadconfig6 --file kcminputrc --group Mouse --key cursorTheme) vapor-pkg=$(pacman -Q cachyos-vapor 2>/dev/null || echo none) layout-backup=$(yn ~/.local/state/steamify/theme-layout)"
echo "wallpaper: $(grep -h '^Image=' ~/.config/plasma-org.kde.plasma.desktop-appletsrc | sort -u | tr '\n' ' ')" echo "wallpaper: $(grep -h '^Image=' ~/.config/plasma-org.kde.plasma.desktop-appletsrc | sort -u | tr '\n' ' ')"
echo "lock_screen=$(kreadconfig6 --file kdeglobals --group 'KDE Action Restrictions' --key action/lock_screen) autolock=$(kreadconfig6 --file kscreenlockerrc --group Daemon --key Autolock) metaL=$(kreadconfig6 --file kglobalshortcutsrc --group ksmserver --key 'Lock Session' | cut -c1-12)" echo "lock_screen=$(kreadconfig6 --file kdeglobals --group 'KDE Action Restrictions' --key action/lock_screen) autolock=$(kreadconfig6 --file kscreenlockerrc --group Daemon --key Autolock) metaL=$(kreadconfig6 --file kglobalshortcutsrc --group ksmserver --key 'Lock Session' | cut -c1-12)"
+1 -5
View File
@@ -32,7 +32,7 @@ suite="${1:?usage: $0 <suite> [--window] | --prepare}"
[[ "${2:-}" == --window ]] || export VM_HEADLESS=1 [[ "${2:-}" == --window ]] || export VM_HEADLESS=1
dir="$repo/share/vmtest/$suite" dir="$repo/share/vmtest/$suite"
[[ -d "$dir" ]] || { echo "No suite $suite (folders in share/vmtest/)" >&2; exit 2; } [[ -d "$dir" ]] || { echo "No suite $suite (folders in share/vmtest/)" >&2; exit 2; }
case "$suite" in cli) port=2301 ;; menu) port=2302 ;; hw) port=2303 ;; installer) port=2304 ;; toggles) port=2305 ;; nvidia) port=2306 ;; *) port=2390 ;; esac case "$suite" in cli) port=2301 ;; menu) port=2302 ;; hw) port=2303 ;; installer) port=2304 ;; toggles) port=2305 ;; *) port=2390 ;; esac
export VM_PORT="${VM_PORT:-$port}" export VM_PORT="${VM_PORT:-$port}"
export VM_DIR="$HOME/vms/run-$suite" export VM_DIR="$HOME/vms/run-$suite"
export REPO="${REPO:-$repo/../steamify-cachyos}" export REPO="${REPO:-$repo/../steamify-cachyos}"
@@ -96,11 +96,7 @@ for block in "$dir"/*.sh; do
done done
vm_stop vm_stop
} 2>&1 | tee "$log" | sed -u "s/^/[$suite] /" >> "$TEST_LOG" } 2>&1 | tee "$log" | sed -u "s/^/[$suite] /" >> "$TEST_LOG"
rc=${PIPESTATUS[0]} # an abort inside the braces must not pass as 0 failed checks
fails="$(grep -c '^FAIL' "$log")" fails="$(grep -c '^FAIL' "$log")"
if [[ $rc -ne 0 && $fails -eq 0 ]]; then
echo "FAIL $suite aborted (exit $rc): $(grep -v '^$' "$log" | tail -n 1)" | tee -a "$log"; fails=1
fi
echo "== $suite: $(grep -c '^PASS' "$log") passed, $fails failed, $(grep -c '^SKIP' "$log") skipped (log: $log)" | tee -a "$log" echo "== $suite: $(grep -c '^PASS' "$log") passed, $fails failed, $(grep -c '^SKIP' "$log") skipped (log: $log)" | tee -a "$log"
grep '^FAIL' "$log" grep '^FAIL' "$log"
exit "$fails" exit "$fails"
+1 -1
View File
@@ -3,7 +3,7 @@
# update the repo there, start scripts/vmtest.sh in a detached `screen`, wait for the summary, print it. # update the repo there, start scripts/vmtest.sh in a detached `screen`, wait for the summary, print it.
# scripts/vmtest-remote.sh <ssh-host> [--no-wait] [vmtest.sh args...] e.g. scripts/vmtest-remote.sh pc cli hw # scripts/vmtest-remote.sh <ssh-host> [--no-wait] [vmtest.sh args...] e.g. scripts/vmtest-remote.sh pc cli hw
# The branch you are on must be pushed (the remote pulls it). The remote needs this repo, ../steamify-cachyos, # The branch you are on must be pushed (the remote pulls it). The remote needs this repo, ../steamify-cachyos,
# ../steamify-cachyos-live-iso (for the loader list and the ISO), qemu + OVMF + screen, KVM, and the VMs in # ../steammachine-cachyos-live-iso (for the loader list and the ISO), qemu + OVMF + screen, KVM, and the VMs in
# ~/vms (see the vm-install skill). Env: REMOTE_REPO (default ~/projects/steamify/steamify-cachyos-dev). # ~/vms (see the vm-install skill). Env: REMOTE_REPO (default ~/projects/steamify/steamify-cachyos-dev).
set -uo pipefail set -uo pipefail
host="${1:?usage: $0 <ssh-host> [--no-wait] [vmtest.sh args...]}"; shift host="${1:?usage: $0 <ssh-host> [--no-wait] [vmtest.sh args...]}"; shift
+17 -25
View File
@@ -4,12 +4,10 @@
# scripts/vmtest.sh [--install] [--window] [--screen] [boot | <loader>... | <suite>...] # scripts/vmtest.sh [--install] [--window] [--screen] [boot | <loader>... | <suite>...]
# no argument everything: boot (all advertised loaders) and all suites # no argument everything: boot (all advertised loaders) and all suites
# boot the boot loader test (limine, systemd-boot, grub), or name loaders # boot the boot loader test (limine, systemd-boot, grub), or name loaders
# generic the plain-PC install check for every loader (vmbootloadertest.sh --quick --generic: a PC that is not a # <suite> cli, menu, hw, installer, toggles (folders in share/vmtest/)
# Steam Machine must get no Steam Machine items and no DKMS modules); part of the default run
# <suite> cli, menu, hw, installer, toggles, nvidia (folders in share/vmtest/)
# --install install the boot loader VMs first from the newest Steamify ISO (asks before replacing a disk) # --install install the boot loader VMs first from the newest Steamify ISO (asks before replacing a disk)
# --window show the VMs' windows (default: headless; one shared Konsole follows ~/vms/test.log) # --window show the VMs' windows (default: headless; one shared Konsole follows ~/vms/test.log)
# MAX_PARALLEL=3 (env) VMs at once (5 worked on a 9800X3D / 64 GB); the slowest suites start first # MAX_PARALLEL=3 (env) VMs at once; the slowest suites start first
# --screen run in a detached `screen` session named vmtest: `screen -r vmtest` to watch, Ctrl-a d to leave # --screen run in a detached `screen` session named vmtest: `screen -r vmtest` to watch, Ctrl-a d to leave
# The suites run on the plain CachyOS VM ~/vms/steamify-vm (Plasma, from vminstall.sh --iso <CachyOS ISO> # The suites run on the plain CachyOS VM ~/vms/steamify-vm (Plasma, from vminstall.sh --iso <CachyOS ISO>
# with VM_STEAMIFY=skip); the boot loader test on ~/vms/bl-<loader> (Steamify ISO). # with VM_STEAMIFY=skip); the boot loader test on ~/vms/bl-<loader> (Steamify ISO).
@@ -18,16 +16,15 @@
set -uo pipefail set -uo pipefail
here="$(cd "$(dirname "$0")" && pwd)" here="$(cd "$(dirname "$0")" && pwd)"
repo="$(cd "$here/.." && pwd)" repo="$(cd "$here/.." && pwd)"
iso_repo="$repo/../steamify-cachyos-live-iso" iso_repo="$repo/../steammachine-cachyos-live-iso"
args=("$@") args=("$@")
install=""; window=""; screen=false; loaders=(); suites=(); want_boot=false; want_generic=false install=""; window=""; screen=false; loaders=(); suites=(); want_boot=false
for a in "$@"; do for a in "$@"; do
case "$a" in case "$a" in
--install) install=--install ;; --install) install=--install ;;
--window) window=--window ;; --window) window=--window ;;
--screen) screen=true ;; --screen) screen=true ;;
boot) want_boot=true ;; boot) want_boot=true ;;
generic) want_generic=true ;;
limine|systemd-boot|grub) loaders+=("$a"); want_boot=true ;; limine|systemd-boot|grub) loaders+=("$a"); want_boot=true ;;
*) [[ -d "$repo/share/vmtest/$a" ]] && suites+=("$a") || { echo "Unknown argument: $a" >&2; exit 2; } ;; *) [[ -d "$repo/share/vmtest/$a" ]] && suites+=("$a") || { echo "Unknown argument: $a" >&2; exit 2; } ;;
esac esac
@@ -40,32 +37,27 @@ if $screen; then
echo "Running in the screen session vmtest: screen -r vmtest (Ctrl-a d to leave it running); the summary lands in ~/vms/last-test.txt" echo "Running in the screen session vmtest: screen -r vmtest (Ctrl-a d to leave it running); the summary lands in ~/vms/last-test.txt"
exit 0 exit 0
fi fi
if [[ ${#loaders[@]} -eq 0 && ${#suites[@]} -eq 0 ]] && ! $want_boot && ! $want_generic; then if [[ ${#loaders[@]} -eq 0 && ${#suites[@]} -eq 0 ]] && ! $want_boot; then
want_boot=true; want_generic=true want_boot=true
for d in cli menu hw installer toggles nvidia; do [[ -d "$repo/share/vmtest/$d" ]] && suites+=("$d"); done for d in cli menu hw installer toggles; do [[ -d "$repo/share/vmtest/$d" ]] && suites+=("$d"); done
fi fi
# What the ISO advertises: the boot loaders calamares-online.sh keeps. # What the ISO advertises: the boot loaders calamares-online.sh keeps.
advertised="$(sed -n 's/.*"bootloader:\([^"]*\)".*/\1/p' "$iso_repo/archiso/airootfs/usr/local/bin/calamares-online.sh" 2>/dev/null | head -n 1)" advertised="$(sed -n 's/.*"bootloader:\([^"]*\)".*/\1/p' "$iso_repo/archiso/airootfs/usr/local/bin/calamares-online.sh" 2>/dev/null | head -n 1)"
[[ -n "$advertised" ]] || { echo "Can't read the advertised boot loaders from $iso_repo (calamares-online.sh)" >&2; exit 2; } [[ -n "$advertised" ]] || { echo "Can't read the advertised boot loaders from $iso_repo (calamares-online.sh)" >&2; exit 2; }
$want_boot && [[ ${#loaders[@]} -eq 0 ]] && read -r -a loaders <<< "$advertised" $want_boot && [[ ${#loaders[@]} -eq 0 ]] && read -r -a loaders <<< "$advertised"
gloaders=() # the plain-PC install check: the same loaders as the boot loader test, or all advertised ones
if $want_generic; then if [[ ${#loaders[@]} -gt 0 ]]; then gloaders=("${loaders[@]}"); else read -r -a gloaders <<< "$advertised"; fi; fi
tested="limine systemd-boot grub" # the loaders vmbootloadertest.sh has a way to boot the other kernel for tested="limine systemd-boot grub" # the loaders vmbootloadertest.sh has a way to boot the other kernel for
summary="$HOME/vms/last-test.txt" summary="$HOME/vms/last-test.txt"
maxp="${MAX_PARALLEL:-3}" # VMs at once (4 GB RAM each in the suites, see vmsuite.sh); 5 ran fine on a 9800X3D with 64 GB maxp="${MAX_PARALLEL:-3}" # VMs at once: 8 GB RAM each, 6 vCPUs each
# Jobs, the slowest first so the last ones are short: suites, then the boot loaders. # Jobs, the slowest first so the last ones are short: suites, then the boot loaders.
jobs_list=() jobs_list=()
for s in toggles hw cli menu installer; do [[ " ${suites[*]} " == *" $s "* ]] && jobs_list+=("suite:$s"); done for s in toggles hw cli menu installer; do [[ " ${suites[*]} " == *" $s "* ]] && jobs_list+=("suite:$s"); done
for s in "${suites[@]}"; do [[ " toggles hw cli menu installer " == *" $s "* ]] || jobs_list+=("suite:$s"); done for s in "${suites[@]}"; do [[ " toggles hw cli menu installer " == *" $s "* ]] || jobs_list+=("suite:$s"); done
for l in "${loaders[@]}"; do jobs_list+=("loader:$l"); done for l in "${loaders[@]}"; do jobs_list+=("loader:$l"); done
for l in "${gloaders[@]}"; do jobs_list+=("generic:$l"); done run_job() { # run_job <kind:name>: output in /tmp/vmtest-<name>.out, exit status in /tmp/vmtest-<name>.rc
jobtag() { [[ "${1%%:*}" == generic ]] && echo "${1#*:}-generic" || echo "${1#*:}"; } # files and names of a job local kind="${1%%:*}" name="${1#*:}"
run_job() { # run_job <kind:name>: output in /tmp/vmtest-<tag>.out, exit status in /tmp/vmtest-<tag>.rc if [[ "$kind" == loader ]]; then bash "$here/vmbootloadertest.sh" "$name" $install $window > "/tmp/vmtest-$name.out" 2>&1
local kind="${1%%:*}" name="${1#*:}" tag; tag="$(jobtag "$1")" else bash "$here/vmsuite.sh" "$name" $window > "/tmp/vmtest-$name.out" 2>&1; fi
if [[ "$kind" == loader ]]; then bash "$here/vmbootloadertest.sh" "$name" $install $window > "/tmp/vmtest-$tag.out" 2>&1 echo $? > "/tmp/vmtest-$name.rc"
elif [[ "$kind" == generic ]]; then bash "$here/vmbootloadertest.sh" "$name" $install --quick --generic $window > "/tmp/vmtest-$tag.out" 2>&1
else bash "$here/vmsuite.sh" "$name" $window > "/tmp/vmtest-$tag.out" 2>&1; fi
echo $? > "/tmp/vmtest-$tag.rc"
} }
{ {
echo "vmtest.sh $(date '+%F %T') branch $(git -C "$repo" branch --show-current) $(git -C "$repo" rev-parse --short HEAD)" echo "vmtest.sh $(date '+%F %T') branch $(git -C "$repo" branch --show-current) $(git -C "$repo" rev-parse --short HEAD)"
@@ -81,12 +73,12 @@ for job in "${jobs_list[@]}"; do
done done
wait wait
for job in "${jobs_list[@]}"; do for job in "${jobs_list[@]}"; do
tag="$(jobtag "$job")" name="${job#*:}"
sed -n '/^== /,$p' "/tmp/vmtest-$tag.out" sed -n '/^== /,$p' "/tmp/vmtest-$name.out"
total=$((total + $(cat "/tmp/vmtest-$tag.rc" 2>/dev/null || echo 1))) total=$((total + $(cat "/tmp/vmtest-$name.rc" 2>/dev/null || echo 1)))
done done
echo echo
echo "SKIP (needs a person, TESTPLAN.md): U1-U9 the app's screens, R2.2 the first desktop login, H-Real the real Steam Machine" echo "SKIP (needs a person, TESTPLAN.md): U1-U9 the app's screens, R2.2 the first desktop login, H-Real the real Steam Machine"
echo "== total: $total failed check(s); boot loaders: ${loaders[*]:-none}; plain PC: ${gloaders[*]:-none}; suites: ${suites[*]:-none}" echo "== total: $total failed check(s); boot loaders: ${loaders[*]:-none}; suites: ${suites[*]:-none}"
} 2>&1 | tee "$summary" } 2>&1 | tee "$summary"
exit "$(grep -c '^FAIL' "$summary")" exit "$(grep -c '^FAIL' "$summary")"
-4
View File
@@ -3,9 +3,6 @@
pass() { echo "PASS $*"; }; fail() { echo "FAIL $*"; } pass() { echo "PASS $*"; }; fail() { echo "FAIL $*"; }
k=$(uname -r) k=$(uname -r)
echo "kernel: $k"; echo "cmdline: $(cut -c1-140 /proc/cmdline)" echo "kernel: $k"; echo "cmdline: $(cut -c1-140 /proc/cmdline)"
if [[ "${HARDWARE:-fremont}" == generic ]]; then
lsmod | grep -qE "^(steamify_fremont_poweroff|leds_valve)" && fail "Steam Machine modules loaded on a plain PC" || pass "no Steam Machine modules loaded (not a Steam Machine)"
else
compgen -G "/usr/lib/modules/$k/updates/dkms/steamify-fremont-poweroff.ko*" >/dev/null && pass "power-off fix built for the running kernel" || fail "no power-off fix for $k" compgen -G "/usr/lib/modules/$k/updates/dkms/steamify-fremont-poweroff.ko*" >/dev/null && pass "power-off fix built for the running kernel" || fail "no power-off fix for $k"
# Loaded (a real Steam Machine), or tried at boot and refused with "No such device" (the VM has no AMD GPIO # Loaded (a real Steam Machine), or tried at boot and refused with "No such device" (the VM has no AMD GPIO
# controller, so the module returns -ENODEV silently). The refusal is in the journal; it only sometimes # controller, so the module returns -ENODEV silently). The refusal is in the journal; it only sometimes
@@ -15,7 +12,6 @@ elif sudo journalctl -b -q --no-pager -u systemd-modules-load | grep -q "steamif
pass "power-off fix tried at boot (No such device: no AMD GPIO controller in the VM)" pass "power-off fix tried at boot (No such device: no AMD GPIO controller in the VM)"
else fail "power-off fix not loaded, and not tried at boot"; fi else fail "power-off fix not loaded, and not tried at boot"; fi
lsmod | grep -q '^leds_valve' && pass "leds_valve loaded" || fail "leds_valve not loaded" lsmod | grep -q '^leds_valve' && pass "leds_valve loaded" || fail "leds_valve not loaded"
fi
[[ -z "$(systemctl --failed --no-legend)" ]] && pass "no failed units" || { fail "failed units:"; systemctl --failed --no-legend; } [[ -z "$(systemctl --failed --no-legend)" ]] && pass "no failed units" || { fail "failed units:"; systemctl --failed --no-legend; }
# B1: booted through the loader's own EFI boot entry, not the firmware's automatic disk entry (the # B1: booted through the loader's own EFI boot entry, not the firmware's automatic disk entry (the
# fallback path \EFI\BOOT\BOOTX64.EFI) and not an entry without a partition (HD(0,GPT,0000...)). # fallback path \EFI\BOOT\BOOTX64.EFI) and not an entry without a partition (HD(0,GPT,0000...)).
-10
View File
@@ -2,16 +2,6 @@
# Guest side: Steamify's DKMS modules exist for every installed kernel. # Guest side: Steamify's DKMS modules exist for every installed kernel.
# Prints PASS/FAIL lines (scripts/vmbootloadertest.sh counts the FAILs). # Prints PASS/FAIL lines (scripts/vmbootloadertest.sh counts the FAILs).
pass() { echo "PASS $*"; }; fail() { echo "FAIL $*"; } pass() { echo "PASS $*"; }; fail() { echo "FAIL $*"; }
# HARDWARE=generic (a plain PC): none of Steamify's Steam Machine modules belong on it.
if [[ "${HARDWARE:-fremont}" == generic ]]; then
for k in $(ls /usr/lib/modules); do
for m in cros-ec-cec leds-valve steamify-fremont-poweroff; do
compgen -G "/usr/lib/modules/$k/updates/dkms/$m.ko*" >/dev/null && fail "$m is installed for $k, but this is not a Steam Machine" || pass "no $m for $k (not a Steam Machine)"
done
done
[[ -f /etc/modules-load.d/steamify-fremont-poweroff.conf ]] && fail "modules-load.d has the power-off fix on a plain PC" || pass "no modules-load.d entry (not a Steam Machine)"
exit 0
fi
kernels=$(ls /usr/lib/modules) kernels=$(ls /usr/lib/modules)
n=$(wc -w <<< "$kernels") n=$(wc -w <<< "$kernels")
echo "kernels: $(tr '\n' ' ' <<< "$kernels")" echo "kernels: $(tr '\n' ' ' <<< "$kernels")"
+2 -10
View File
@@ -5,22 +5,14 @@
pass() { echo "PASS $*"; }; fail() { echo "FAIL $*"; } pass() { echo "PASS $*"; }; fail() { echo "FAIL $*"; }
sudo mountpoint -q /mnt || sudo mount -t 9p -o trans=virtio,version=9p2000.L repo /mnt sudo mountpoint -q /mnt || sudo mount -t 9p -o trans=virtio,version=9p2000.L repo /mnt
cd /mnt || exit 1 cd /mnt || exit 1
for lib in $(grep -oP '^for lib in \K.*(?=; do$)' steamify.sh); do for lib in common state packages login-manager single-user steam-desktop steam-machine fremont-poweroff vram-booster hdmi-refresh cec boot-session vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut steam-game update-notifier; do
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "lib/$lib.sh" 2>/dev/null . "lib/$lib.sh" 2>/dev/null
done done
# Items this Steam Machine VM has on after `--options gaming,theme,glyphs,single,launcher,notify,cec,machine,poweroff`. # Items this Steam Machine VM has on after `--options gaming,theme,glyphs,single,launcher,notify,cec,machine,poweroff`.
# HARDWARE=generic (a plain PC): the Steam Machine items (poweroff, cec) must stay off. for item in gaming theme glyphs single launcher notify poweroff cec; do
for item in gaming theme glyphs single launcher notify; do
if "${item}_status" 2>/dev/null; then pass "$item is on (the menu shows it ticked)"; else fail "$item is off"; fi if "${item}_status" 2>/dev/null; then pass "$item is on (the menu shows it ticked)"; else fail "$item is off"; fi
done done
for item in poweroff cec; do
if [[ "${HARDWARE:-fremont}" == generic ]]; then
if "${item}_status" 2>/dev/null; then fail "$item is on, but this is not a Steam Machine"; else pass "$item is off (not a Steam Machine)"; fi
else
if "${item}_status" 2>/dev/null; then pass "$item is on (the menu shows it ticked)"; else fail "$item is off"; fi
fi
done
# Steamify never renames the OS (limine-snapper-sync / grub-btrfs read it). # Steamify never renames the OS (limine-snapper-sync / grub-btrfs read it).
grep -qx 'NAME="CachyOS Linux"' /etc/os-release && grep -qx 'PRETTY_NAME="CachyOS"' /etc/os-release && pass "os-release NAME and PRETTY_NAME are CachyOS's" || fail "os-release NAME/PRETTY_NAME changed" grep -qx 'NAME="CachyOS Linux"' /etc/os-release && grep -qx 'PRETTY_NAME="CachyOS"' /etc/os-release && pass "os-release NAME and PRETTY_NAME are CachyOS's" || fail "os-release NAME/PRETTY_NAME changed"
grep -q 'with Steamify' /etc/lsb-release && fail "lsb-release still says 'with Steamify'" || pass "lsb-release name is CachyOS's" grep -q 'with Steamify' /etc/lsb-release && fail "lsb-release still says 'with Steamify'" || pass "lsb-release name is CachyOS's"
-19
View File
@@ -1,19 +0,0 @@
# C1-C3: extended controller support: the repo's DKMS drivers (xone + dongle firmware, xpadneo) are installed and built for every
# installed kernel; a second run is quiet; turning it off removes them again. The VM has no dongle or controller: this covers
# the packages, the headers and the DKMS builds, not the hardware. Covers TESTPLAN C1 (on), C2 (again), C3 (off).
info "applying extended_controller_support (kernel headers and DKMS builds for every kernel: a few minutes)..."
sf --defaults --options extended_controller_support > /tmp/c1.log; rc=$?
[[ $rc -eq 0 ]] && pass "C1 exit 0" || { fail "C1 exit $rc: $(tail -n 12 /tmp/c1.log)"; exit 0; }
pacman -Q xpadneo-dkms xone-dongle-firmware >/dev/null 2>&1 && pacman -Q xone-dkms >/dev/null 2>&1 && pass "C1 xpadneo-dkms, xone-dkms and xone-dongle-firmware are installed" || fail "C1 packages: $(pacman -Qq | grep -E 'xone|xpadneo' | tr '\n' ' ')"
expect_on extended_controller_support
for kd in /usr/lib/modules/*/; do
k="$(basename "$kd")"; [[ -d "$kd/kernel" ]] || continue
n="$(dkms status -k "$k" 2>/dev/null | grep -E 'xone|xpadneo' | grep -c installed)"
[[ "$n" -ge 2 ]] && pass "C1 $k: DKMS built xone and xpadneo ($n modules installed)" || fail "C1 $k: DKMS: $(dkms status -k "$k" 2>&1 | tr '\n' ';')"
modinfo -k "$k" xone_dongle >/dev/null 2>&1 && modinfo -k "$k" hid_xpadneo >/dev/null 2>&1 && pass "C1 $k: xone_dongle and hid_xpadneo modules exist" || fail "C1 $k: modules missing"
done
sf --defaults --options extended_controller_support | grep -qiE 'nothing|already|exit: 0' && pass "C2 a second run is quiet" || info "C2 second run: $(sf --defaults --options extended_controller_support | tail -n 3)"
sf --defaults --options notify > /tmp/c3.log; rc=$?
[[ $rc -eq 0 ]] && pass "C3 turn-off exit 0" || fail "C3 exit $rc: $(tail -n 8 /tmp/c3.log)"
pacman -Q xpadneo-dkms xone-dkms xone-dongle-firmware >/dev/null 2>&1 && fail "C3 packages are still installed" || pass "C3 the three packages are removed again"
expect_off extended_controller_support
+1 -4
View File
@@ -1,8 +1,5 @@
# H3: BIOS update, the whole flow with WIZARD_BIOS_DRY_RUN=1 (never flashes). The VM reports an older BIOS. # H3: BIOS update, the whole flow with WIZARD_BIOS_DRY_RUN=1 (never flashes). The VM reports an older BIOS.
# env: BIOS_VERSION=F7F0107 # env: BIOS_VERSION=F7F0107
# The BIOS row's number by its name (a new menu item shifts the numbers). out=$(printf '11\n\ny\ny\nUPDATE\nm\nq\nn\n' | WIZARD_BIOS_DRY_RUN=1 bash /mnt/steamify.sh 2>&1 | sed 's/\x1b\[[0-9;]*m//g')
n=$(printf 'q\n' | bash /mnt/steamify.sh 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | sed -n 's/^ *\([0-9][0-9]*\) .*Update BIOS.*/\1/p' | head -n 1)
[[ -n "$n" ]] && pass "H3 Update BIOS is menu row $n" || { fail "H3 no Update BIOS row in the menu"; exit 0; }
out=$(printf "$n\n\ny\ny\nUPDATE\nm\nq\nn\n" | WIZARD_BIOS_DRY_RUN=1 bash /mnt/steamify.sh 2>&1 | sed 's/\x1b\[[0-9;]*m//g')
grep -qi "dry run" <<< "$out" && pass "H3 the dry run went through" || fail "H3 no dry-run message: $(tail -n 3 <<< "$out" | tr '\n' ' ' | cut -c1-160)" grep -qi "dry run" <<< "$out" && pass "H3 the dry run went through" || fail "H3 no dry-run message: $(tail -n 3 <<< "$out" | tr '\n' ' ' | cut -c1-160)"
grep -q '^\[ERROR\]' <<< "$out" && fail "H3 errors: $(grep -B4 -m1 '^\[ERROR\]' <<< "$out" | tr '\n' '|' | cut -c1-400)" || pass "H3 no errors" grep -q '^\[ERROR\]' <<< "$out" && fail "H3 errors: $(grep -B4 -m1 '^\[ERROR\]' <<< "$out" | tr '\n' '|' | cut -c1-400)" || pass "H3 no errors"
+2 -7
View File
@@ -2,13 +2,8 @@
menu() { printf "$1" | bash /mnt/steamify.sh 2>&1 | sed 's/\x1b\[[0-9;]*m//g'; } menu() { printf "$1" | bash /mnt/steamify.sh 2>&1 | sed 's/\x1b\[[0-9;]*m//g'; }
# R1.1: first-run ticks: everything but Boot into desktop and BIOS # R1.1: first-run ticks: everything but Boot into desktop and BIOS
out=$(menu 'q\n') out=$(menu 'q\n')
# By name, not by row number (a new item shifts the numbers): the ticked ones, then the ones that are shown but never preselected. for n in 1 3 4 5 6 7 8 9 10; do grep -qE "^ +$n +[a-z-]+ +\[x\]" <<< "$out" && pass "R1.1 row $n ticked" || fail "R1.1 row $n not ticked"; done
for item in "SteamOS conversion" "SteamOS theme" "Steam Deck/Machine icons" "Single user mode" "Steamify shortcut" "Update notifications" "HDMI-CEC" "Steam Machine support" "Power-off fix"; do grep -qE "^ +11 .*\[ \]" <<< "$out" && pass "R1.1 Update BIOS not ticked" || fail "R1.1 Update BIOS ticked"
grep -qE "^ +[0-9]+ +[a-z-]+ +\[x\] .*$item" <<< "$out" && pass "R1.1 $item ticked" || fail "R1.1 $item not ticked"
done
for item in "Extended controller support" "Update BIOS"; do
grep -qE "^ +[0-9]+ +[a-z-]+ +\[ \] .*$item" <<< "$out" && pass "R1.1 $item shown, not ticked" || fail "R1.1 $item missing or ticked"
done
grep -q "Boot into: \[gamescope\]" <<< "$out" && pass "R1.1 boots into gamescope by default" || fail "R1.1 boot default is not gamescope" grep -q "Boot into: \[gamescope\]" <<< "$out" && pass "R1.1 boots into gamescope by default" || fail "R1.1 boot default is not gamescope"
# R1.2: everything OK, back in the menu with all on # R1.2: everything OK, back in the menu with all on
info "applying Steamify (installs packages, builds kernel modules: a few minutes)..." info "applying Steamify (installs packages, builds kernel modules: a few minutes)..."
-18
View File
@@ -1,18 +0,0 @@
# N1: an NVIDIA PC (fake GPU): the menu hides the SteamOS conversion and its gamescope options, shows Gaming on NVIDIA, its
# Big Picture option and Single user mode. Covers TESTPLAN N1.
fake_nvidia
out="$(sf --defaults --list)"
ids="$(python3 -c 'import json,sys; d=json.load(sys.stdin); print(" ".join(sorted(i["id"] for i in d)))' <<< "$out" 2>/dev/null)"
[[ -n "$ids" ]] && pass "N1 --defaults --list is valid JSON: $ids" || { fail "N1 --defaults --list is not JSON: $(echo "$out" | head -c 300)"; exit 0; }
for i in nvidia bigpicture single; do [[ " $ids " == *" $i "* ]] && pass "N1 $i is offered" || fail "N1 $i is missing"; done
for i in gaming boot glyphs; do [[ " $ids " == *" $i "* ]] && fail "N1 $i is offered on an NVIDIA PC" || pass "N1 $i is hidden"; done
python3 -c '
import json,sys
d={i["id"]:i for i in json.load(sys.stdin)}
ok = d["bigpicture"]["parent"]=="nvidia" and d["nvidia"]["on"] and d["bigpicture"]["on"] and d["single"]["on"]
sys.exit(0 if ok else 1)' <<< "$out" && pass "N1 Big Picture is a sub-option of Gaming on NVIDIA, all three preselected" || fail "N1 parent or preselection wrong"
st="$(sf --backend status)"
python3 -c 'import json,sys; d=json.load(sys.stdin); ids=[i["id"] for i in d.get("items",d)]; sys.exit(1 if "gaming" in ids else 0)' <<< "$st" 2>/dev/null && pass "N1 --backend status (the app) has no conversion row" || fail "N1 --backend status lists the conversion"
# Without a NVIDIA GPU nothing changes: the conversion is offered, Gaming on NVIDIA is not.
out2="$(NVIDIA_DRM_DIR=/nonexistent sf --defaults --list)"
python3 -c 'import json,sys; ids=[i["id"] for i in json.load(sys.stdin)]; sys.exit(0 if "gaming" in ids and "nvidia" not in ids and "bigpicture" not in ids else 1)' <<< "$out2" && pass "N1 no NVIDIA GPU: the conversion is offered, Gaming on NVIDIA is not" || fail "N1 no-NVIDIA menu wrong"
@@ -1,51 +0,0 @@
# N2-N5: Gaming on NVIDIA + Big Picture + Single user mode on a fake NVIDIA PC, for real: pacman installs Steam, the SDDM autologin,
# the boot loader files and the initramfs, then a reboot into Plasma, then everything off again and a second reboot.
# Covers TESTPLAN N2 (apply), N3 (after the reboot), N4 (turn off), N5 (after the second reboot).
unit='~/.config/systemd/user/steamify-steam-autostart.service'
g() { guest "$@"; }
yes_() { [[ "$(g "$1 && echo YES || echo NO" | tail -n 1)" == YES ]]; } # a guest condition
info "N2 applying nvidia,bigpicture,single (Steam, SDDM, initramfs: several minutes)..."
rc="$(g 'fake_nvidia; sf --defaults --options nvidia,bigpicture,single > /tmp/n2.log; echo "rc=$?"' | tail -n 1)"
[[ "$rc" == rc=0 ]] && pass "N2 apply exit 0" || { fail "N2 apply: $rc: $(g 'tail -n 15 /tmp/n2.log')"; return 0 2>/dev/null || exit 0; }
yes_ 'pacman -Q steam >/dev/null 2>&1' && pass "N2 Steam is installed" || fail "N2 Steam missing"
yes_ "grep -qx 'ExecStart=/usr/bin/steam -gamepadui' $unit" && pass "N2 the autostart unit starts Steam with -gamepadui" || fail "N2 unit: $(g "cat $unit")"
yes_ 'systemctl --user is-enabled -q steamify-steam-autostart.service' && pass "N2 the unit is enabled" || fail "N2 unit not enabled"
[[ "$(g 'kreadconfig6 --file ksmserverrc --group General --key loginMode' | tail -n 1)" == emptySession ]] && pass "N2 Plasma starts with an empty session" || fail "N2 loginMode"
g 'is nvidia && is bigpicture && echo ON' | grep -qx ON && pass "N2 nvidia and bigpicture report on" || fail "N2 status functions"
yes_ "grep -q 'nvidia-drm.modeset=1 nvidia-drm.fbdev=1' /etc/default/limine" && pass "N2 the kernel parameters are in /etc/default/limine" || fail "N2 limine defaults"
yes_ "sudo grep -q 'nvidia-drm.fbdev=1' /boot/limine.conf" && pass "N2 and in the generated /boot/limine.conf" || fail "N2 limine.conf"
yes_ 'test -f /etc/mkinitcpio.conf.d/90-steamify-nvidia.conf && test -x /usr/local/libexec/steamify-nvidia-initramfs && test -f /etc/pacman.d/hooks/85-steamify-nvidia-initramfs.hook' && pass "N2 early-load file, hook and its script installed" || fail "N2 initramfs files"
yes_ 'sudo find /boot -name "initramfs*" -exec sh -c "lsinitcpio {} | grep -q nvidia" \; -print | grep -q .' && pass "N2 the NVIDIA modules are in the initramfs" || fail "N2 initramfs has no nvidia modules"
yes_ 'systemctl is-enabled -q sddm.service' && pass "N2 SDDM is the login manager" || fail "N2 sddm not enabled"
yes_ "grep -qx 'Session=plasma.desktop' /etc/sddm.conf.d/zzz-steamify-autologin.conf && grep -qx \"User=\$USER\" /etc/sddm.conf.d/zzz-steamify-autologin.conf" && pass "N2 SDDM autologin: the user into the Plasma session" || fail "N2 autologin file: $(g 'cat /etc/sddm.conf.d/zzz-steamify-autologin.conf')"
yes_ 'is single' && pass "N2 single user mode is on" || fail "N2 single not on"
g 'fake_nvidia; sf --defaults --options nvidia,bigpicture,single | tail -n 3' | grep -qiE 'nothing|already|exit: 0' && pass "N2 a second run is quiet" || info "N2 second run: $(g 'fake_nvidia; sf --defaults --options nvidia,bigpicture,single | tail -n 3')"
reboot_guest && pass "N3 the VM is back after the reboot" || { fail "N3 no ssh after the reboot"; return 0 2>/dev/null || exit 0; }
up=""; for _ in $(seq 40); do vm_ssh "pgrep -u $VM_USER -x plasmashell" >/dev/null 2>&1 && { up=1; break; }; sleep 3; done
[[ -n "$up" ]] && pass "N3 Plasma straight after the boot (SDDM autologin, no greeter)" || fail "N3 no plasmashell after the reboot"
yes_ '! pgrep -x sddm-greeter >/dev/null' && pass "N3 no greeter running" || fail "N3 sddm-greeter is running"
yes_ 'grep -q "nvidia-drm.modeset=1" /proc/cmdline && grep -q "nvidia-drm.fbdev=1" /proc/cmdline' && pass "N3 the kernel parameters are on the running command line" || fail "N3 cmdline: $(g 'cat /proc/cmdline')"
yes_ 'systemctl is-active -q sddm.service' && pass "N3 sddm is the running display manager" || fail "N3 sddm not active"
yes_ 'journalctl --user -b --no-pager -u steamify-steam-autostart.service | grep -qiE "Started|Starting"' && pass "N3 the Steam autostart unit was started at login" || fail "N3 unit never started: $(g 'systemctl --user status steamify-steam-autostart.service 2>&1 | head -n 8')"
[[ "$(g 'kreadconfig6 --file ksmserverrc --group General --key loginMode' | tail -n 1)" == emptySession ]] && pass "N3 the empty session setting survived" || fail "N3 loginMode after the reboot"
info "N4 turning everything off (only notify stays)..."
rc="$(g 'fake_nvidia; sf --defaults --options notify > /tmp/n4.log; echo "rc=$?"' | tail -n 1)"
[[ "$rc" == rc=0 ]] && pass "N4 turn-off exit 0" || { fail "N4 turn-off: $rc: $(g 'tail -n 15 /tmp/n4.log')"; return 0 2>/dev/null || exit 0; }
yes_ '! pacman -Q steam >/dev/null 2>&1' && pass "N4 Steam is removed again (Steamify installed it)" || fail "N4 Steam is still installed"
yes_ "! test -e $unit" && pass "N4 the autostart unit is gone" || fail "N4 unit still there"
[[ -z "$(g 'kreadconfig6 --file ksmserverrc --group General --key loginMode' | tail -n 1)" ]] && pass "N4 loginMode is back to not set" || fail "N4 loginMode: $(g 'kreadconfig6 --file ksmserverrc --group General --key loginMode')"
yes_ '! grep -q nvidia-drm /etc/default/limine' && pass "N4 the kernel parameters are gone from /etc/default/limine" || fail "N4 limine defaults still have them"
# Snapshot entries (snapper's, rootflags=.../.snapshots/N/snapshot) keep the command line of their time: a snapshot of the state with the
# parameters is meant to boot that state. Only the current boot entries count.
yes_ "! sudo grep 'cmdline:' /boot/limine.conf | grep -v '\.snapshots/' | grep -q nvidia-drm" && pass "N4 and from the current entries in /boot/limine.conf (snapshot entries keep their old command line)" || fail "N4 limine.conf still has them: $(g "sudo grep 'cmdline:' /boot/limine.conf | grep -v '\.snapshots/' | cut -c1-150" | tr '\n' '|')"
yes_ '! test -e /etc/mkinitcpio.conf.d/90-steamify-nvidia.conf && ! test -e /usr/local/libexec/steamify-nvidia-initramfs && ! test -e /etc/pacman.d/hooks/85-steamify-nvidia-initramfs.hook' && pass "N4 early-load file, hook and script removed" || fail "N4 initramfs files remain"
yes_ '! test -e /etc/sddm.conf.d/zzz-steamify-autologin.conf' && pass "N4 the SDDM autologin file is gone" || fail "N4 autologin file remains"
yes_ 'systemctl is-enabled -q plasmalogin.service && ! systemctl is-enabled -q sddm.service' && pass "N4 plasma-login-manager is the login manager again" || fail "N4 login manager: $(g 'systemctl show -p Id --value display-manager')"
yes_ '! is nvidia && ! is bigpicture && ! is single' && pass "N4 nvidia, bigpicture and single report off" || fail "N4 status still on"
reboot_guest && pass "N5 the VM is back after the second reboot" || { fail "N5 no ssh after the second reboot"; return 0 2>/dev/null || exit 0; }
up=""; for _ in $(seq 40); do vm_ssh "pgrep -u $VM_USER -x plasmashell" >/dev/null 2>&1 && { up=1; break; }; sleep 3; done
[[ -n "$up" ]] && pass "N5 Plasma comes up (the test autologin of plasma-login-manager)" || fail "N5 no plasmashell after the second reboot"
yes_ '! grep -q "nvidia-drm" /proc/cmdline' && pass "N5 the kernel parameters are off the command line" || fail "N5 cmdline still has them"
+2 -22
View File
@@ -6,7 +6,7 @@ pass() { echo "PASS $*"; }; fail() { echo "FAIL $*"; }; skip() { echo "SKIP $*";
sudo mountpoint -q /mnt || sudo mount -t 9p -o trans=virtio,version=9p2000.L repo /mnt sudo mountpoint -q /mnt || sudo mount -t 9p -o trans=virtio,version=9p2000.L repo /mnt
cd /mnt || exit 1 cd /mnt || exit 1
export SCRIPT_DIR=/mnt # steamify.sh sets it; the libs alone read patches/ and services/ from it export SCRIPT_DIR=/mnt # steamify.sh sets it; the libs alone read patches/ and services/ from it
for lib in $(grep -oP '^for lib in \K.*(?=; do$)' steamify.sh); do for lib in common state packages login-manager single-user steam-desktop steam-machine fremont-poweroff vram-booster hdmi-refresh cec boot-session vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut steam-game update-notifier; do
# shellcheck source=/dev/null # shellcheck source=/dev/null
. "lib/$lib.sh" 2>/dev/null . "lib/$lib.sh" 2>/dev/null
done done
@@ -15,24 +15,4 @@ sf() { bash /mnt/steamify.sh "$@" < /dev/null 2>&1 | sed 's/\x1b\[[0-9;]*m//g';
is() { "${1}_status" 2>/dev/null; } is() { "${1}_status" 2>/dev/null; }
expect_on() { local i; for i in "$@"; do is "$i" && pass "$i is on" || fail "$i is off"; done; } expect_on() { local i; for i in "$@"; do is "$i" && pass "$i is on" || fail "$i is off"; done; }
expect_off() { local i; for i in "$@"; do is "$i" && fail "$i is on" || pass "$i is off"; done; } expect_off() { local i; for i in "$@"; do is "$i" && fail "$i is on" || pass "$i is off"; done; }
ticks() { local i; for i in gaming nvidia bigpicture theme glyphs single launcher notify vram cec machine poweroff boot; do is "$i" && printf '%s ' "$i"; done; echo; } ticks() { local i; for i in gaming theme glyphs single launcher notify vram cec machine poweroff boot; do is "$i" && printf '%s ' "$i"; done; echo; }
# fake_nvidia: an NVIDIA PC for the nvidia suite. A fake RTX 5080 in a fake DRM tree (NVIDIA_DRM_DIR is Steamify's test hook, exported
# here: call it in every guest command that runs the wizard) and fake NVIDIA modules for every installed kernel (renamed copies of a
# tiny module, then depmod), so `modinfo nvidia_drm` finds one and the initramfs build has modules to put in.
fake_nvidia() {
local d=/var/tmp/fake-drm k kd src ext m
mkdir -p "$d/card1/device"
echo 0x10de > "$d/card1/device/vendor"; echo 0x030000 > "$d/card1/device/class"; echo 0x2c02 > "$d/card1/device/device"
for kd in /usr/lib/modules/*/; do
k="$(basename "$kd")"
[[ -d "$kd/kernel" ]] || continue
src="$(find "$kd/kernel" -name 'dummy.ko*' | head -n 1)"
[[ -n "$src" ]] || { echo "no dummy module for $k"; continue; }
ext="${src##*dummy.ko}"
for m in nvidia nvidia-modeset nvidia-uvm nvidia-drm; do
[[ -e "$kd/kernel/drivers/video/$m.ko$ext" ]] || sudo install -Dm644 "$src" "$kd/kernel/drivers/video/$m.ko$ext"
done
sudo depmod -a "$k"
done
export NVIDIA_DRM_DIR="$d"
}
-85
View File
@@ -1,85 +0,0 @@
#!/bin/bash
# Tests lib/controllers.sh (extended controller support: xone, the Xbox wireless dongle, and xpadneo, Xbox Bluetooth) with a stub pacman
# and a temp home. No root, VM or hardware needed: bash tests/controllers-test.sh
# The Steamify checkout (REPO, default: the steamify-cachyos next to this repo)
REPO="${REPO:-$(cd "$(dirname "$0")/../../steamify-cachyos" && pwd)}"; cd "$REPO" || exit 1
T="$(mktemp -d)"; trap 'rm -rf "$T"' EXIT
fail=0
check() { if eval "$2"; then echo "ok $1"; else echo "FAIL $1"; fail=1; fi; }
SCRIPT_DIR="$PWD"; HOME="$T/home"; mkdir -p "$HOME"
source lib/common.sh; source lib/state.sh; source lib/controllers.sh; source lib/menu.sh
# Runs as the user, and refuses anything under the real system folders: a test must never touch them.
sudo() { local a; for a in "$@"; do case "$a" in /etc/*|/usr/*|/boot/*|/var/*) echo "REFUSED sudo $*" >&2; return 1 ;; esac; done; "$@"; }
info() { :; }; ok() { :; }; warn() { :; }; err() { echo "ERR $*"; }
kwriteconfig6() { local f="" k="" v=""; while [[ $# -gt 0 ]]; do case "$1" in --file) f="$2"; shift ;; --key) k="$2"; shift ;; --group) shift ;; *) v="$1" ;; esac; shift; done; mkdir -p "$(dirname "$f")"; sed -i "/^$k=/d" "$f" 2>/dev/null; printf '%s=%s\n' "$k" "$v" >> "$f"; }
kreadconfig6() { local f="" k=""; while [[ $# -gt 0 ]]; do case "$1" in --file) f="$2"; shift ;; --key) k="$2"; shift ;; --group|--default) shift ;; esac; shift; done; sed -n "s/^$k=//p" "$f" 2>/dev/null; }
# A pretend package database.
PKGS=""; PACMAN_LOG="$T/pacman.log"; : > "$PACMAN_LOG"; PACMAN_FAIL=0
pacman() {
case "$1" in
-Q) local p; for p in "${@:2}"; do [[ " $PKGS " == *" $p "* ]] || return 1; done ;;
-S) [[ $PACMAN_FAIL == 1 ]] && return 1; echo "S ${*:4}" >> "$PACMAN_LOG"; PKGS+=" ${*:4}" ;;
-Rns) echo "R ${*:3}" >> "$PACMAN_LOG"; local p; for p in ${*:3}; do PKGS=" ${PKGS// $p / } "; done ;;
esac
}
HEADERS=0; install_kernel_headers() { HEADERS=$((HEADERS + 1)); }
check "off to begin with" '! extended_controller_support_status'
extended_controller_support_enable
check "installs the repo's xpadneo-dkms, xone-dkms and the dongle firmware" 'pacman -Q xpadneo-dkms xone-dkms xone-dongle-firmware'
check "kernel headers are made sure of first" '[[ $HEADERS == 1 ]]'
check "what we installed is recorded" '[[ "$(state_get extended_controller_support installed_pkgs)" == "xpadneo-dkms xone-dkms xone-dongle-firmware" ]]'
check "it is on" extended_controller_support_status
n="$(wc -l < "$PACMAN_LOG")"; extended_controller_support_enable
check "a second run installs nothing again" '[[ "$(wc -l < "$PACMAN_LOG")" == "$n" ]]'
extended_controller_support_disable
check "off: the three packages are removed again" '! pacman -Q xpadneo-dkms 2>/dev/null && ! pacman -Q xone-dkms 2>/dev/null && ! pacman -Q xone-dongle-firmware 2>/dev/null && [[ -z "$(state_get extended_controller_support installed_pkgs)" ]]'
# The AUR's xone-dkms-git conflicts with the repo's xone-dkms: it counts, and is kept.
PKGS=" xone-dkms-git xone-dongle-firmware xpadneo-dkms"; : > "$PACMAN_LOG"
check "xone-dkms-git counts as xone: already on" extended_controller_support_status
extended_controller_support_enable; extended_controller_support_disable
check "with xone-dkms-git: nothing installed and nothing removed" '[[ ! -s "$PACMAN_LOG" ]] && pacman -Q xone-dkms-git xpadneo-dkms xone-dongle-firmware'
PKGS=" xone-dkms-git"
extended_controller_support_enable
check "xone-dkms-git present: only the missing ones are installed, never xone-dkms" 'grep -q "^S xpadneo-dkms xone-dongle-firmware$" "$PACMAN_LOG" && ! pacman -Q xone-dkms 2>/dev/null'
extended_controller_support_disable
check "and off removes only those two, xone-dkms-git stays" 'pacman -Q xone-dkms-git && ! pacman -Q xpadneo-dkms 2>/dev/null'
# A failed install is an error and records nothing.
PKGS=""; PACMAN_FAIL=1
check "a failed pacman is an error" '! extended_controller_support_enable >/dev/null'
check "...and records nothing" '[[ -z "$(state_get extended_controller_support installed_pkgs)" ]]'
PACMAN_FAIL=0
# Packages that were already there are never removed by turning it off.
PKGS=" xpadneo-dkms xone-dkms xone-dongle-firmware"; : > "$PACMAN_LOG"
extended_controller_support_enable; extended_controller_support_disable
check "packages you had before stay" 'pacman -Q xpadneo-dkms xone-dkms xone-dongle-firmware && [[ ! -s "$PACMAN_LOG" ]]'
# The menu.
check "offered on every PC" 'component_available extended_controller_support'
check "not preselected: it builds kernel modules for every kernel" '[[ " ${NO_PRESELECT[*]} " == *" extended_controller_support "* ]]'
check "a top-level item with a label" '[[ -z "${PARENT[extended_controller_support]:-}" && -n "${LABEL[extended_controller_support]}" ]]'
# The "new" badge: an opt-in option added after setup shows it, but is never ticked for you.
component_selectable() { return 0; }
declare -A CURRENT=([gaming]=1 [nvidia]=0 [extended_controller_support]=0 [vram]=0 [boot]=0)
rm -f "$STATE_DIR/features.state"
check "new opt-in option (conversion on, no record): the new badge" 'feature_new_optin extended_controller_support'
check "...but it is not ticked for you (not a new default option)" '! feature_new extended_controller_support'
check "...and the menu label says (new)" '[[ "$(menu_label extended_controller_support)" == *"(new)"* ]]'
check "a new default option is still ticked for you, and is not the opt-in kind" 'feature_new vram && ! feature_new_optin vram'
check "Boot into never gets the badge (it is a choice row)" '! feature_new_optin boot'
state_set_features() { kwriteconfig6 --file "$STATE_DIR/features.state" --group State --key "$1" "$2"; }
state_set_features extended_controller_support off
check "once a run recorded it as off, the badge is gone" '! feature_new_optin extended_controller_support'
rm -f "$STATE_DIR/features.state"
CURRENT[gaming]=0; CURRENT[nvidia]=1
check "on an NVIDIA PC (Gaming on NVIDIA on, no conversion) it shows too" 'feature_new_optin extended_controller_support'
CURRENT[nvidia]=0
check "with neither the conversion nor Gaming on NVIDIA on (a fresh PC) nothing is new" '! feature_new_optin extended_controller_support && ! feature_new vram'
CURRENT[gaming]=1; CURRENT[extended_controller_support]=1
check "already on: not new" '! feature_new_optin extended_controller_support'
exit $fail
-170
View File
@@ -1,170 +0,0 @@
== pc first run
wanted: gaming glyphs theme single launcher notify
disable:
enable: gaming glyphs theme single launcher notify
== pc first run, untick gaming
wanted: theme launcher notify
disable:
enable: theme launcher notify
== pc single on
wanted: gaming theme single launcher notify
disable:
enable: gaming theme single launcher notify
== pc single then gaming off
wanted: theme launcher notify
disable:
enable: theme launcher notify
== pc boot desktop
wanted: gaming boot glyphs theme single launcher notify
disable:
enable: gaming boot glyphs theme single launcher notify
== pc boot desktop then gaming off
wanted: theme launcher notify
disable:
enable: theme launcher notify
== pc silent only
wanted: gaming silent glyphs theme single launcher notify
disable:
enable: gaming silent glyphs theme single launcher notify
== pc all on, theme off
wanted: gaming boot silent glyphs single launcher notify
disable: theme
enable: gaming silent notify
== pc launcher off
wanted:
disable: launcher
enable:
== pc steamgame on without launcher
wanted: theme launcher steamgame notify
disable:
enable: theme launcher steamgame notify
== machine first run
wanted: gaming glyphs theme single launcher steamgame notify vram cec machine poweroff
disable:
enable: gaming glyphs theme single launcher steamgame notify vram cec machine poweroff
== machine silent hidden until boot desktop
wanted: gaming silent glyphs theme single launcher notify vram machine
disable:
enable: gaming silent glyphs theme single launcher notify vram machine
== machine boot desktop keeps silent
wanted: gaming boot silent glyphs theme single launcher notify vram machine
disable:
enable: gaming boot silent glyphs theme single launcher notify vram machine
== machine off
wanted: gaming glyphs theme single launcher notify vram
disable: poweroff machine
enable: gaming glyphs theme single launcher notify vram
== machine poweroff off
wanted: gaming glyphs theme single launcher notify vram machine
disable: poweroff
enable: gaming glyphs theme single launcher notify vram machine
== machine poweroff on brings machine
wanted: gaming glyphs theme single launcher notify vram machine poweroff
disable:
enable: gaming glyphs theme single launcher notify vram machine poweroff
== machine kpin removal
wanted: gaming glyphs theme single launcher notify vram machine poweroff
disable: kpin
enable: gaming glyphs theme single launcher notify vram machine poweroff
== nvidia first run
wanted: nvidia bigpicture theme single launcher notify
disable:
enable: nvidia bigpicture theme single launcher notify
== nvidia bigpicture off
wanted: nvidia theme single launcher notify
disable: bigpicture
enable: nvidia theme single launcher notify
== nvidia nvsilent on
wanted: nvidia nvsilent theme single launcher notify
disable: bigpicture
enable: nvidia nvsilent theme single launcher notify
== nvidia nvsilent then bigpicture
wanted: nvidia bigpicture theme single launcher notify
disable:
enable: nvidia bigpicture theme single launcher notify
== nvidia off
wanted: theme single launcher notify
disable: nvsilent bigpicture nvidia
enable: theme single launcher notify
== nvidia single
wanted: nvidia bigpicture theme launcher notify
disable:
enable: nvidia bigpicture theme launcher notify
== nvidia gaming already on
wanted: nvidia theme launcher notify
disable: gaming
enable: nvidia theme launcher notify
== outdated features
wanted: gaming glyphs theme single launcher notify
disable:
enable: gaming glyphs single launcher notify
== reapply (a)
wanted: gaming glyphs theme single launcher notify
disable:
enable: gaming glyphs theme single launcher notify
== backend_apply single
{"event":"plan","disable":[],"enable":["gaming","single"]}
== backend_apply boot
{"event":"plan","disable":[],"enable":[]}
== backend_apply poweroff
{"event":"plan","disable":[],"enable":["machine","poweroff"]}
== backend_apply silent
{"event":"plan","disable":[],"enable":["gaming","silent"]}
== backend_apply kpin
{"event":"plan","disable":[],"enable":[]}
== backend_apply gaming boot
{"event":"plan","disable":[],"enable":["gaming","boot"]}
== backend_apply launcher steamgame
{"event":"plan","disable":[],"enable":["launcher","steamgame"]}
== backend_apply nvidia bigpicture
{"event":"plan","disable":[],"enable":["nvidia","bigpicture"]}
== backend_apply nvidia nvsilent
{"event":"plan","disable":[],"enable":["nvidia","nvsilent"]}
== backend_apply nvidia bigpicture nvsilent
{"event":"plan","disable":[],"enable":["nvidia","bigpicture"]}
== backend_apply nvidia single
{"event":"plan","disable":[],"enable":["single"]}
== backend_apply nvidia boot
{"event":"plan","disable":[],"enable":[]}
== defaults_list []
{"hideWhen":"","hint":"boot into gaming mode, Steam on the desktop","id":"gaming","kind":"toggle","label":"SteamOS conversion","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"","id":"boot","kind":"choice","label":"Boot into the desktop instead of gaming mode","needs":"","on":false,"parent":"gaming","selectable":true}
{"hideWhen":"","hint":"in the tray at login on the desktop, no window","id":"silent","kind":"toggle","label":"Start Steam silently in desktop mode","needs":"","on":false,"parent":"gaming","selectable":true}
{"hideWhen":"","hint":"Deck button icons in gaming mode","id":"glyphs","kind":"toggle","label":"Install Steam Deck/Machine icons","needs":"","on":true,"parent":"gaming","selectable":true}
{"hideWhen":"","hint":"Vapor look (cachyos-vapor)","id":"theme","kind":"toggle","label":"Install SteamOS theme","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"no password, lock screen or log out (SDDM)","id":"single","kind":"toggle","label":"Single user mode","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"the app on the desktop, Steamify Terminal in the launcher","id":"launcher","kind":"toggle","label":"Steamify shortcut","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"a notification when there's a new Steamify, never updates by itself","id":"notify","kind":"toggle","label":"Update notifications","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"Xbox wireless dongle (xone), Xbox controllers over Bluetooth (xpadneo)","id":"extended_controller_support","kind":"toggle","label":"Extended controller support","needs":"","on":false,"parent":"","selectable":true}
{"hideWhen":"","hint":"use Steam with the TV remote, TV on/off with the PC (experimental)","id":"cec","kind":"toggle","label":"HDMI-CEC","needs":"","on":false,"parent":"","selectable":true}
== backend_status []
{"bios":null,"firstRun":true,"items":[{"hint":"boot into gaming mode, Steam on the desktop","id":"gaming","kind":"toggle","label":"SteamOS conversion","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"","id":"boot","kind":"choice","label":"Boot into the desktop instead of gaming mode","needs":"","new":false,"on":false,"parent":"gaming","selectable":true,"update":false,"wanted":false},{"hint":"in the tray at login on the desktop, no window","id":"silent","kind":"toggle","label":"Start Steam silently in desktop mode","needs":"","new":false,"on":false,"parent":"gaming","selectable":true,"update":false,"wanted":false},{"hint":"Deck button icons in gaming mode","id":"glyphs","kind":"toggle","label":"Install Steam Deck/Machine icons","needs":"","new":false,"on":false,"parent":"gaming","selectable":true,"update":false,"wanted":true},{"hint":"Vapor look (cachyos-vapor)","id":"theme","kind":"toggle","label":"Install SteamOS theme","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"no password, lock screen or log out (SDDM)","id":"single","kind":"toggle","label":"Single user mode","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"the app on the desktop, Steamify Terminal in the launcher","id":"launcher","kind":"toggle","label":"Steamify shortcut","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"a notification when there's a new Steamify, never updates by itself","id":"notify","kind":"toggle","label":"Update notifications","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"Xbox wireless dongle (xone), Xbox controllers over Bluetooth (xpadneo)","id":"extended_controller_support","kind":"toggle","label":"Extended controller support","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":false},{"hint":"use Steam with the TV remote, TV on/off with the PC (experimental)","id":"cec","kind":"toggle","label":"HDMI-CEC","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":false}],"pinnedKernel":"7.1.6-1","steamMachine":false,"version":"0.0.0"}
== defaults_list [fremont steam vram]
{"hideWhen":"","hint":"boot into gaming mode, Steam on the desktop","id":"gaming","kind":"toggle","label":"SteamOS conversion","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"","id":"boot","kind":"choice","label":"Boot into the desktop instead of gaming mode","needs":"","on":false,"parent":"gaming","selectable":true}
{"hideWhen":"","hint":"in the tray at login on the desktop, no window","id":"silent","kind":"toggle","label":"Start Steam silently in desktop mode","needs":"","on":false,"parent":"gaming","selectable":true}
{"hideWhen":"","hint":"Deck button icons in gaming mode","id":"glyphs","kind":"toggle","label":"Install Steam Deck/Machine icons","needs":"","on":true,"parent":"gaming","selectable":true}
{"hideWhen":"","hint":"Vapor look (cachyos-vapor)","id":"theme","kind":"toggle","label":"Install SteamOS theme","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"no password, lock screen or log out (SDDM)","id":"single","kind":"toggle","label":"Single user mode","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"the app on the desktop, Steamify Terminal in the launcher","id":"launcher","kind":"toggle","label":"Steamify shortcut","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"Steamify in your Steam library, for the controller and gaming mode","id":"steamgame","kind":"toggle","label":"Add as non-Steam game","needs":"","on":true,"parent":"launcher","selectable":true}
{"hideWhen":"","hint":"a notification when there's a new Steamify, never updates by itself","id":"notify","kind":"toggle","label":"Update notifications","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"Xbox wireless dongle (xone), Xbox controllers over Bluetooth (xpadneo)","id":"extended_controller_support","kind":"toggle","label":"Extended controller support","needs":"","on":false,"parent":"","selectable":true}
{"hideWhen":"","hint":"the game in front keeps its VRAM, background apps make room","id":"vram","kind":"toggle","label":"VRAM booster","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"use Steam with the TV remote, TV on/off with the PC (experimental)","id":"cec","kind":"toggle","label":"HDMI-CEC","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"LED bar driver, hardware settings in Steam","id":"machine","kind":"toggle","label":"Steam Machine support","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"the Steam Machine stays off after shutting down","id":"poweroff","kind":"toggle","label":"Power-off fix","needs":"","on":true,"parent":"machine","selectable":true}
== backend_status [fremont steam vram]
{"bios":null,"firstRun":true,"items":[{"hint":"boot into gaming mode, Steam on the desktop","id":"gaming","kind":"toggle","label":"SteamOS conversion","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"","id":"boot","kind":"choice","label":"Boot into the desktop instead of gaming mode","needs":"","new":false,"on":false,"parent":"gaming","selectable":true,"update":false,"wanted":false},{"hint":"in the tray at login on the desktop, no window","id":"silent","kind":"toggle","label":"Start Steam silently in desktop mode","needs":"","new":false,"on":false,"parent":"gaming","selectable":true,"update":false,"wanted":false},{"hint":"Deck button icons in gaming mode","id":"glyphs","kind":"toggle","label":"Install Steam Deck/Machine icons","needs":"","new":false,"on":false,"parent":"gaming","selectable":true,"update":false,"wanted":true},{"hint":"Vapor look (cachyos-vapor)","id":"theme","kind":"toggle","label":"Install SteamOS theme","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"no password, lock screen or log out (SDDM)","id":"single","kind":"toggle","label":"Single user mode","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"the app on the desktop, Steamify Terminal in the launcher","id":"launcher","kind":"toggle","label":"Steamify shortcut","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"Steamify in your Steam library, for the controller and gaming mode","id":"steamgame","kind":"toggle","label":"Add as non-Steam game","needs":"","new":false,"on":false,"parent":"launcher","selectable":true,"update":false,"wanted":true},{"hint":"a notification when there's a new Steamify, never updates by itself","id":"notify","kind":"toggle","label":"Update notifications","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"Xbox wireless dongle (xone), Xbox controllers over Bluetooth (xpadneo)","id":"extended_controller_support","kind":"toggle","label":"Extended controller support","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":false},{"hint":"the game in front keeps its VRAM, background apps make room","id":"vram","kind":"toggle","label":"VRAM booster","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"use Steam with the TV remote, TV on/off with the PC (experimental)","id":"cec","kind":"toggle","label":"HDMI-CEC","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"LED bar driver, hardware settings in Steam","id":"machine","kind":"toggle","label":"Steam Machine support","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"the Steam Machine stays off after shutting down","id":"poweroff","kind":"toggle","label":"Power-off fix","needs":"","new":false,"on":false,"parent":"machine","selectable":true,"update":false,"wanted":true}],"pinnedKernel":"7.1.6-1","steamMachine":true,"version":"0.0.0"}
== defaults_list [nvidia steam]
{"hideWhen":"","hint":"Steam on the desktop, started at login","id":"nvidia","kind":"toggle","label":"Gaming on NVIDIA","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"the controller-friendly Steam (untick: Steam's normal window)","id":"bigpicture","kind":"toggle","label":"Steam starts in Big Picture","needs":"","on":true,"parent":"nvidia","selectable":true}
{"hideWhen":"bigpicture","hint":"in the tray at login, no window (untick: Steam's normal window)","id":"nvsilent","kind":"toggle","label":"Start Steam silently","needs":"","on":false,"parent":"nvidia","selectable":true}
{"hideWhen":"","hint":"Vapor look (cachyos-vapor)","id":"theme","kind":"toggle","label":"Install SteamOS theme","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"no password, lock screen or log out (SDDM)","id":"single","kind":"toggle","label":"Single user mode","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"the app on the desktop, Steamify Terminal in the launcher","id":"launcher","kind":"toggle","label":"Steamify shortcut","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"a notification when there's a new Steamify, never updates by itself","id":"notify","kind":"toggle","label":"Update notifications","needs":"","on":true,"parent":"","selectable":true}
{"hideWhen":"","hint":"Xbox wireless dongle (xone), Xbox controllers over Bluetooth (xpadneo)","id":"extended_controller_support","kind":"toggle","label":"Extended controller support","needs":"","on":false,"parent":"","selectable":true}
{"hideWhen":"","hint":"use Steam with the TV remote, TV on/off with the PC (experimental)","id":"cec","kind":"toggle","label":"HDMI-CEC","needs":"","on":false,"parent":"","selectable":true}
== backend_status [nvidia steam]
{"bios":null,"firstRun":true,"items":[{"hint":"Steam on the desktop, started at login","id":"nvidia","kind":"toggle","label":"Gaming on NVIDIA","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"the controller-friendly Steam (untick: Steam's normal window)","id":"bigpicture","kind":"toggle","label":"Steam starts in Big Picture","needs":"","new":false,"on":false,"parent":"nvidia","selectable":true,"update":false,"wanted":true},{"hint":"in the tray at login, no window (untick: Steam's normal window)","id":"nvsilent","kind":"toggle","label":"Start Steam silently","needs":"","new":false,"on":false,"parent":"nvidia","selectable":true,"update":false,"wanted":false},{"hint":"Vapor look (cachyos-vapor)","id":"theme","kind":"toggle","label":"Install SteamOS theme","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"no password, lock screen or log out (SDDM)","id":"single","kind":"toggle","label":"Single user mode","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"the app on the desktop, Steamify Terminal in the launcher","id":"launcher","kind":"toggle","label":"Steamify shortcut","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"a notification when there's a new Steamify, never updates by itself","id":"notify","kind":"toggle","label":"Update notifications","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":true},{"hint":"Xbox wireless dongle (xone), Xbox controllers over Bluetooth (xpadneo)","id":"extended_controller_support","kind":"toggle","label":"Extended controller support","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":false},{"hint":"use Steam with the TV remote, TV on/off with the PC (experimental)","id":"cec","kind":"toggle","label":"HDMI-CEC","needs":"","new":false,"on":false,"parent":"","selectable":true,"update":false,"wanted":false}],"pinnedKernel":"7.1.6-1","steamMachine":false,"version":"0.0.0"}
-135
View File
@@ -1,135 +0,0 @@
#!/bin/bash
# Golden-output test for the menu rules (lib/menu.sh, lib/backend.sh): what
# toggling does to WANTED, what plan_changes picks, and the JSON the app and
# the installer read. Hardware and package state are stubbed, so no root, VM
# or hardware is needed.
# bash tests/menu-test.sh compare with menu-test.golden
# bash tests/menu-test.sh --update rewrite the golden file (review the diff!)
# The Steamify checkout (REPO, default: the steamify-cachyos next to this repo)
REPO="${REPO:-$(cd "$(dirname "$0")/../../steamify-cachyos" && pwd)}"; HERE="$(cd "$(dirname "$0")" && pwd)"; cd "$REPO" || exit 1
T="$(mktemp -d)"; trap 'rm -rf "$T"' EXIT
SCRIPT_DIR="$PWD"; HOME="$T/home"; mkdir -p "$HOME"
VERSION=0.0.0
for lib in $(grep -oP '^for lib in \K.*(?=; do$)' steamify.sh); do
source "lib/$lib.sh"
done
info() { :; }; ok() { :; }; warn() { :; }; err() { :; }
# From the entry point.
RESTART_FOR_LOGIN=false; restart_needed() { return 1; }
# Hardware profile and what is "on", set per scenario.
HW=""; ON=""
has() { [[ " $HW " == *" $1 "* ]]; }
nvidia_present() { has nvidia; }
nvidia_available() { has nvidia; }
bigpicture_available() { has nvidia; }
machine_available() { has fremont; }
detect_valve_fremont() { has fremont; }
vram_available() { has vram; }
vram_selectable() { return 0; }
steamgame_available() { has steam; }
kpin_available() { has fremont && [[ " $ON " == *" kpin "* ]]; }
hdmi_available() { return 1; }
bios_available() { return 1; }
bios_selectable() { return 1; }
cec_repair() { return 1; }
launcher_repair() { return 1; }
bios_lookup_newest() { :; }
kernel_overview() { :; }
os_version_refresh() { :; }
for c in "${COMPONENTS[@]}"; do
eval "${c}_status() { [[ \" \$ON \" == *\" $c \"* ]]; }"
done
show() {
# show <label>: WANTED as the ticked ids, then the plan.
local c w=""
for c in "${COMPONENTS[@]}"; do [[ "${WANTED[$c]:-0}" == 1 ]] && w+=" $c"; done
echo "$1"
echo " wanted:$w"
plan_changes
echo " disable: ${TO_DISABLE[*]:-}"
echo " enable: ${TO_ENABLE[*]:-}"
}
scenario() {
# scenario <name> <hardware> <on> [toggle...]
local name="$1"; HW="$2"; ON="$3"; shift 3
rm -rf "$STATE_DIR"; REAPPLY=false
detect_components
local t
for t in "$@"; do toggle_component "$t"; done
show "== $name"
}
run() {
scenario "pc first run" "" ""
scenario "pc first run, untick gaming" "" "" gaming
scenario "pc single on" "" "" gaming single
scenario "pc single then gaming off" "" "" single gaming
scenario "pc boot desktop" "" "" boot
scenario "pc boot desktop then gaming off" "" "" boot gaming
scenario "pc silent only" "" "gaming" silent
scenario "pc all on, theme off" "" "gaming boot silent theme glyphs single launcher steamgame notify" theme
scenario "pc launcher off" "" "launcher steamgame" launcher
scenario "pc steamgame on without launcher" "steam" "" gaming launcher steamgame
scenario "machine first run" "fremont steam vram" ""
scenario "machine silent hidden until boot desktop" "fremont steam vram" "gaming silent"
scenario "machine boot desktop keeps silent" "fremont steam vram" "gaming silent" boot
scenario "machine off" "fremont steam vram" "gaming machine poweroff" machine
scenario "machine poweroff off" "fremont steam vram" "gaming machine poweroff" poweroff
scenario "machine poweroff on brings machine" "fremont steam vram" "gaming" poweroff
scenario "machine kpin removal" "fremont steam vram" "gaming machine poweroff kpin"
scenario "nvidia first run" "nvidia steam" ""
scenario "nvidia bigpicture off" "nvidia steam" "nvidia bigpicture" bigpicture
scenario "nvidia nvsilent on" "nvidia steam" "nvidia bigpicture" nvsilent
scenario "nvidia nvsilent then bigpicture" "nvidia steam" "nvidia" nvsilent bigpicture
scenario "nvidia off" "nvidia steam" "nvidia bigpicture nvsilent" nvidia
scenario "nvidia single" "nvidia steam" "" single
scenario "nvidia gaming already on" "nvidia steam" "gaming" gaming
# Feature versions: an old record is ticked and re-applied.
HW=""; ON="gaming theme"; rm -rf "$STATE_DIR"; REAPPLY=false
state_set features gaming 2.1.0; state_set features theme 2.1.0
detect_components; show "== outdated features"
REAPPLY=true; ON="gaming theme launcher"; detect_components; show "== reapply (a)"
REAPPLY=false
# The app's own rules (backend_apply's normalisation) go through the same plan.
HW="fremont steam vram"; ON=""; rm -rf "$STATE_DIR"
detect_components
local sel
for sel in "single" "boot" "poweroff" "silent" "kpin" "gaming boot" "launcher steamgame"; do
echo "== backend_apply $sel"
backend_sudo() { return 0; }
backend_run_component() { return 0; }
# shellcheck disable=SC2086
backend_apply $sel | jq -c 'select(.event == "plan")'
done
HW="nvidia steam"; ON=""; detect_components
for sel in "bigpicture" "nvsilent" "bigpicture nvsilent" "single" "boot"; do
echo "== backend_apply nvidia $sel"
# shellcheck disable=SC2086
backend_apply $sel | jq -c 'select(.event == "plan")'
done
# JSON for the app and the installer page.
local hw
for hw in "" "fremont steam vram" "nvidia steam"; do
HW="$hw"; ON=""; rm -rf "$STATE_DIR"; detect_components
echo "== defaults_list [$hw]"
defaults_list | jq -S -c '.[]'
echo "== backend_status [$hw]"
backend_status | jq -S -c 'del(.kernel, .cecDevices, .leds)'
done
}
if [[ "${1:-}" == --update ]]; then
run > "$HERE/menu-test.golden" 2>&1
echo "wrote $HERE/menu-test.golden ($(wc -l < "$HERE/menu-test.golden") lines)"
elif diff -u "$HERE/menu-test.golden" <(run 2>&1); then
echo "ok menu rules, plan and JSON match the golden file"
else
echo "FAIL menu rules changed (diff above; --update if intended)"; exit 1
fi
-96
View File
@@ -1,96 +0,0 @@
#!/bin/bash
# Test of the NVIDIA fix on a real PC with an NVIDIA GPU (the unit test fakes
# one: tests/nvidia-test.sh). Run from this repo (REPO points at the Steamify checkout):
# bash tests/nvidia-hardware-test.sh check read-only: what is detected, what the live kernel uses, what apply would change
# bash tests/nvidia-hardware-test.sh apply really applies the fix (sudo), then reboot and run check again
# bash tests/nvidia-hardware-test.sh visual after trying gaming mode: was the picture clean? (asks you)
# Every run appends to ~/steamify-nvidia-report.txt: paste that file when asking for help.
# The Steamify checkout (REPO, default: the steamify-cachyos next to this repo)
REPO="${REPO:-$(cd "$(dirname "$0")/../../steamify-cachyos" && pwd)}"; cd "$REPO" || exit 1
# patch_file (lib/common.sh) reads the hook script from $SCRIPT_DIR/patches.
SCRIPT_DIR="$PWD"
REPORT="${NVIDIA_REPORT:-$HOME/steamify-nvidia-report.txt}"
source lib/common.sh; source lib/hdmi-refresh.sh; source lib/vram-booster.sh; source lib/nvidia.sh
mode="${1:-check}"
fail=0
say() { echo "$*" | tee -a "$REPORT"; }
pass() { say "PASS $*"; }
bad() { say "FAIL $*"; fail=1; }
note() { say " $*"; }
root_cat() { cat "$1" 2>/dev/null || sudo cat "$1" 2>/dev/null || echo "(unreadable)"; }
say ""; say "===== $(date '+%F %T') mode=$mode kernel=$(uname -r)"
report_system() {
say "-- GPU and software"
lspci -nn 2>/dev/null | grep -iE 'vga|3d|display' | while read -r l; do note "$l"; done
command -v nvidia-smi >/dev/null && note "driver: $(nvidia-smi --query-gpu=name,driver_version --format=csv,noheader 2>&1 | head -n 1)"
for p in nvidia-open nvidia-utils nvidia-dkms gamescope gamescope-session-cachyos linux-cachyos-nvidia-open; do
v="$(pacman -Q "$p" 2>/dev/null)" && note "$v"
done
note "boot loader file: $(nvidia_boot_file || true)"
note "cmdline: $(cat /proc/cmdline)"
}
check_state() {
say "-- Steamify's view"
if nvidia_present; then pass "a supported NVIDIA GPU (RTX 20 series or newer) with its driver is detected"
elif nvidia_legacy_gpu; then bad "the NVIDIA GPU is older than RTX 20 (on chwd's legacy lists in $VRAM_CHWD_IDS): the fix is not applied"
else bad "no NVIDIA GPU with its driver detected: the fix does nothing here"; fi
for c in /sys/class/drm/card[0-9]*/device; do
[[ "$(cat "$c/vendor" 2>/dev/null)" == 0x10de ]] && note "NVIDIA PCI device id $(cat "$c/device") $(vram_nvidia_legacy_id "$(cat "$c/device")" && echo '(on a chwd legacy list)' || echo '(not on a legacy list: RTX 20 or newer)')"
done
ls "$VRAM_CHWD_IDS"/nvidia-*.ids >/dev/null 2>&1 || note "no chwd legacy lists found: every NVIDIA card counts as supported"
local p
for p in $NVIDIA_PARAMS; do
if grep -qw -- "$p" /proc/cmdline; then pass "$p is on the running kernel's command line"
elif nvidia_param_set "$p"; then bad "$p is in the boot loader's file but not booted yet: reboot"
else bad "$p is missing (run: bash tests/nvidia-hardware-test.sh apply)"; fi
done
say "-- the live NVIDIA driver"
local m
for m in modeset fbdev; do
v="$(root_cat /sys/module/nvidia_drm/parameters/$m)"
if [[ "$v" == Y ]]; then pass "nvidia_drm $m = Y"; else bad "nvidia_drm $m = $v (want Y)"; fi
done
lsmod | grep -q '^nvidia_drm' && pass "nvidia_drm is loaded" || bad "nvidia_drm is not loaded"
local c found=0
for c in /sys/class/drm/card[0-9]*/device; do
[[ "$(cat "$c/vendor" 2>/dev/null)" == 0x10de ]] || continue
found=1; note "NVIDIA card: ${c%/device} driver=$(basename "$(readlink -f "$c/driver" 2>/dev/null)")"
for s in "${c%/device}"/card*-*/status; do [[ -f "$s" ]] && note " $(basename "$(dirname "$s")"): $(cat "$s")"; done
done
[[ $found == 1 ]] || bad "no NVIDIA card in /sys/class/drm"
say "-- initramfs"
if nvidia_modules_everywhere; then note "every installed kernel has nvidia_drm: the early-load drop-in is used"
else note "not every installed kernel has nvidia_drm: the early-load drop-in is skipped (parameters still apply)"; fi
nvidia_initramfs_ok && note "NVIDIA modules are in the initramfs config" || note "NVIDIA modules are not in the initramfs config"
say "-- what apply would change"
if nvidia_params_missing; then note "adds $NVIDIA_PARAMS to $(nvidia_boot_file)"; else note "kernel parameters: nothing to change"; fi
if ! nvidia_initramfs_ok && nvidia_modules_everywhere; then note "writes $NVIDIA_INITRAMFS_CONF"; fi
}
case "$mode" in
check)
report_system; check_state ;;
apply)
report_system
say "-- applying"
if nvidia_enable 2>&1 | tee -a "$REPORT"; [[ ${PIPESTATUS[0]} == 0 ]]; then pass "nvidia_enable finished"
else bad "nvidia_enable failed (output above)"; fi
say "Now reboot, then run: bash tests/nvidia-hardware-test.sh check" ;;
visual)
report_system
say "-- try gaming mode now (log in to it or reboot into it), look at the screen, then answer"
read -rp "Is the gaming mode picture clean (no corruption, no flicker)? [y/n/partly] " a
read -rp "What did you see (one line, e.g. 'garbled colours at start, fine after login')? " what
say "visual result: $a: $what"
[[ "$a" == y ]] && pass "picture clean" || bad "picture not clean ($a)"
say "-- gamescope and NVIDIA lines from this boot's log"
{ journalctl -b --no-pager 2>/dev/null || sudo journalctl -b --no-pager 2>/dev/null; } |
grep -iE 'gamescope|nvidia|drm|nvrm|wlserver|vulkan' | tail -n 80 | tee -a "$REPORT" | tail -n 5
note "(more in $REPORT)" ;;
*) echo "usage: $0 check|apply|visual"; exit 2 ;;
esac
say ""; say "Report: $REPORT"
exit $fail
-207
View File
@@ -1,207 +0,0 @@
#!/bin/bash
# Tests lib/nvidia.sh ("Gaming on NVIDIA" and its Big Picture option) and the menu rules around it, against a fake RTX 5080
# (PCI 10de:2c02) beside an Intel iGPU, a stub modinfo, pacman and systemctl, and a temp home. No root, VM or NVIDIA
# hardware needed: bash tests/nvidia-test.sh
# The Steamify checkout (REPO, default: the steamify-cachyos next to this repo)
REPO="${REPO:-$(cd "$(dirname "$0")/../../steamify-cachyos" && pwd)}"; cd "$REPO" || exit 1
T="$(mktemp -d)"; trap 'rm -rf "$T"' EXIT
fail=0
check() { if eval "$2"; then echo "ok $1"; else echo "FAIL $1"; fail=1; fi; }
SCRIPT_DIR="$PWD"; HOME="$T/home"; mkdir -p "$HOME"
source lib/common.sh; source lib/state.sh; source lib/login-manager.sh; source lib/single-user.sh; source lib/hdmi-refresh.sh; source lib/vram-booster.sh; source lib/nvidia.sh; source lib/menu.sh
# Runs as the user, and refuses anything under the real system folders: a test must never touch them.
sudo() { local a; for a in "$@"; do case "$a" in /etc/*|/usr/*|/boot/*|/var/*) echo "REFUSED sudo $*" >&2; return 1 ;; esac; done; "$@"; }
info() { :; }; ok() { :; }; warn() { :; }; err() { echo "ERR $*"; }
# KDE's config tools on plain files under the temp home (a relative file name is relative to ~/.config, as in KDE).
kwriteconfig6() {
local f="" k="" v="" del=0
while [[ $# -gt 0 ]]; do case "$1" in --file) f="$2"; shift ;; --key) k="$2"; shift ;; --group) shift ;; --delete) del=1 ;; *) v="$1" ;; esac; shift; done
[[ "$f" == /* ]] || f="$HOME/.config/$f"; mkdir -p "$(dirname "$f")"
if [[ $del == 1 ]]; then sed -i "/^$k=/d" "$f" 2>/dev/null; else sed -i "/^$k=/d" "$f" 2>/dev/null; printf '%s=%s\n' "$k" "$v" >> "$f"; fi
}
kreadconfig6() {
local f="" k="" d="" r
while [[ $# -gt 0 ]]; do case "$1" in --file) f="$2"; shift ;; --key) k="$2"; shift ;; --default) d="$2"; shift ;; --group) shift ;; esac; shift; done
[[ "$f" == /* ]] || f="$HOME/.config/$f"
r="$(sed -n "s/^$k=//p" "$f" 2>/dev/null)"; printf '%s\n' "${r:-$d}"
}
# A pretend package database and systemd: what the tests assert on.
PKGS=""; PACMAN_LOG="$T/pacman.log"; : > "$PACMAN_LOG"; ENABLED=0
pacman() {
case "$1" in
-Q) [[ " $PKGS " == *" $2 "* ]] ;;
-S) echo "S $*" >> "$PACMAN_LOG"; PKGS+=" ${*: -1}" ;;
-Rns) echo "R $*" >> "$PACMAN_LOG"; PKGS=" ${PKGS//${*: -1}/} " ;;
esac
}
user_systemctl() {
case "$1" in
enable) ENABLED=1 ;;
disable) ENABLED=0 ;;
is-enabled) [[ $ENABLED == 1 ]] ;;
esac
return 0
}
FAKE_DRIVER=1; modinfo() { [[ "$FAKE_DRIVER" == 1 ]]; }
GAMING_ON=0; gaming_status() { [[ "$GAMING_ON" == 1 ]]; }
UNIT="$HOME/.config/systemd/user/$NVIDIA_UNIT"
mkdir -p "$T/drm/card0/device" "$T/drm/card1/device"
echo 0x8086 > "$T/drm/card0/device/vendor"; echo 0x030000 > "$T/drm/card0/device/class"
echo 0x10de > "$T/drm/card1/device/vendor"; echo 0x030000 > "$T/drm/card1/device/class"
echo 0x2c02 > "$T/drm/card1/device/device"
NVIDIA_DRM_DIR="$T/drm"
backup_file() { [[ -f "$1.bak" ]] || cp "$1" "$1.bak"; }
REBUILDS=0; nvidia_rebuild_boot() { REBUILDS=$((REBUILDS + 1)); }
: > "$T/cmdline"; NVIDIA_CMDLINE="$T/cmdline" # the running kernel's command line: without the parameters
NVIDIA_INITRAMFS_CONF="$T/90-steamify-nvidia.conf"
NVIDIA_SCRIPT="$T/libexec/steamify-nvidia-initramfs"; NVIDIA_HOOK="$T/hooks/85-steamify-nvidia-initramfs.hook"
mkdir -p "$T/mods/6.1.0-cachyos/kernel"; : > "$T/mods/6.1.0-cachyos/kernel/nvidia-drm.ko.zst"; : > "$T/mods/6.1.0-cachyos/pkgbase"
NVIDIA_MODULES_DIR="$T/mods"
NVIDIA_LIMINE_CONF="$T/none/limine.conf"; NVIDIA_SDBOOT_DIR="$T/none/entries"; NVIDIA_GRUB_CFG="$T/none/grub.cfg" # not generated here: not checked
mkdir -p "$T/chwd"; VRAM_CHWD_IDS="$T/chwd"
# The component's kernel step edits this boot loader file (systemd-boot's) in the tests, never a real one.
printf 'LINUX_OPTIONS="quiet"\n' > "$T/sdboot-manage.conf"; nvidia_boot_file() { echo "$T/sdboot-manage.conf"; }
# --- detection
check "finds the NVIDIA card as card1 beside an iGPU" nvidia_present
FAKE_DRIVER=0; check "no nvidia_drm module (nouveau): not an NVIDIA PC" '! nvidia_present'
FAKE_DRIVER=1; NVIDIA_DRM_DIR="$T/none"; check "no NVIDIA GPU: not an NVIDIA PC" '! nvidia_present'
NVIDIA_DRM_DIR="$T/drm"
# --- which items the menu shows
check "NVIDIA PC: the SteamOS conversion and its gamescope options are hidden" '! component_available gaming && ! component_available boot && ! component_available glyphs'
check "NVIDIA PC: Gaming on NVIDIA and Big Picture are shown" 'component_available nvidia && component_available bigpicture'
GAMING_ON=1
check "NVIDIA PC with the conversion already on: it is shown, so it can be turned off" 'component_available gaming && component_available boot'
check "...and Gaming on NVIDIA is hidden (both would start Steam)" '! component_available nvidia && ! component_available bigpicture'
GAMING_ON=0; NVIDIA_DRM_DIR="$T/none"
check "no NVIDIA: the conversion is shown as before" 'component_available gaming && component_available boot'
check "...and Gaming on NVIDIA is hidden" '! component_available nvidia && ! component_available bigpicture'
NVIDIA_DRM_DIR="$T/drm"
# --- "Add as non-Steam game" is for gaming mode's controller: not offered on an NVIDIA PC (unless already on)
steamgame_available() { return 0; }; STEAMGAME_ON=0; steamgame_status() { [[ "$STEAMGAME_ON" == 1 ]]; }
check "NVIDIA PC: Add as non-Steam game is hidden" '! component_available steamgame'
STEAMGAME_ON=1; check "...unless it is already on (so it can be turned off)" 'component_available steamgame'; STEAMGAME_ON=0
NVIDIA_DRM_DIR="$T/none"; check "no NVIDIA: it is offered as before" 'component_available steamgame'
steamgame_available() { return 1; }; check "no NVIDIA, no Steam account yet: still not offered" '! component_available steamgame'
steamgame_available() { return 0; }; NVIDIA_DRM_DIR="$T/drm"
# --- turning it on
check "off to begin with" '! nvidia_status && ! bigpicture_status'
nvidia_enable
check "Steam is installed when missing, and recorded as ours" 'grep -q "^S " "$PACMAN_LOG" && [[ "$(state_get nvidia installed_pkgs)" == steam ]]'
check "the autostart unit is written: Steam without arguments" 'grep -q "^ExecStart=/usr/bin/steam $" "$UNIT"'
check "it is on" 'nvidia_status && ! bigpicture_status'
n="$(wc -l < "$PACMAN_LOG")"; nvidia_enable
check "a second run installs nothing again" '[[ "$(wc -l < "$PACMAN_LOG")" == "$n" ]]'
# --- Big Picture
bigpicture_enable
check "Big Picture: Steam starts with -gamepadui" 'grep -q "^ExecStart=/usr/bin/steam -gamepadui$" "$UNIT" && bigpicture_status'
nvidia_enable
check "turning Gaming on NVIDIA on again keeps Big Picture" 'bigpicture_status'
check "Big Picture: Plasma starts with an empty session" '[[ "$(kreadconfig6 --file ksmserverrc --group General --key loginMode)" == emptySession ]]'
bigpicture_disable
check "Big Picture off: Steam's normal window again" '! bigpicture_status && nvidia_status'
check "...and the session setting is back as it was (not set)" '[[ -z "$(kreadconfig6 --file ksmserverrc --group General --key loginMode)" ]]'
# --- turning it off
bigpicture_enable; nvidia_disable
check "off: the unit is gone and Steam, which we installed, is removed" '! nvidia_status && [[ ! -e "$UNIT" ]] && ! pacman -Q steam && [[ -z "$(state_get nvidia installed_pkgs)" ]]'
check "Big Picture without the unit does nothing" 'bigpicture_enable && bigpicture_disable && [[ ! -e "$UNIT" ]]'
# --- Steam that was there already is never removed
PKGS=" steam"; : > "$PACMAN_LOG"
nvidia_enable; nvidia_disable
check "Steam installed before is kept, and not installed again" '[[ ! -s "$PACMAN_LOG" ]] && pacman -Q steam'
# --- kernel parameters and early modules (a supported card only: RTX 20 series or newer)
check "RTX 5080 (2c02) is supported: no chwd legacy lists" nvidia_supported
printf '1c03\n1b80\n' > "$T/chwd/nvidia-580.ids" # a GTX 1060 and a GTX 1080: chwd's 580xx legacy list
check "RTX 5080 is not on a legacy list" 'nvidia_supported && ! nvidia_legacy_gpu'
echo 0x1c03 > "$T/drm/card1/device/device"
check "GTX 1060 (older than RTX 20): no kernel setup, but still an NVIDIA PC" '! nvidia_supported && nvidia_legacy_gpu && nvidia_present'
echo 0x2c02 > "$T/drm/card1/device/device"
for loader in limine limineplus sdboot grub; do
case $loader in
limine) F="$T/limine"; printf 'KERNEL_CMDLINE[default]="quiet splash"\n' > "$F" ;;
# CachyOS writes its line as "+=" and may already have nvidia_drm.modeset=1 in it.
limineplus) F="$T/limine"; printf 'KERNEL_CMDLINE[default]+="quiet splash nvidia_drm.modeset=1"\n' > "$F" ;;
sdboot) F="$T/sdboot-manage.conf"; printf 'LINUX_OPTIONS="quiet splash"\n' > "$F" ;;
grub) F="$T/grub"; printf 'GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"\n' > "$F" ;;
esac
nvidia_boot_file() { echo "$F"; }
rm -f "$NVIDIA_INITRAMFS_CONF"; REBUILDS=0; ORIG="$(cat "$F")"
nvidia_kernel_enable
check "$loader: parameters added" 'grep -q "nvidia-drm.modeset=1" "$F" && grep -q "nvidia-drm.fbdev=1" "$F"'
check "$loader: initramfs drop-in written" 'grep -q nvidia_drm "$NVIDIA_INITRAMFS_CONF"'
check "$loader: script and pacman hook installed" '[[ -x "$NVIDIA_SCRIPT" ]] && grep -q "^Exec = $NVIDIA_SCRIPT sync" "$NVIDIA_HOOK"'
check "$loader: backup made, rebuilt once" '[[ -f "$F.bak" && $REBUILDS == 1 ]]'
SUM="$(cat "$F")"; nvidia_kernel_enable
check "$loader: second run changes nothing" '[[ "$(cat "$F")" == "$SUM" && $REBUILDS == 1 ]]'
nvidia_kernel_disable
check "$loader: disable restores the file, drops the drop-in, script and hook" '[[ "$(cat "$F")" == "$ORIG" && ! -f "$NVIDIA_INITRAMFS_CONF" && ! -e "$NVIDIA_SCRIPT" && ! -e "$NVIDIA_HOOK" ]]'
done
# A missing patches/ file must be an error, never an empty script or hook.
( SCRIPT_DIR="$T/no-patches"; NVIDIA_SCRIPT="$T/empty/script"; NVIDIA_HOOK="$T/empty/hook"; ! nvidia_hook_install 2>/dev/null && [[ ! -s "$T/empty/script" ]] ) && echo "ok a missing patch file is an error, nothing empty installed" || { echo "FAIL a missing patch file is an error, nothing empty installed"; fail=1; }
# The pacman hook's script decides again at every kernel change.
syncit() { NVIDIA_INITRAMFS_CONF="$NVIDIA_INITRAMFS_CONF" NVIDIA_MODULES_DIR="$NVIDIA_MODULES_DIR" bash "$NVIDIA_SCRIPT" sync; }
nvidia_hook_install; rm -f "$NVIDIA_INITRAMFS_CONF"
syncit; check "hook script: every kernel has the modules -> drop-in written" '[[ -f "$NVIDIA_INITRAMFS_CONF" ]]'
mkdir -p "$T/mods/6.6.0-lts/kernel"; : > "$T/mods/6.6.0-lts/pkgbase"
syncit; check "hook script: a new kernel without modules -> drop-in removed" '[[ ! -f "$NVIDIA_INITRAMFS_CONF" ]]'
: > "$T/mods/6.6.0-lts/kernel/nvidia-drm.ko.zst"
syncit; check "hook script: the modules arrive (DKMS built) -> drop-in back" '[[ -f "$NVIDIA_INITRAMFS_CONF" ]]'
rm -rf "$T/mods/6.6.0-lts"; mkdir -p "$T/mods/5.0.0-leftover"; : > "$T/mods/5.0.0-leftover/modules.dep"
syncit; check "hook script: a leftover folder of a removed kernel is ignored" '[[ -f "$NVIDIA_INITRAMFS_CONF" ]]'
rm -rf "$T/mods/5.0.0-leftover"; nvidia_kernel_disable
# A second kernel without the NVIDIA modules: no early-load drop-in, parameters still set.
mkdir -p "$T/mods/6.6.0-lts/kernel"; : > "$T/mods/6.6.0-lts/pkgbase"; F="$T/sdboot-manage.conf"; printf 'LINUX_OPTIONS="quiet"\n' > "$F"
nvidia_boot_file() { echo "$F"; }; rm -f "$NVIDIA_INITRAMFS_CONF"; REBUILDS=0; nvidia_kernel_enable
check "kernel without nvidia modules: no drop-in, parameters set" '[[ ! -f "$NVIDIA_INITRAMFS_CONF" ]] && grep -q "nvidia-drm.fbdev=1" "$F"'
rm -rf "$T/mods/6.6.0-lts"
# Generated boot entries without the parameters (the initramfs build failed): an error.
printf 'linux /vmlinuz root=/dev/sda1\n' > "$T/grub.cfg"; NVIDIA_GRUB_CFG="$T/grub.cfg"
F="$T/grub"; printf 'GRUB_CMDLINE_LINUX_DEFAULT="quiet"\n' > "$F"; nvidia_boot_file() { echo "$F"; }; rm -f "$NVIDIA_INITRAMFS_CONF"
check "boot entries without the parameters: error" '! nvidia_kernel_enable >/dev/null'
NVIDIA_GRUB_CFG="$T/none/grub.cfg"
# A config file without the setting to edit: an error, not "applied".
F="$T/sdboot-manage.conf"; printf '#LINUX_OPTIONS=""\n' > "$F"; nvidia_boot_file() { echo "$F"; }; REBUILDS=0
check "no setting to edit: error, nothing rebuilt" '! nvidia_kernel_enable >/dev/null && [[ $REBUILDS == 0 ]]'
FAKE_DRIVER=0; NVIDIA_DRM_DIR="$T/drm"; printf 'LINUX_OPTIONS="quiet"\n' > "$T/sdboot"; F="$T/sdboot"; REBUILDS=0; nvidia_kernel_enable
check "no NVIDIA driver: nothing touched" '[[ "$(cat "$F")" == "LINUX_OPTIONS=\"quiet\"" && $REBUILDS == 0 ]]'
# --- single user mode on NVIDIA: its own login step (SDDM, autologin into Plasma) without the conversion
FAKE_DRIVER=1; NVIDIA_DRM_DIR="$T/drm" # an earlier section ended without the driver
SW="$T/switches.log"; : > "$SW"; TARGET_USER=tester; SINGLE_AUTOLOGIN="$T/sddm.conf.d/zzz-steamify-autologin.conf"
switch_to_sddm() { echo sddm >> "$SW"; }; switch_to_plasmalogin() { echo plasmalogin >> "$SW"; }
remove_sddm_base_autologin() { :; } # would look at the real /etc/sddm.conf
GAMING_ON=0
check "single user mode is offered on NVIDIA without the conversion..." 'component_available single'
GAMING_ON=1; check "...and with it" 'component_available single'; GAMING_ON=0
single_login_enable
check "single user: SDDM, autologin of the user into the Plasma session" 'grep -qx "User=tester" "$SINGLE_AUTOLOGIN" && grep -qx "Session=plasma.desktop" "$SINGLE_AUTOLOGIN" && grep -qx "Relogin=true" "$SINGLE_AUTOLOGIN" && grep -qx sddm "$SW"'
c1="$(cat "$SINGLE_AUTOLOGIN")"; single_login_enable
check "single user login: a second run changes nothing" '[[ "$(cat "$SINGLE_AUTOLOGIN")" == "$c1" ]]'
single_login_disable
check "single user off: the file is gone and plasma-login-manager is the login manager again" '[[ ! -e "$SINGLE_AUTOLOGIN" ]] && grep -qx plasmalogin "$SW"'
: > "$SW"; GAMING_ON=1; single_login_enable
check "with the conversion on, single user mode leaves the login to it" '[[ ! -e "$SINGLE_AUTOLOGIN" && ! -s "$SW" ]]'
GAMING_ON=0
# --- the menu rules
declare -A WANTED=([nvidia]=1 [bigpicture]=1 [gaming]=0 [boot]=0 [single]=0 [machine]=0 [poweroff]=0 [launcher]=0 [steamgame]=0)
component_selectable() { return 0; }
toggle_component nvidia
check "unticking Gaming on NVIDIA unticks Big Picture" '[[ "${WANTED[nvidia]}" == 0 && "${WANTED[bigpicture]}" == 0 ]]'
toggle_component bigpicture
check "ticking Big Picture ticks Gaming on NVIDIA" '[[ "${WANTED[nvidia]}" == 1 && "${WANTED[bigpicture]}" == 1 ]]'
WANTED[single]=0; WANTED[gaming]=0; toggle_component single
check "ticking single user mode does not tick the conversion where it isn't offered" '[[ "${WANTED[single]}" == 1 && "${WANTED[gaming]}" == 0 ]]'
check "Big Picture follows Gaming on NVIDIA in the menu" '[[ " ${COMPONENTS[*]} " == *" nvidia bigpicture "* && "${PARENT[bigpicture]}" == nvidia ]]'
exit $fail
-161
View File
@@ -1,161 +0,0 @@
"""Unit tests of the Python helpers in steamify-cachyos/patches (stdlib only).
Run through tests/python-test.sh (REPO = the Steamify checkout), or:
REPO=../steamify-cachyos python3 -m unittest tests/patches_test.py -v
"""
import contextlib
import importlib.util
import io
import os
import struct
import tempfile
import unittest
from unittest import mock
REPO = os.environ.get("REPO") or os.path.join(os.path.dirname(__file__), "..", "..", "steamify-cachyos")
def load(name, filename):
spec = importlib.util.spec_from_file_location(name, os.path.join(REPO, "patches", filename))
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
shortcuts = load("steam_shortcuts", "steam-shortcuts.py")
notifier = load("steamify_notifier", "steamify-notifier.py")
class VdfTests(unittest.TestCase):
def test_roundtrip(self):
root = [("shortcuts", [("0", [("appid", 123), ("AppName", "A"), ("tags", [])])])]
self.assertEqual(shortcuts.parse(shortcuts.dump(root)), root)
def test_empty_data_is_an_empty_list(self):
self.assertEqual(shortcuts.parse(b""), [("shortcuts", [])])
def test_unknown_type_is_an_error(self):
with self.assertRaises(ValueError):
shortcuts.parse(b"\x07key\0")
def test_ints_are_unsigned_32_bit(self):
data = shortcuts.dump([("n", -1)])
self.assertEqual(struct.unpack_from("<I", data, 3)[0], 0xFFFFFFFF)
def test_keys_match_regardless_of_case(self):
entry = [("AppName", "x")]
self.assertEqual(shortcuts.get(entry, "appname"), "x")
shortcuts.put(entry, "APPNAME", "y")
self.assertEqual(entry, [("AppName", "y")])
def test_ours(self):
exe = "/home/u/.local/share/steamify/bin/run-app"
self.assertTrue(shortcuts.ours([("Exe", f'"{exe}"')], exe))
# An older install's start script counts as ours: updated, not doubled.
self.assertTrue(shortcuts.ours([("Exe", '"/home/u/.local/share/cachyos-gamescope-boot/bin/run-app"')], exe))
self.assertFalse(shortcuts.ours([("Exe", '"/usr/bin/other"')], exe))
self.assertFalse(shortcuts.ours([("Exe", '"/home/u/games/run-app"')], exe))
class ShortcutsCommandTests(unittest.TestCase):
def setUp(self):
self.dir = tempfile.TemporaryDirectory()
self.addCleanup(self.dir.cleanup)
self.vdf = os.path.join(self.dir.name, "userdata", "1", "config", "shortcuts.vdf")
self.exe = "/home/u/.local/share/steamify/bin/run-app"
def run_cmd(self, *args):
out = io.StringIO()
with contextlib.redirect_stdout(out), contextlib.redirect_stderr(io.StringIO()):
rc = shortcuts.main(["steam-shortcuts.py", *args])
return rc, out.getvalue().strip()
def entries(self):
return shortcuts.shortcuts(shortcuts.load(self.vdf))
def test_add_creates_the_file_and_an_entry(self):
rc, out = self.run_cmd("add", self.vdf, "Steamify", self.exe, "/icon.png", "steamify.desktop")
self.assertEqual(rc, 0)
self.assertTrue(out.startswith("added "))
(_, entry), = self.entries()
self.assertEqual(shortcuts.get(entry, "AppName"), "Steamify")
self.assertEqual(shortcuts.get(entry, "Exe"), f'"{self.exe}"')
# Steam's id for a shortcut has the top bit set.
self.assertGreaterEqual(shortcuts.get(entry, "appid"), 0x80000000)
self.assertEqual(out, f"added {shortcuts.get(entry, 'appid')}")
def test_add_twice_updates_instead_of_doubling(self):
self.run_cmd("add", self.vdf, "Steamify", self.exe, "/a.png", "a.desktop")
rc, out = self.run_cmd("add", self.vdf, "Steamify 2", self.exe, "/b.png", "b.desktop")
self.assertTrue(out.startswith("updated "))
entries = self.entries()
self.assertEqual(len(entries), 1)
self.assertEqual(shortcuts.get(entries[0][1], "AppName"), "Steamify 2")
self.assertEqual(shortcuts.get(entries[0][1], "icon"), "/b.png")
def test_other_entries_are_kept(self):
self.run_cmd("add", self.vdf, "Other", "/usr/bin/other", "", "")
self.run_cmd("add", self.vdf, "Steamify", self.exe, "", "")
self.assertEqual(len(self.entries()), 2)
self.assertEqual([k for k, _ in self.entries()], ["0", "1"])
def test_find(self):
self.assertEqual(self.run_cmd("find", self.vdf, self.exe)[0], 1)
_, out = self.run_cmd("add", self.vdf, "Steamify", self.exe, "", "")
rc, found = self.run_cmd("find", self.vdf, self.exe)
self.assertEqual((rc, found), (0, out.split()[1]))
def test_remove_by_appid_renumbers(self):
self.run_cmd("add", self.vdf, "Other", "/usr/bin/other", "", "")
_, out = self.run_cmd("add", self.vdf, "Steamify", self.exe, "", "")
self.assertEqual(self.run_cmd("remove", self.vdf, out.split()[1])[0], 0)
entries = self.entries()
self.assertEqual([k for k, _ in entries], ["0"])
self.assertEqual(shortcuts.get(entries[0][1], "AppName"), "Other")
def test_remove_of_nothing_leaves_the_file_alone(self):
self.run_cmd("add", self.vdf, "Steamify", self.exe, "", "")
with open(self.vdf, "rb") as f:
before = f.read()
self.assertEqual(self.run_cmd("remove", self.vdf, "999")[0], 0)
with open(self.vdf, "rb") as f:
self.assertEqual(f.read(), before)
def test_unknown_command(self):
self.assertEqual(self.run_cmd("bogus", self.vdf)[0], 2)
class NotifierTests(unittest.TestCase):
def test_version(self):
self.assertEqual(notifier.version("v2.11.1"), (2, 11, 1))
self.assertEqual(notifier.version("2.9.0"), (2, 9, 0))
self.assertIsNone(notifier.version("nightly"))
def test_versions_compare_as_numbers(self):
self.assertGreater(notifier.version("2.10.0"), notifier.version("2.9.0"))
def pending(self, new, seen="", skipped=""):
values = {"seen": seen, "skipped": skipped}
with mock.patch.object(notifier, "state", lambda key: values[key]):
return notifier.pending(new)
def test_newer_than_seen(self):
self.assertTrue(self.pending("2.12.0", seen="2.11.1"))
def test_same_or_older_is_not_pending(self):
self.assertFalse(self.pending("2.11.1", seen="2.11.1"))
self.assertFalse(self.pending("2.9.0", seen="2.11.1"))
def test_skipped_version_is_not_pending(self):
self.assertFalse(self.pending("2.12.0", seen="2.11.1", skipped="2.12.0"))
def test_nothing_seen_yet(self):
self.assertTrue(self.pending("2.12.0", seen=""))
def test_unparsable_tag_is_not_pending(self):
# A release tag like "nightly" must not crash the daily check.
self.assertFalse(self.pending("nightly", seen="2.11.1"))
if __name__ == "__main__":
unittest.main()
-12
View File
@@ -1,12 +0,0 @@
#!/bin/bash
# Unit tests of the Python helpers in steamify-cachyos/patches (steam-shortcuts.py,
# steamify-notifier.py). No root, VM or hardware needed: bash tests/python-test.sh
# The Steamify checkout (REPO, default: the steamify-cachyos next to this repo)
REPO="${REPO:-$(cd "$(dirname "$0")/../../steamify-cachyos" && pwd)}"
export REPO
cd "$(dirname "$0")/.." || exit 1
if python3 -B -m unittest tests/patches_test.py 2>&1 | tail -n 25 | tee /dev/stderr | grep -q '^OK'; then
echo "ok Python helpers"
else
echo "FAIL Python helpers"; exit 1
fi