diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..90a3af8
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,15 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+indent_style = space
+indent_size = 4
+
+[*.{yml,yaml,qml}]
+indent_size = 2
+
+[Makefile]
+indent_style = tab
diff --git a/.github/workflows/bundle.yml b/.github/workflows/bundle.yml
index 99a4132..276e679 100644
--- a/.github/workflows/bundle.yml
+++ b/.github/workflows/bundle.yml
@@ -40,7 +40,7 @@ jobs:
- name: Check
run: |
bash -n dist/steamify.sh
- python3 -m py_compile ui/steamify-ui
+ python3 -m py_compile ui/steamify-ui patches/*.py
# shellcheck through an action that brings its own binary: GitHub's runner has it installed,
# Gitea's act_runner image doesn't. One file per run, as before (the exceptions are the bundle's).
diff --git a/.shellcheckrc b/.shellcheckrc
new file mode 100644
index 0000000..4f310dd
--- /dev/null
+++ b/.shellcheckrc
@@ -0,0 +1,2 @@
+# The modules share variables across files once bundled (see AGENTS.md).
+disable=SC2034,SC2154
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a821bcf..3a88835 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes, per version and per commit. Versions follow
`steamify.sh`. Versions before 0.7.0 were numbered afterwards,
one per merged pull request.
+## Unreleased
+
+- **refactor: Valve's package mirror in one module (`lib/valve-mirror.sh`: newest repo, package database fields, checksummed download); the SteamOS extras, CEC driver, BIOS lookup and os-release version used four copies**
+- **test: the menu rules, the plan and the app's and installer's JSON are checked against a golden file (`tests/menu-test.sh` in steamify-cachyos-dev); `make check`, `.editorconfig`, `.shellcheckrc`**
+- **docs: README lists the sources and projects Steamify builds on, with licences and thanks**
+
## 2.11.1 - 2026-10-02
- `d70aeb8` **docs: new README screenshots of the app (2.11 menu) on a Steam Machine and on an NVIDIA PC, and the README names the opt-in "Start Steam silently in desktop mode" and the icons as an option of the conversion**
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1796693
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,24 @@
+# Local checks, the same as CI's: make check
+SH := steamify.sh $(wildcard lib/*.sh) ui/steamify-app.sh .github/tools/bundle.sh
+# The unit tests live in ../steamify-cachyos-dev/tests (REPO points at this checkout).
+DEV ?= ../steamify-cachyos-dev
+TESTS := $(wildcard $(DEV)/tests/*-test.sh)
+
+.PHONY: check syntax lint test bundle fmt-check
+check: syntax lint test bundle
+
+syntax:
+ @for f in $(SH); do bash -n "$$f" || exit 1; done
+ python3 -m py_compile ui/steamify-ui patches/*.py
+
+lint:
+ @command -v shellcheck >/dev/null && shellcheck -S warning $(SH) || echo "shellcheck not installed: skipped"
+
+test:
+ @for t in $(TESTS); do echo "== $$t"; REPO="$(CURDIR)" bash "$$t" || exit 1; done
+
+bundle:
+ .github/tools/bundle.sh dist/steamify.sh
+
+fmt-check:
+ @command -v shfmt >/dev/null && shfmt -d -i 4 -ci $(SH) || echo "shfmt not installed: skipped"
diff --git a/lib/bios.sh b/lib/bios.sh
index ce91d84..e9090f3 100644
--- a/lib/bios.sh
+++ b/lib/bios.sh
@@ -30,16 +30,16 @@ bios_lookup_newest() {
[[ -n "${BIOS_LOOKED_UP:-}" ]] && return 0
BIOS_LOOKED_UP=1; BIOS_NEWEST=""
local tmp desc
- BIOS_REPO="$(curl -fsL --max-time 10 "$VALVE_MIRROR/" | grep -oE "$BIOS_REPO_PREFIX-[0-9]+\.[0-9]+/" | tr -d / | sort -uV | tail -n 1)"
+ BIOS_REPO="$(valve_newest_repo "$BIOS_REPO_PREFIX" 10)"
[[ -n "$BIOS_REPO" ]] || return 1
tmp="$(mktemp -d)"
- if curl -fsL --max-time 30 "$VALVE_MIRROR/$BIOS_REPO/os/x86_64/$BIOS_REPO.files" -o "$tmp/files" &&
- curl -fsL --max-time 30 "$VALVE_MIRROR/$BIOS_REPO/os/x86_64/$BIOS_REPO.db" -o "$tmp/db"; then
+ if curl -fsL --max-time 30 "$(valve_repo_url "$BIOS_REPO")/$BIOS_REPO.files" -o "$tmp/files" &&
+ curl -fsL --max-time 30 "$(valve_repo_url "$BIOS_REPO")/$BIOS_REPO.db" -o "$tmp/db"; then
desc="$(tar -tf "$tmp/files" 2>/dev/null | grep -E '^fremont-hw-support-[0-9][^/]*/files$' | head -n 1)"
[[ -n "$desc" ]] && BIOS_CAB="$(tar -xOf "$tmp/files" "$desc" | grep -E '^usr/share/fwupd/.*\.cab$' | head -n 1)"
desc="${desc%/files}/desc"
- BIOS_PKG="$(tar -xOf "$tmp/db" "$desc" 2>/dev/null | awk '/^%FILENAME%$/ { getline; print }')"
- BIOS_SHA256="$(tar -xOf "$tmp/db" "$desc" 2>/dev/null | awk '/^%SHA256SUM%$/ { getline; print }')"
+ BIOS_PKG="$(valve_desc_field "$tmp/db" "$desc" FILENAME)"
+ BIOS_SHA256="$(valve_desc_field "$tmp/db" "$desc" SHA256SUM)"
[[ -n "${BIOS_CAB:-}" && -n "$BIOS_PKG" && -n "$BIOS_SHA256" ]] && BIOS_NEWEST="$(basename "$BIOS_CAB" .cab)"
fi
rm -rf "$tmp"
@@ -143,7 +143,7 @@ bios_prepare() {
{ err "Installing fwupd failed."; return 1; }
rm -rf "$BIOS_STAGE_DIR"; mkdir -p "$BIOS_STAGE_DIR"; chmod 700 "$BIOS_STAGE_DIR"
info "Downloading $BIOS_PKG ($BIOS_REPO)..."
- if ! curl -fsSL "$VALVE_MIRROR/$BIOS_REPO/os/x86_64/$BIOS_PKG" -o "$BIOS_STAGE_DIR/pkg.tar.zst" ||
+ if ! curl -fsSL "$(valve_repo_url "$BIOS_REPO")/$BIOS_PKG" -o "$BIOS_STAGE_DIR/pkg.tar.zst" ||
! echo "$BIOS_SHA256 $BIOS_STAGE_DIR/pkg.tar.zst" | sha256sum -c --quiet - ||
! tar -I unzstd -xf "$BIOS_STAGE_DIR/pkg.tar.zst" -C "$BIOS_STAGE_DIR" "$BIOS_CAB"; then
err "Downloading or verifying $BIOS_PKG failed; the BIOS was not touched."
diff --git a/lib/cec.sh b/lib/cec.sh
index 0171646..41ecd26 100644
--- a/lib/cec.sh
+++ b/lib/cec.sh
@@ -113,24 +113,8 @@ cec_repair() { cec_installed && ! cec_status; }
fetch_holo_pkg() {
# fetch_holo_pkg
: download the newest from
- # Valve's newest holo repository into and print its path. The
- # SHA-256 comes from Valve's package index and is verified.
- local dir="$1" name="$2" repo desc file_name sha256
- repo="$(curl -fsL "$VALVE_MIRROR/" | grep -oE 'holo-[0-9]+\.[0-9]+/' | tr -d / | sort -V | tail -n 1)"
- if [[ ! -f "$dir/holo.db" ]]; then
- [[ -n "$repo" ]] && curl -fsL "$VALVE_MIRROR/$repo/os/x86_64/$repo.db" -o "$dir/holo.db" ||
- { err "Couldn't read Valve's SteamOS package index ($VALVE_MIRROR)."; return 1; }
- fi
- desc="$(tar -tf "$dir/holo.db" 2>/dev/null | grep -E "^$name-[0-9][^/]*/desc$" | head -n 1)"
- [[ -n "$desc" ]] || { err "No $name in Valve's $repo repository."; return 1; }
- file_name="$(tar -xOf "$dir/holo.db" "$desc" | awk '/^%FILENAME%$/ { getline; print }')"
- sha256="$(tar -xOf "$dir/holo.db" "$desc" | awk '/^%SHA256SUM%$/ { getline; print }')"
- info "Downloading $file_name ($repo)..." >&2
- curl -fsL "$VALVE_MIRROR/$repo/os/x86_64/$file_name" -o "$dir/$file_name" ||
- { err "Downloading $file_name failed."; return 1; }
- echo "$sha256 $dir/$file_name" | sha256sum -c --quiet - >&2 ||
- { err "Checksum mismatch for $file_name; not using it."; return 1; }
- echo "$dir/$file_name"
+ # Valve's newest holo repository into and print its path.
+ valve_fetch_pkg holo "$2" "$1"
}
cec_enable() {
diff --git a/lib/login-manager.sh b/lib/login-manager.sh
index 7cef57f..6e8cc5b 100644
--- a/lib/login-manager.sh
+++ b/lib/login-manager.sh
@@ -35,7 +35,7 @@ steamos_release() {
# capitals: steamos-3.9). Offline (e.g. in the installer): the one set
# before, if any.
local v
- v="$(curl -fsL --max-time 15 "$VALVE_MIRROR/" 2>/dev/null | grep -oE 'jupiter-[0-9]+\.[0-9]+/' | tr -d / | sort -V | tail -n 1)"
+ v="$(valve_newest_repo jupiter 15)"
if [[ -n "$v" ]]; then echo "steamos-${v#jupiter-}"
else sed -n 's/^VERSION_ID=\(steamos-.*\)$/\1/p' /etc/os-release 2>/dev/null; fi
}
diff --git a/lib/steamos-extras.sh b/lib/steamos-extras.sh
index ca40c3e..c7bba9b 100644
--- a/lib/steamos-extras.sh
+++ b/lib/steamos-extras.sh
@@ -8,7 +8,6 @@
# package-owned); per-user settings go through the undo journal.
# Sourced by steamify.sh; not meant to be run on its own.
-VALVE_MIRROR="https://steamdeck-packages.steamos.cloud/archlinux-mirror"
NESTED_DIR=/usr/local/share/applications/steam/holo-nested-desktop
EXTRAS_FILES=(
/usr/local/bin/holo-add-to-steam
@@ -23,25 +22,12 @@ STEAM_KEYBOARD_RULE=cachyos-gamescope-boot-steam-keyboard
fetch_valve_presets() {
# fetch_valve_presets : download and extract the newest
- # steamdeck-kde-presets into . The newest SteamOS release repo
- # (jupiter-3.9, jupiter-3.10, ...) is found on the mirror; its pacman
- # database gives the exact file name and SHA-256, which is verified.
- local dir="$1" repo desc file_name sha256
- repo="$(curl -fsL "$VALVE_MIRROR/" | grep -oE 'jupiter-[0-9]+\.[0-9]+/' | tr -d / | sort -V | tail -n 1)"
- [[ -n "$repo" ]] && curl -fsL "$VALVE_MIRROR/$repo/os/x86_64/$repo.db" -o "$dir/repo.db" ||
- { err "Couldn't read Valve's SteamOS package index ($VALVE_MIRROR)."; return 1; }
- desc="$(tar -tf "$dir/repo.db" 2>/dev/null | grep -E '^steamdeck-kde-presets-[0-9][^/]*/desc$' | head -n 1)"
- [[ -n "$desc" ]] || { err "No steamdeck-kde-presets in Valve's $repo repository."; return 1; }
- file_name="$(tar -xOf "$dir/repo.db" "$desc" | awk '/^%FILENAME%$/ { getline; print }')"
- sha256="$(tar -xOf "$dir/repo.db" "$desc" | awk '/^%SHA256SUM%$/ { getline; print }')"
-
- info "Downloading $file_name ($repo)..."
- curl -fsL "$VALVE_MIRROR/$repo/os/x86_64/$file_name" -o "$dir/presets.pkg.tar.zst" ||
- { err "Downloading $file_name failed."; return 1; }
- echo "$sha256 $dir/presets.pkg.tar.zst" | sha256sum -c --quiet - ||
- { err "Checksum mismatch for $file_name; not using it."; return 1; }
- tar -I unzstd -xf "$dir/presets.pkg.tar.zst" -C "$dir" usr/bin usr/share ||
- { err "Extracting $file_name failed."; return 1; }
+ # steamdeck-kde-presets (from the newest SteamOS release repo, jupiter-3.9,
+ # jupiter-3.10, ...) into .
+ local dir="$1" pkg
+ pkg="$(valve_fetch_pkg jupiter steamdeck-kde-presets "$dir")" || return 1
+ tar -I unzstd -xf "$pkg" -C "$dir" usr/bin usr/share ||
+ { err "Extracting $(basename "$pkg") failed."; return 1; }
}
extras_enable() {
diff --git a/lib/valve-mirror.sh b/lib/valve-mirror.sh
new file mode 100644
index 0000000..c3f3dbf
--- /dev/null
+++ b/lib/valve-mirror.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+# Valve's SteamOS package mirror: finding its newest repository and fetching a
+# verified package from it. Used by the SteamOS extras, the Steam Machine CEC
+# driver, the BIOS update and the os-release version.
+# Sourced by steamify.sh; not meant to be run on its own.
+
+VALVE_MIRROR="https://steamdeck-packages.steamos.cloud/archlinux-mirror"
+
+valve_newest_repo() {
+ # valve_newest_repo [max-seconds]: the newest -X.Y repo
+ # (jupiter, holo), compared as versions: 3.10 is newer than 3.9. Prints
+ # nothing when the mirror can't be reached.
+ local -a limit=()
+ [[ -n "${2:-}" ]] && limit=(--max-time "$2")
+ curl -fsL "${limit[@]}" "$VALVE_MIRROR/" 2>/dev/null | grep -oE "$1-[0-9]+\.[0-9]+/" | tr -d / | sort -uV | tail -n 1
+}
+
+valve_repo_url() { echo "$VALVE_MIRROR/$1/os/x86_64"; }
+
+valve_desc_field() {
+ # valve_desc_field : one value from a package's
+ # entry in a pacman database (FILENAME, SHA256SUM).
+ tar -xOf "$1" "$2" 2>/dev/null | awk -v f="%$3%" '$0 == f { getline; print }'
+}
+
+valve_fetch_pkg() {
+ # valve_fetch_pkg : download the newest
+ # from the newest repository into and print its
+ # path. The file name and SHA-256 come from the repository's database and
+ # the checksum is verified; the database is kept in for more calls.
+ local prefix="$1" name="$2" dir="$3" repo db desc file_name sha256
+ repo="$(valve_newest_repo "$prefix")"
+ db="$dir/$prefix.db"
+ if [[ ! -f "$db" ]]; then
+ [[ -n "$repo" ]] && curl -fsL "$(valve_repo_url "$repo")/$repo.db" -o "$db" ||
+ { err "Couldn't read Valve's SteamOS package index ($VALVE_MIRROR)."; return 1; }
+ fi
+ desc="$(tar -tf "$db" 2>/dev/null | grep -E "^$name-[0-9][^/]*/desc$" | head -n 1)"
+ [[ -n "$desc" ]] || { err "No $name in Valve's $repo repository."; return 1; }
+ file_name="$(valve_desc_field "$db" "$desc" FILENAME)"
+ sha256="$(valve_desc_field "$db" "$desc" SHA256SUM)"
+
+ info "Downloading $file_name ($repo)..." >&2
+ curl -fsL "$(valve_repo_url "$repo")/$file_name" -o "$dir/$file_name" ||
+ { err "Downloading $file_name failed."; return 1; }
+ echo "$sha256 $dir/$file_name" | sha256sum -c --quiet - >&2 ||
+ { err "Checksum mismatch for $file_name; not using it."; return 1; }
+ echo "$dir/$file_name"
+}
diff --git a/steamify.sh b/steamify.sh
index dcfa49a..bcebe23 100755
--- a/steamify.sh
+++ b/steamify.sh
@@ -19,7 +19,7 @@ VERSION=2.11.1
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-for lib in common state packages login-manager single-user steam-desktop steam-machine fremont-poweroff vram-booster hdmi-refresh nvidia controllers cec boot-session vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut steam-game update-notifier first-login menu backend; do
+for lib in common valve-mirror state packages login-manager single-user steam-desktop steam-machine fremont-poweroff vram-booster hdmi-refresh nvidia controllers cec boot-session vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut steam-game update-notifier first-login menu backend; do
# shellcheck source=/dev/null
source "$SCRIPT_DIR/lib/$lib.sh"
done