22 Commits
Author SHA1 Message Date
theupriser 1a8c75721a Merge pull request #25 from theupriser/feature/silent-steam-start
Sync git.upriser.nl mirror / sync (push) Skipped
feat: Steamify page: Start Steam silently, the icons under the conversion; steamify.nodownload
2026-10-02 14:11:20 +02:00
theupriser d70b238a06 docs: TODO: steamify.nodownload keeps the ISO's own Steamify (live session and first login)
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 14:06:44 +02:00
theupriser 0756626700 feat: steamify-install passes steamify.nodownload on to the installed system (its first login keeps the ISO's Steamify)
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 11:11:20 +02:00
theupriser 564f2a8dcd feat: Steamify page: the silent option is called "in desktop mode" in the fallback rows, and is no longer tied to Boot into
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 09:10:28 +02:00
theupriser 084b68d109 feat: steamify.nodownload (kernel command line) or STEAMIFY_NO_DOWNLOAD keeps the ISO's own Steamify instead of the newest release
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 08:33:43 +02:00
theupriser 7ee32f926b feat: Steamify page: Start Steam silently option (needs/hideWhen rules from --defaults --list)
Sync git.upriser.nl mirror / sync (push) Skipped
2026-10-02 07:37:13 +02:00
theupriser 46fa044ffb Merge pull request #24 from theupriser/docs/iso-todo-2
Sync git.upriser.nl mirror / sync (push) Skipped
docs: TODO: tick off what is done, say what is left
2026-10-01 15:36:08 +02:00
theupriser 05d2e2632d docs: remove the hand-written Steamify section of CHANGELOG.md (the release notes are generated), TODO updated
Sync git.upriser.nl mirror / sync (push) Skipped
2026-09-30 13:58:19 +02:00
theupriser 556f962a6f docs: TODO: the same-day replacement of a real release is proven by the official 2.9.6 build
Sync git.upriser.nl mirror / sync (push) Skipped
2026-09-30 13:56:56 +02:00
theupriser 8b1241af9d docs: TODO: retention code and the release notes filter done, names and badge items say what was already seen 2026-09-30 13:53:43 +02:00
theupriser 5a0ca99fa7 Merge pull request #23 from theupriser/docs/iso-todo
Sync git.upriser.nl mirror / sync (push) Skipped
docs: TODO as a checkbox list, up to date
2026-09-30 13:52:04 +02:00
theupriser d5c61d578f docs: TODO as a checkbox list, with the release pipeline work and what is still open
Sync git.upriser.nl mirror / sync (push) Skipped
2026-09-30 13:48:45 +02:00
theupriser d2a35807de docs: TODO: the ISO's name by the day, steamify_ref for unreleased Steamify, the build runs on the Gitea runner 2026-09-30 13:47:05 +02:00
theupriser 40e76cf6f3 Merge pull request #22 from theupriser/bugfix/iso-retention-mirror
Sync git.upriser.nl mirror / sync (push) Skipped
fix(ci): retention also frees the mirror (delete its release with the ISO)
2026-09-30 13:40:52 +02:00
theupriser d6b69d1026 fix(ci): retention deletes the mirror's release (and its ISO) too, not only the GitHub release 2026-09-30 13:39:16 +02:00
theupriser ec174cc7b7 Merge pull request #21 from theupriser/feature/iso-release-notes
Sync git.upriser.nl mirror / sync (push) Skipped
Feature/iso release notes
2026-09-30 13:33:50 +02:00
theupriser 66d4b6c91d feat(ci): release title CachyOS <base> with Steamify <version> on GitHub and on the mirror
Sync git.upriser.nl mirror / sync (push) Skipped
2026-09-30 13:33:03 +02:00
theupriser 63b2bb96a0 fix(ci): the badge links to the public Gitea address (the runner's own server URL is http://server:3000)
Sync git.upriser.nl mirror / sync (push) Skipped
2026-09-30 13:30:36 +02:00
theupriser e430e0bb01 Merge pull request #20 from theupriser/feature/iso-release-notes
Sync git.upriser.nl mirror / sync (push) Skipped
feat(ci): generated release notes (Steamify changelog and commits since the last release)
2026-09-30 13:30:21 +02:00
theupriser dac15ff9e4 feat(ci): release notes: wording when there is no earlier release to compare with
Sync git.upriser.nl mirror / sync (push) Skipped
2026-09-30 13:23:02 +02:00
theupriser 71d429915e feat(ci): release notes: clearer wording, workflow (ci) and docs commits left out of the ISO's change list, checksum section
Sync git.upriser.nl mirror / sync (push) Skipped
2026-09-30 13:22:47 +02:00
theupriser acde716b86 feat(ci): release notes are generated: the base version, Steamify's changelog section and the commits since the previous release; the mirror's release uses the same text 2026-09-30 13:20:22 +02:00
9 changed files with 156 additions and 147 deletions

No files matched your search

+43 -10
View File
@@ -38,6 +38,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Tag and release
env:
@@ -53,29 +55,53 @@ jobs:
[ -n "$steamify" ] || { echo "::error::no VERSION in steamify.sh at $sref"; exit 1; }
fi
stamp="$(date -u +%y%m%d)" day="$(date -u +%Y.%m.%d)"
base="$(head -n 1 CHANGELOG.md | sed 's/^# *//')" # the CachyOS version (first line of the changelog)
kind="${{ inputs.kind }}"
if [ -z "$kind" ] || [ "$kind" = auto ]; then
if [ "${{ github.ref }}" = refs/heads/master ]; then kind=release; else kind=dev; fi
fi
if [ "$kind" = release ]; then
tag="v$steamify-$stamp" pre="" name="CachyOS with Steamify Live ISO $steamify ($day UTC)"
tag="v$steamify-$stamp" pre="" name="CachyOS $base with Steamify $steamify ($day UTC)"
else
tag="v$steamify-dev-$stamp" pre="--prerelease" name="CachyOS with Steamify Live ISO $steamify (test build $day UTC)"
tag="v$steamify-dev-$stamp" pre="--prerelease" name="CachyOS $base with Steamify $steamify (test build $day UTC)"
fi
# The ISO's file on the mirror is named after the tag (iso-2-gitea-build.yml, profiledef.sh), so its
# download link is known now.
file="steamify-cachyos-${tag#v}-x86_64.iso" dl="$GITEA_URL/$GITEA_REPO/releases/download/$tag"
# The Steamify section of CHANGELOG.md ("## CachyOS with Steamify Live ISO"), without its heading.
section="$(awk '/^## CachyOS with Steamify Live ISO/ {found = 1; next} found && /^#/ {exit} found' CHANGELOG.md)"
# The notes are generated, nothing is written by hand:
# - the CachyOS base version (first line of CHANGELOG.md, the fork's upstream changelog),
# - the Steamify version's own changelog section (steamify-cachyos CHANGELOG.md at the ref or tag it is built from),
# - this repository's commits since the previous release of the same kind.
steamify_notes="$(gh api "repos/theupriser/steamify-cachyos/contents/CHANGELOG.md?ref=${sref:-v$steamify}" -q .content 2> /dev/null | base64 -d 2> /dev/null |
awk -v v="$steamify" 'index($0, "## " v " ") == 1 {f = 1; next} f && /^## / {exit} f')"
[ "$kind" = release ] && want_pre=false || want_pre=true
[ "$kind" = release ] && kind_name="real release" || kind_name="dev build"
prev="$(gh release list -L 50 --json tagName,isPrerelease,createdAt -q "[.[] | select(.isPrerelease == $want_pre and .tagName != \"$tag\")] | sort_by(.createdAt) | reverse | .[0].tagName // empty")"
# Commits that change the ISO itself: not the workflow ones (type or scope ci, e.g. ci: / fix(ci):) and not docs:.
if [ -n "$prev" ] && git rev-parse -q --verify "refs/tags/$prev" > /dev/null; then
range="$prev..HEAD"; limit=40; since="Since \`$prev\`:"
else
range="HEAD"; limit=12; since="The latest commits (there is no earlier $kind_name to compare with):"
fi
changes="$(git log --no-merges --format='%s' "$range" | grep -Ev '^(ci|docs)(\(.*\))?:|^[a-z]+\(ci\):' | sed 's/^/- /' | head -n "$limit" || true)"
[ -n "$changes" ] || changes="- No changes to the ISO itself."
notes="![ISO build](https://img.shields.io/badge/ISO_build-running-yellow)
The ISO is built from \`$(git rev-parse --short HEAD)\` (${{ github.ref_name }}) with Steamify $steamify${sref:+ (branch $sref)} and published on the mirror (GitHub releases take at most 2 GB per file), about half an hour after this release (the mirror's sync, then the build):
**CachyOS $base with Steamify $steamify${sref:+ (branch $sref)} built in.** Install CachyOS and turn the PC into a Steam Machine in one go.
The ISO is built from \`$(git rev-parse --short HEAD)\` (${{ github.ref_name }}). It is about 3 GB, more than a GitHub release can hold (2 GB per file), so it is published on the mirror about 20 minutes after this release is created:
**Download: [$file]($dl/$file)** ([SHA-256]($dl/$file.sha256), [release page]($GITEA_URL/$GITEA_REPO/releases/tag/$tag))
Sources: [Steamify](https://github.com/theupriser/steamify-cachyos/tree/${sref:-v$steamify}), this repository at this tag, and the packages' sources at [CachyOS](https://github.com/CachyOS) and [Arch Linux](https://archlinux.org/packages/).
### Steamify $steamify
${steamify_notes:-There is no changelog section for this version.}
$section"
### Changes in this ISO
$since
$changes
### Sources
[Steamify](https://github.com/theupriser/steamify-cachyos/tree/${sref:-v$steamify}), this repository at this tag, and the packages' sources at [CachyOS](https://github.com/CachyOS) and [Arch Linux](https://archlinux.org/packages/)."
# One ISO per day and kind: the newest build of a day replaces the earlier one (space), here and on the mirror.
if git ls-remote --exit-code --tags origin "refs/tags/$tag" > /dev/null 2>&1 || gh release view "$tag" > /dev/null 2>&1; then
echo "$tag exists: replacing it"
@@ -92,13 +118,20 @@ jobs:
echo "ISO_TAG=$tag" >> "$GITHUB_ENV"
echo "STEAMIFY_REF=$sref" >> "$GITHUB_ENV"
# Retention (space): at most 10 dev and 10 real releases; the oldest go, release and tag. The mirror drops the
# tag with its release and ISO at its next sync (the step below).
# Retention (space): at most 10 dev and 10 real releases; the oldest go: the mirror's release with its ISO, then the GitHub
# release and tag (the mirror drops the tag at its next sync, the step below).
keep=10
for pre_flag in true false; do
gh release list -L 200 --json tagName,isPrerelease,createdAt \
-q "[.[] | select(.isPrerelease == $pre_flag)] | sort_by(.createdAt) | reverse | .[$keep:][] | .tagName" |
while read -r old; do echo "Retention: removing $old"; gh release delete "$old" --cleanup-tag -y; done
while read -r old; do
echo "Retention: removing $old"
# The mirror keeps a release (and its 3 GB ISO) after its tag is gone, so it is deleted there first.
if [ -n "$GIT_UPRISER_TOKEN" ]; then
curl -sS -X DELETE -H "Authorization: token $GIT_UPRISER_TOKEN" -o /dev/null -w " mirror release $old: HTTP %{http_code}\n" "$GITEA_URL/api/v1/repos/$GITEA_REPO/releases/tags/$old" || true
fi
gh release delete "$old" --cleanup-tag -y
done
done
# The mirror takes the tag (sync), then the build on the mirror is started explicitly (its dispatch API): a tag that
+19 -11
View File
@@ -159,10 +159,11 @@ jobs:
[ -f "$iso" ] || { echo "::error::no ISO in out/desktop"; exit 1; }
tag="${{ github.ref_name }}" b="$STEAMIFY_BUILD_STAMP"
stamp="20${b:0:2}.${b:2:2}.${b:4:2}"
base="$(head -n 1 CHANGELOG.md | sed 's/^# *//')" # the CachyOS version, as in the GitHub release's title
if [ "$ISO_PRERELEASE" = true ]; then
name="CachyOS with Steamify Live ISO $STEAMIFY_VERSION (test build $stamp UTC)"
name="CachyOS $base with Steamify $STEAMIFY_VERSION (test build $stamp UTC)"
else
name="CachyOS with Steamify Live ISO $STEAMIFY_VERSION ($stamp UTC)"
name="CachyOS $base with Steamify $STEAMIFY_VERSION ($stamp UTC)"
fi
# One ISO per day and kind: a release of this tag from an earlier build of the day is replaced.
api="${{ github.server_url }}/api/v1/repos/${{ github.repository }}"
@@ -173,15 +174,18 @@ jobs:
{ echo "publish=true"; echo "name=$name"; } >> "$GITHUB_OUTPUT"
section="$(awk '/^## CachyOS with Steamify Live ISO/ {found = 1; next} found && /^#/ {exit} found' CHANGELOG.md)"
src="${{ inputs.steamify_ref }}"; src="${src:-v$STEAMIFY_VERSION}"
# The same notes as the GitHub release (one source: iso-1 generated them), without its badge line; the
# checksum line is added here. Fallback: a short text.
if curl -fsS "https://api.github.com/repos/theupriser/steamify-cachyos-live-iso/releases/tags/$tag" -o /tmp/ghrel.json; then
jq -r '.body // ""' /tmp/ghrel.json | sed '/ISO build\]/d' > release-notes.md
else
echo "Built from \`$(git rev-parse --short HEAD)\` ($tag) with Steamify $STEAMIFY_VERSION." > release-notes.md # fallback when GitHub cannot be reached
fi
{
echo "Built from \`$(git rev-parse --short HEAD)\` ($tag) with Steamify $STEAMIFY_VERSION."
echo
echo "SHA-256: \`$(cut -d' ' -f1 "$iso.sha256")\`, label \`STEAMIFY_${STEAMIFY_VERSION//./_}_$b\`"
echo
echo "Sources: [Steamify](https://github.com/theupriser/steamify-cachyos/tree/$src), this repository at this tag, and the packages' sources at [CachyOS](https://github.com/CachyOS) and [Arch Linux](https://archlinux.org/packages/)."
echo
echo "$section"
} > release-notes.md
echo "### Checksum"
echo "SHA-256 of the ISO: \`$(cut -d' ' -f1 "$iso.sha256")\` (label \`STEAMIFY_${STEAMIFY_VERSION//./_}_$b\`)."
} >> release-notes.md
cat release-notes.md
- name: Publish ${{ github.ref_name }}
@@ -225,11 +229,15 @@ jobs:
run="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
api="https://api.github.com/repos/theupriser/steamify-cachyos-live-iso/releases"
curl -fsS -H "Authorization: Bearer $GH_TOKEN" "$api/tags/$tag" -o /tmp/rel.json || { echo "::warning::no GitHub release for $tag"; exit 0; }
ST="$st" COL="$col" RUN="$run" python3 - << 'PY'
ST="$st" COL="$col" RUN="$run" RUN_ID="${{ github.run_id }}" python3 - << 'PY'
import json, os, re
d = json.load(open("/tmp/rel.json"))
badge = f"[![ISO build](https://img.shields.io/badge/ISO_build-{os.environ['ST']}-{os.environ['COL']})]({os.environ['RUN']})"
body = d.get("body") or ""
# This runner reports its internal address (http://server:3000) as the server URL: the public one is in the
# release page link that iso-1 wrote into the notes.
m = re.search(r"\]\((https?://[^)\s]+)/releases/tag/", body)
run = f"{m.group(1)}/actions/runs/{os.environ['RUN_ID']}" if m else os.environ["RUN"]
badge = f"[![ISO build](https://img.shields.io/badge/ISO_build-{os.environ['ST']}-{os.environ['COL']})]({run})"
pat = re.compile(r"\[?!\[ISO build\]\([^)]*\)(\]\([^)]*\))?")
body = pat.sub(lambda m: badge, body, count=1) if pat.search(body) else badge + "\n\n" + body
json.dump({"body": body}, open("/tmp/patch.json", "w"))
+8 -1
View File
@@ -119,7 +119,7 @@ Full write-up, with every trap: `steamify-iso-release` in steamify-cachyos-dev.
date, no time): `vX.Y.Z-YYMMDD` (real) or `vX.Y.Z-dev-YYMMDD` (dev), file `steamify-cachyos-X.Y.Z-[dev-]YYMMDD-x86_64.iso`, an **annotated** tag and a
release with notes and the direct download link. One ISO per day and kind: a second build the same day deletes
the earlier release and tag (GitHub) and tags again; the mirror takes the new tag object, iso-2 replaces the old mirror release. Retention: at most 10 dev and 10 real
releases are kept (space); the oldest are deleted with `gh release delete --cleanup-tag`, and the mirror drops their tags, releases and ISOs at its next sync. The workflow always runs from `master`; its input `steamify_ref` (a Steamify branch or tag, empty = the newest published release) builds the ISO with that checkout and names it after that branch's `VERSION` (a release branch tests its own version before it is released); its input `kind` (`release`, `dev`, or `auto`: release on master, dev on other branches) decides: `release` is a real release, `dev` a `dev-` pre-release. steamify-cachyos' bundle.yml passes it: a version published from main -> `release`, a version on a release branch -> `dev`.
releases are kept (space); the oldest are deleted: first the mirror's release with its ISO (Gitea API, `GIT_UPRISER_TOKEN`), then the GitHub release and tag with `gh release delete --cleanup-tag`; the mirror drops the tag at its next sync. (A mirror release is NOT removed by a tag deletion alone: it stays, tagless, with its ISO.) The workflow always runs from `master`; its input `steamify_ref` (a Steamify branch or tag, empty = the newest published release) builds the ISO with that checkout and names it after that branch's `VERSION` (a release branch tests its own version before it is released); its input `kind` (`release`, `dev`, or `auto`: release on master, dev on other branches) decides: `release` is a real release, `dev` a `dev-` pre-release. steamify-cachyos' bundle.yml passes it: a version published from main -> `release`, a version on a release branch -> `dev`.
- `.github/workflows/iso-2-gitea-build.yml` (Gitea, `workflow_dispatch` with the tag as the ref, started by iso-1's last step through the mirror's dispatch API after the mirror has the new tag object; a tag push starts nothing, because a replaced tag of the same name starts no run; skipped on GitHub) runs two jobs for that
tag: build the ISO, then attach it to the mirror's release. It is not tested there (the runner is small): the VM tests
run on GitHub, in steamify-cachyos `vmtest.yml`, on the newest ISO release of the mirror. The tag decides everything: Steamify version (`steamify-prepare.sh`
@@ -139,3 +139,10 @@ Full write-up, with every trap: `steamify-iso-release` in steamify-cachyos-dev.
iso-2's last job `report` (`if: always()`) replaces it with succeeded / failed / cancelled, linked to the Gitea run,
through the GitHub API with the Gitea secret `GH_RELEASE_TOKEN` (fine-grained, this repo, Contents read and write).
Without the secret the badge stays "running". Gitea's own badge can't show these runs (they run on a tag ref).
- **Release notes are generated, not written by hand** (iso-1): the CachyOS base version (first line of CHANGELOG.md), the
Steamify version's own changelog section (steamify-cachyos CHANGELOG.md at the ref or tag it is built from) and this
repository's commits since the previous release of the same kind (newest 40). iso-2 takes the same text from the GitHub
release for the mirror's release (one source), without the badge line, and adds the checksum line. The old hand-written
"## CachyOS with Steamify Live ISO" section in CHANGELOG.md is no longer used.
- **Release title:** `CachyOS <base> with Steamify <version> (<YYYY.MM.DD> UTC)` (dev: `(test build <date> UTC)`), the same on GitHub
and on the mirror; `<base>` is the first line of CHANGELOG.md (e.g. 26.08).
-40
View File
@@ -26,46 +26,6 @@
* Correctly handle VM environments
* Correctly quote pacman arguments
## CachyOS with Steamify Live ISO
CachyOS 26.08 with Steamify built in: install CachyOS and turn the PC into a Steam Machine in one go.
Its version is the Steamify version on it plus the build date and time (UTC, when the release starts on GitHub; a release on `master` is `v<Steamify version>-<date>-<HHMM>`, e.g. `v2.9.3-2026.09.29-2015`). A new Steamify release starts one (a test build for now); by hand: Run workflow on "ISO 1/2 · Tag and release (GitHub)".
Releases: GitHub creates the tag and a release with the notes; the ISO itself (over GitHub's 2 GB limit) is built on the Gitea mirror and attached to its release for that tag (git.upriser.nl).
**Features:**
* **ISO:** named after its release tag (`steamify-cachyos-2.9.3-2026.09.29-2030-x86_64.iso`; built by hand, without a tag: `local`), labelled after the Steamify version on it and the build time (`STEAMIFY_2_9_1_20260929_1432`, was CachyOS's `COS_<year><month>`); CachyOS Hello installs it like CachyOS's own
release (the ISO's own release, its tag without the v, is in `/etc/steammachine-iso-build` and the boot menu)
* **Installer (CachyOS - Steamify Installer):**
* A Steamify page after the packages page: which Steam Machine parts to set up (rows from
`steamify.sh --defaults --list`, the newest Steamify fetched before Calamares starts) and where
the PC starts (gaming mode or the desktop)
* The Summary lists every chosen item and where the PC starts
* The new user's Steam Machine setup runs during the install (`steamify-install`, a Calamares step
before cleanup): the first boot is already a Steam Machine
* Only what Steamify supports is offered: KDE Plasma, and the boot loaders Limine, systemd-boot and
GRUB (not rEFInd)
* **Live session:** keeps CachyOS's own name; the power-off fix is built for the ISO's kernels, and
the login has the Vapor look
**Fixes:**
* **systemd-boot:** the installed system gets its "Linux Boot Manager" EFI boot entry, first in the
boot order. cachyos-installer runs `bootctl install` in a chroot, where bootctl leaves the EFI
variables alone, so the entry was missing: the PC could only boot through the disk's fallback path
(`\EFI\BOOT\BOOTX64.EFI`). Most PCs find it early and boot normally, but firmware that tries the
network first waits minutes on every boot (QEMU/OVMF: PXE, HTTP boot and the EFI shell come first),
and anything else that writes the fallback file (another OS) takes the boot over.
`steamify-install` now registers it with `efibootmgr` from the live system, with the ESP's real
disk and partition (bootctl in the chroot writes an entry without a partition, which the firmware
can't load); the output is in `/var/log/steamify-bootentry.log`
* **Installer:** Steamify's install-time logs (`/var/log/steamify-install.log`,
`steamify-bootentry.log`) are readable after the first boot: `/var/log` is its own btrfs subvolume
(`@log`) that wasn't mounted yet when Steamify ran, so they ended up hidden under it
* **Installer:** Steamify's setup never asks for a password (its sudo rule is read after CachyOS's
`%wheel` rule); Calamares starts with the Boost version it was linked against
* **Build:** finishes cleanly as root and reports no error after a successful build; the Steamify page's
Calamares module is built with the ISO and left out when Calamares' version differs
# 26.07
**Features:**
+68 -79
View File
@@ -1,89 +1,78 @@
# To do (Steam Machine edition)
Tick items off as they are done; the newest entries are at the top of each list.
## Open
1. **Steamify installer page** (2026-09-28, working end to end; see
`AGENTS.md` for the module/build/testing details and gotchas behind
this). `packagechooserq` (custom QML) is built with each ISO by
`build-calamares-modules.sh` into `archiso/airootfs/usr/lib/calamares/modules/packagechooserq/`.
`netinstall` (checkbox tree) stays as a manually-swappable fallback
(`steamify-cachyos-dev/share/sn.sh` + `nisteamify.conf`) for when the
module can't load, but isn't wired into `calamares-online.sh`
automatically yet.
- **Still open**:
- A Python job (e.g. `steamifychoice`, before `packages@online`) to
normalize either page's choice (packagechooserq's
`packagechooser_steamifypage` GS key, or netinstall's
packageOperations markers) into one `steamifyChoice` GS key, so
`shellprocess_steamify.conf` doesn't need to know which page ran.
2. **Names**: only the installer says Steamify ("CachyOS with Steamify
Installer", productName set by calamares-online.sh); the live session,
Hello and About this System keep CachyOS's own names (legal clarity). The
ISO file stays `steamify-cachyos-<date>.iso`.
3. **Check the names** after the next build: installer title, Hello and
About this System say CachyOS.
4. ~~Steamify PR for `feat/defaults-options`~~ — done: 2.7.0 (`--options`,
`--boot`, `steamify.sh --boot` standalone) and 2.8.0
(`--defaults --list`, what the Steamify page's rows come from) are both
released. **The ISO builder is up to date with this**: no checkout
argument needed, `steamify-prepare.sh` (no args) and `calamares-online.sh`
at boot both just fetch the newest release, so the ISO always has 2.8.0+
without a rebuild for a Steamify-side change alone. Only pass
`steamify-prepare.sh <checkout>` / `vmisobuild.sh --steamify <path>` when
testing an *unreleased* Steamify change.
5. **Build in the test VM** instead of on the Steam Machine
(steam-machine-iso skill), and install the result unattended with
`scripts/vminstall.sh --iso` (vm-install skill; the Calamares Steamify
step then needs `steamify-install` run from the live script).
6. **Test on the real Steam Machine** from a USB stick (gamescope, LEDs, CEC,
power-off) before calling the ISO usable.
- [ ] **Steamify installer page, one choice key** (2026-09-28; the page works end to end, see `AGENTS.md` for the
module/build/testing details). `packagechooserq` (custom QML) is built with each ISO by
`build-calamares-modules.sh`. `netinstall` (checkbox tree) stays a manually swappable fallback
(`steamify-cachyos-dev/share/sn.sh` + `nisteamify.conf`), not wired into `calamares-online.sh`.
Still to do: a Python job (e.g. `steamifychoice`, before `packages@online`) that normalizes either page's choice
(`packagechooser_steamifypage` GS key, or netinstall's packageOperations markers) into one `steamifyChoice`
GS key, so `shellprocess_steamify.conf` doesn't need to know which page ran.
- [ ] **Names, the live session**: the installed system's names were checked (2026-09-30: `os-release` and `lsb-release` are
CachyOS's, test B3). Still to look at: the installer title, Hello and About this System in the live session (only the
installer says Steamify).
- [ ] **Test on the real Steam Machine** from a USB stick (gamescope, LEDs, CEC, power-off) before calling the ISO usable.
- [ ] **Retention, first clean-up**: the code is merged (PR #22: at most 10 dev + 10 real releases, the mirror's release with
its ISO is deleted first). Watch the first time there are more than 10 releases of a kind; it hasn't run yet.
- [ ] **Build badge, the other states**: succeeded was seen (`v2.9.6-260930`). Failed, cancelled and a build superseded by a
newer one haven't been seen; a crashed runner leaves the badge on "running".
- [ ] **Check with the CachyOS team** whether the installer branding and the ISO file name are fine with them.
- [ ] **Drop the Boost 1.91 workaround** in `steamify-prepare.sh` once `cachyos-calamares-next` is rebuilt against the repos' Boost.
## Done
- **Full ISO install with the Steamify page** (2026-09-28, ISO VM,
`--fremont`, Start in: desktop): Summary listed the choices, the
install step ran Steamify 2.8.0 with every item OK, `--boot desktop`
applied, `exit: 0`; the installed system boots into the desktop.
- **Steamify Summary** (2026-09-28, ISO VM): an unordered list of the chosen
items' names plus "Starts in gaming mode/the desktop"
(`patches/packagechooserq-steamify-summary.patch`), with `items.json`
written by the launcher (its `$sdir` bug fixed, `0cc7367`).
- **packagechooserq built with the ISO** (2026-09-28):
`build-calamares-modules.sh` builds it in the build container/VM from
CachyOS's Calamares source against the current `cachyos-calamares-next`
(no prebuilt `.so` in git), with `built-for`; `calamares-online.sh` leaves
the Steamify page out when the Calamares it runs differs (the step then
installs everything on) instead of risking "Initialization Failed".
- **Steamify 2.6.0** released (`--defaults`, install-time mode).
- **Live session** (VM, `--fremont`): Vapor look and layout (Steam Deck
wallpaper) at the live login; the power-off module is built for both ISO
kernels and loaded at boot, in the VM it returns "No such device" (no
AMDI0030 GPIO controller), as designed. Whether it keeps the real Steam
Machine off is part of the hardware test.
- **Regression test 2.6.0 on an existing desktop install** (test VM from
ssh-ready, `--fremont`): full first run, re-apply, notifications/CEC/theme
off and on again. No errors; user units enabled and running, theme applied
and restored live, no first-login autostart with a session.
- **ISO name**: `steamify-cachyos-<date>-x86_64.iso` (`iso_name` in
`archiso/profiledef.sh`); the volume label stays `COS_<yyyymm>`.
- **VM install from the ISO (2026-09-27, `f42859a`):** a VM install from the ISO (Hello's
Install, no manual fixes) ran Steamify's step: every component OK,
`exit: 0`, SDDM autologin into gamescope. First desktop login (session switched to plasma in the VM) passed too:
Vapor layout, the app opened with everything on. The first-login script
runs the newest *release*, so it only works once 2.6.0 is released (in the
test it was pointed at the bundle).
The VM's text
console doesn't show (virgl): read the installed disk with
`qemu-nbd -r` + `mount -o ro,rescue=nologreplay,subvol=@` (logs in `@log`).
### Release pipeline (2026-09-29 and 2026-09-30)
## Later
- [x] **Daily names like CachyOS**: tag `v<steamify>-<YYMMDD>` (dev: `v<steamify>-dev-<YYMMDD>`), file
`steamify-cachyos-<steamify>-[dev-]<YYMMDD>-x86_64.iso`, label `STEAMIFY_<version>_<YYMMDD>`; one ISO per day and
kind, the newest build replaces the earlier one.
- [x] **Always from `master`**: `iso-1-github-tag.yml` takes `kind` (release, dev, auto); steamify-cachyos' `bundle.yml` asks
for `release` (a version published from main) or `dev` (an unreleased version on a release branch).
- [x] **`steamify_ref`**: a release branch builds its dev ISO with its own Steamify (and VERSION) before it is released.
- [x] **`steamify.nodownload`** (kernel command line) or `STEAMIFY_NO_DOWNLOAD=1`: the live session keeps the ISO's own Steamify
(`calamares-online.sh` would replace it by the newest release), and `steamify-install` passes it on, so the installed system's
first login runs the ISO's bundle too. For testing an unreleased Steamify; tested with Steamify 2.11.0 (three installs).
- [x] **The mirror's build is started by dispatch** after the mirror has the new tag object (a replaced tag starts no run by
itself; the dispatch needs the full ref `refs/tags/<tag>`).
- [x] **Build badge** in the GitHub release: running, then succeeded / failed / cancelled (secret `GH_RELEASE_TOKEN` on the
mirror), linked to the public Gitea run.
- [x] **Generated release notes** and title (`CachyOS <base> with Steamify <version> (<date> UTC)`): base version, Steamify's
changelog section, commits since the previous release; the mirror's release uses the same text.
- [x] **Retention** of at most 10 dev and 10 real releases, deleting the mirror's release with its ISO too (PR #22; first
clean-up still to be seen, above).
- [x] **Old hand-written `CHANGELOG.md` section** (`## CachyOS with Steamify Live ISO`) removed: the release notes are generated.
- [x] **Same-day replacement of a real release** (2026-09-30): the official build for `v2.9.6-260930` replaced the test build's
release on the mirror (ISO asset 42 -> 50, new title and generated notes, badge green with the public run link).
- [x] **Release notes** leave out the workflow and docs commits (`ci`, `docs`, `…(ci):`) in their list of ISO changes.
- [x] **The ISO is built on the Gitea mirror's runner** (`iso-2-gitea-build.yml`) and hosted there (over GitHub's 2 GB limit);
the VM tests run locally (`scripts/vmtest.sh --install` in steamify-cachyos-dev), not on the runners.
- [x] **Build in the test VM instead of on the Steam Machine**: superseded by the runner above.
- **Branding**: only the installer carries Steamify's name; check with the
CachyOS team whether that and the ISO file name are fine with them.
- **Release**: a CI job that builds the ISO (privileged container, flags from
the steam-machine-iso skill) and hosts it (>2 GB, not a GitHub release asset).
- **Drop the Boost 1.91 workaround** in `steamify-prepare.sh` once
`cachyos-calamares-next` is rebuilt against the repos' Boost.
### Installer, live session and Steamify (2026-09-27 to 2026-09-29)
Build and test details: the `steam-machine-iso` skill in steamify-cachyos-dev.
- [x] **Steamify PR `feat/defaults-options`**: 2.7.0 (`--options`, `--boot`) and 2.8.0 (`--defaults --list`) released; the ISO
fetches the newest release, nothing to pass for a Steamify-side change alone.
- [x] **Full ISO install with the Steamify page** (2026-09-28, ISO VM, `--fremont`, Start in: desktop): the Summary listed
the choices, the install step ran Steamify 2.8.0 with every item OK, `--boot desktop` applied, `exit: 0`; the
installed system boots into the desktop.
- [x] **Steamify Summary** (2026-09-28): an unordered list of the chosen items' names plus "Starts in gaming mode/the
desktop" (`patches/packagechooserq-steamify-summary.patch`), with `items.json` written by the launcher.
- [x] **packagechooserq built with the ISO** (2026-09-28): `build-calamares-modules.sh` builds it against the current
`cachyos-calamares-next` (no prebuilt `.so` in git), with `built-for`; `calamares-online.sh` leaves the Steamify
page out when the Calamares it runs differs (the step then installs everything on).
- [x] **Steamify 2.6.0** released (`--defaults`, install-time mode).
- [x] **Live session** (VM, `--fremont`): Vapor look and layout (Steam Deck wallpaper) at the live login; the power-off
module is built for both ISO kernels and loaded at boot (in the VM it returns "No such device": no AMDI0030 GPIO
controller, as designed; whether it keeps the real Steam Machine off is part of the hardware test).
- [x] **Regression test 2.6.0 on an existing desktop install** (test VM from ssh-ready, `--fremont`): full first run,
re-apply, notifications/CEC/theme off and on again; no errors.
- [x] **ISO name** (first version, since superseded by the daily names above): `steamify-cachyos-<date>-x86_64.iso`.
- [x] **VM install from the ISO** (2026-09-27, `f42859a`): ran Steamify's step with every component OK, `exit: 0`, SDDM
autologin into gamescope; the first desktop login passed too (Vapor layout, the app opened with everything on). The
VM's text console doesn't show (virgl): read the installed disk with `qemu-nbd -r` + `mount -o ro,rescue=nologreplay,subvol=@`
(logs in `@log`).
Build and test details: the `steam-machine-iso` skill in steamify-cachyos-dev; the release pipeline: `steamify-iso-release`.
@@ -69,14 +69,18 @@ EOF
# any, so "$sdir" would still be empty in the same one.
local sdir=/usr/local/share/steamify
local sbin="$sdir/steamify.sh" sqml="$sdir/qml"
# STEAMIFY_NO_DOWNLOAD=1 or `steamify.nodownload` on the kernel command line: keep the ISO's own
# copy, e.g. to test an unreleased Steamify (the newest release would replace it).
local nodl=
if [[ -n "${STEAMIFY_NO_DOWNLOAD:-}" ]] || grep -qw steamify.nodownload /proc/cmdline; then nodl=1; fi
local tmp; tmp="$(mktemp)"
if curl -fsSL --max-time 20 https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify.sh -o "$tmp" &&
if [[ -z "$nodl" ]] && curl -fsSL --max-time 20 https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify.sh -o "$tmp" &&
grep -q -- '--defaults' "$tmp"; then
sudo install -Dm755 "$tmp" "$sbin"
fi
rm -f "$tmp"
tmp="$(mktemp -d)"
if curl -fsSL --max-time 20 https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify-app.tar.gz |
if [[ -z "$nodl" ]] && curl -fsSL --max-time 20 https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify-app.tar.gz |
tar -xz -C "$tmp" --strip-components=2 ui/qml/Theme.qml ui/qml/Texts.qml 2>/dev/null; then
sudo cp "$tmp/Theme.qml" "$tmp/Texts.qml" "$sqml/"
fi
@@ -54,8 +54,12 @@ rule="$root/etc/sudoers.d/zz-steamify-install"
install -Dm755 /usr/local/share/steamify/steamify.sh "$root/var/tmp/steamify.sh"
printf '%s ALL=(ALL) NOPASSWD: ALL\n' "$user" > "$rule"
chmod 440 "$rule"
# steamify.nodownload on the live system's kernel command line (or STEAMIFY_NO_DOWNLOAD): the installed system
# keeps this ISO's Steamify at its first login too, instead of the newest release.
nodl=()
if [[ -n "${STEAMIFY_NO_DOWNLOAD:-}" ]] || grep -qw steamify.nodownload /proc/cmdline; then nodl=(STEAMIFY_NO_DOWNLOAD=1); fi
arch-chroot "$root" runuser -u "$user" -- env -i HOME="/home/$user" USER="$user" LOGNAME="$user" \
PATH=/usr/local/bin:/usr/bin TERM=dumb LANG=C.UTF-8 \
PATH=/usr/local/bin:/usr/bin TERM=dumb LANG=C.UTF-8 "${nodl[@]}" \
bash -c 'cd ~ && bash /var/tmp/steamify.sh --defaults --options "$0" --boot "$1" < /dev/null' "$options" "$boot" > "$log" 2>&1
echo "exit: $?" >> "$log"
rm -f "$rule" "$root/var/tmp/steamify.sh"
@@ -8,6 +8,7 @@ QtObject {
readonly property var rows: [
{ id: "gaming", label: "SteamOS conversion", hint: "boot into gaming mode, Steam on the desktop", kind: "toggle", parent: "", on: true, selectable: true },
{ id: "boot", label: "Boot into", hint: "", kind: "choice", parent: "gaming", on: false, selectable: true },
{ id: "silent", label: "Start Steam silently in desktop mode", hint: "in the tray at login on the desktop, no window", kind: "toggle", parent: "gaming", needs: "", on: true, selectable: true },
{ id: "theme", label: "Install SteamOS theme", hint: "Vapor look (cachyos-vapor)", kind: "toggle", parent: "", on: true, selectable: true },
{ id: "glyphs", label: "Install Steam Deck/Machine icons", hint: "Deck button icons in gaming mode", kind: "toggle", parent: "", on: true, selectable: true },
{ id: "single", label: "Single user mode", hint: "no password, lock screen or log out (SDDM)", kind: "toggle", parent: "", on: true, selectable: true },
@@ -35,7 +35,9 @@ Item {
function label(r) { return (Texts.items[r.id] || {}).label || r.label; }
function hint(r) { return (Texts.items[r.id] || {}).hint || r.hint; }
function shown(r) { return !r.parent || !!want[r.parent]; }
// `needs: "boot"` (an older Steamify's --defaults --list): only with Boot into Desktop.
function shown(r) { return (!r.parent || !!want[r.parent]) && (r.needs !== "boot" || boot === "desktop") &&
!(r.hideWhen && want[r.hideWhen]); }
// Steamify's own rules: a sub-option needs its parent (turning a parent
// off turns its sub-options off); single user mode needs the conversion.
@@ -46,7 +48,8 @@ Item {
var r = rows[i];
if (w[id] && r.id === id && r.parent) w[r.parent] = true;
if (!w[id] && r.parent === id && r.kind === "toggle") w[r.id] = false;
if (w[id] && r.parent === id && r.kind === "toggle") w[r.id] = true; // opt-out sub-options
if (w[id] && r.parent === id && r.kind === "toggle") w[r.id] = !(r.hideWhen && w[r.hideWhen]); // opt-out sub-options
if (w[id] && r.hideWhen === id) w[r.id] = false; // Big Picture and a silent start exclude each other
}
if (id === "single" && w.single) w.gaming = true;
if (id === "gaming" && !w.gaming) { w.single = false; boot = "gamescope"; }
@@ -56,7 +59,7 @@ Item {
function choice() {
var on = [];
for (var i = 0; i < rows.length; i++)
if (rows[i].kind === "toggle" && want[rows[i].id]) on.push(rows[i].id);
if (rows[i].kind === "toggle" && want[rows[i].id] && shown(rows[i])) on.push(rows[i].id);
if (want.gaming && boot === "desktop") on.push("boot");
// ", " (not ","): Calamares' packagechooser Summary step shows this
// value raw ("Install option: <value>"); steamify-install still