The Steam Deck updater kept sabotaging its own next update, and hand-deleting a file was the only way through #122

Merged
enricobuehler merged 1 commits from worktree-steamdeck-update-bunnix-dirt into main 2026-08-08 21:17:31 +00:00
Owner

Reported from the field: update.sh --pull aborted with

error: Your local changes to the following files would be overwritten by merge:
	web/bun.nix
Please commit your changes or stash them before you merge.

before a single service restarted, and the only way past it was to delete web/bun.nix by hand.

The updater did it to itself

web/bun.nix is generated — bun2nix is a pure function of web/bun.lock (packaging/nix/README.md) — but it is committed, because the Nix build fetches node_modules only from it. The web step ran:

cd "$SRC/web" && bun install --frozen-lockfile && bun run build

with no --ignore-scripts, so web's postinstall (bun2nix -o bun.nix) fired and rewrote that tracked file on every single update. The SDK step directly below has always passed --ignore-scripts — which is exactly why only web/bun.nix ever went dirty. That asymmetry is the whole bug.

It stays invisible while the committed file is in sync. But main carried a stale web/bun.nix from 1db8f763 (07-27) through b79d90b4 (08-05) — the drift scripts/ci/check-bun-nix.sh was later written to catch. Any Deck updated in that window had the file rewritten to the correct content and has been sitting dirty ever since, so the next --pull that touches it aborts.

What changed

Both scripts/steamdeck/update.sh and scripts/steamdeck/install.sh:

1. Stop creating the dirt. The web install becomes bun install --frozen-lockfile --ignore-scripts && bun run codegen && bun run build. Web has two install lifecycle scripts and we want exactly one of them:

  • prepare is literally "bun run codegen" (orval + paraglide + the i18n check) and is requiredsrc/api/gen, src/paraglide and src/routeTree.gen.ts are gitignored, and prebuild only re-runs orval, so dropping codegen would ship a build with no i18n messages.
  • postinstall is the one that writes a committed file.

Running codegen explicitly is therefore provably equivalent to the old behaviour minus bun2nix.

2. Unstick the Decks already broken. --pull restores web/bun.nix and sdk/bun.nix before pulling. Lossless by construction: regenerating them from the lockfiles is precisely what bun2nix does.

Deliberately not git reset --hard

$SRC defaults to $HOME/punktfunk — the operator's own checkout, which may carry a patched source file or a cherry-pick. Silently destroying that to save an update is a worse trade than one legible error, and a blanket reset would have hidden this bug indefinitely instead of surfacing it. A still-dirty tree now fails with a message naming the files, the way out, and "Nothing was rebuilt or restarted" — rather than git's raw abort mid-script.

Verification

  • bash -n clean on both scripts; shellcheck 0.11.0 clean at -S warning
  • the embedded bash -lc command bodies parse standalone
  • exec bits preserved
  • prepare == bun run codegen confirmed from web/package.json
  • scripts/ci/check-bun-nix.sh passes on main today (both packages in sync, bun2nix pinned at 2.1.2 everywhere), so the drift that made this visible cannot recur

Not fixed here, deliberately: packaging/debian/build-web-deb.sh, packaging/arch/PKGBUILD and packaging/rpm/punktfunk.spec have the same missing --ignore-scripts for web. Those run against throwaway build trees so the dirt is harmless, though it does make each package build depend on bun2nix for nothing. Easy follow-up if we want the consistency.

Reported from the field: `update.sh --pull` aborted with ``` error: Your local changes to the following files would be overwritten by merge: web/bun.nix Please commit your changes or stash them before you merge. ``` before a single service restarted, and the only way past it was to delete `web/bun.nix` by hand. ## The updater did it to itself `web/bun.nix` is **generated** — bun2nix is a pure function of `web/bun.lock` (`packaging/nix/README.md`) — but it is **committed**, because the Nix build fetches `node_modules` only from it. The web step ran: ```sh cd "$SRC/web" && bun install --frozen-lockfile && bun run build ``` with no `--ignore-scripts`, so web's `postinstall` (`bun2nix -o bun.nix`) fired and rewrote that tracked file on *every single update*. The SDK step directly below has always passed `--ignore-scripts` — which is exactly why only `web/bun.nix` ever went dirty. That asymmetry is the whole bug. It stays invisible while the committed file is in sync. But main carried a **stale** `web/bun.nix` from `1db8f763` (07-27) through `b79d90b4` (08-05) — the drift `scripts/ci/check-bun-nix.sh` was later written to catch. Any Deck updated in that window had the file rewritten to the *correct* content and has been sitting dirty ever since, so the next `--pull` that touches it aborts. ## What changed Both `scripts/steamdeck/update.sh` and `scripts/steamdeck/install.sh`: **1. Stop creating the dirt.** The web install becomes `bun install --frozen-lockfile --ignore-scripts && bun run codegen && bun run build`. Web has two install lifecycle scripts and we want exactly one of them: - `prepare` is literally `"bun run codegen"` (orval + paraglide + the i18n check) and is **required** — `src/api/gen`, `src/paraglide` and `src/routeTree.gen.ts` are gitignored, and `prebuild` only re-runs orval, so dropping codegen would ship a build with no i18n messages. - `postinstall` is the one that writes a committed file. Running codegen explicitly is therefore *provably equivalent* to the old behaviour minus bun2nix. **2. Unstick the Decks already broken.** `--pull` restores `web/bun.nix` and `sdk/bun.nix` before pulling. Lossless by construction: regenerating them from the lockfiles is precisely what bun2nix does. ## Deliberately not `git reset --hard` `$SRC` defaults to `$HOME/punktfunk` — the operator's own checkout, which may carry a patched source file or a cherry-pick. Silently destroying that to save an update is a worse trade than one legible error, and a blanket reset would have hidden this bug indefinitely instead of surfacing it. A still-dirty tree now fails with a message naming the files, the way out, and "Nothing was rebuilt or restarted" — rather than git's raw abort mid-script. ## Verification - `bash -n` clean on both scripts; shellcheck 0.11.0 clean at `-S warning` - the embedded `bash -lc` command bodies parse standalone - exec bits preserved - `prepare == bun run codegen` confirmed from `web/package.json` - `scripts/ci/check-bun-nix.sh` passes on main today (both packages in sync, bun2nix pinned at 2.1.2 everywhere), so the drift that made this visible cannot recur Not fixed here, deliberately: `packaging/debian/build-web-deb.sh`, `packaging/arch/PKGBUILD` and `packaging/rpm/punktfunk.spec` have the same missing `--ignore-scripts` for web. Those run against throwaway build trees so the dirt is harmless, though it does make each package build depend on bun2nix for nothing. Easy follow-up if we want the consistency.
enricobuehler added 1 commit 2026-08-08 15:50:31 +00:00
fix(steamdeck): the updater stops dirtying the checkout it just pulled into
ci / web (pull_request) Successful in 57s
ci / bun-nix (pull_request) Successful in 25s
ci / rust-arm64 (pull_request) Successful in 2m56s
ci / docs-site (pull_request) Successful in 2m13s
ci / rust (pull_request) Successful in 6m8s
12f39e1967
`update.sh --pull` could abort with "Your local changes to the following files
would be overwritten by merge: web/bun.nix" — before a single service was
restarted — and the only way past it was to delete the file by hand.

The updater did it to itself. web/bun.nix is generated (bun2nix, a pure
function of web/bun.lock) but committed, because the Nix build fetches
node_modules only from it. The web step ran `bun install --frozen-lockfile`
without --ignore-scripts, so web's `postinstall` (`bun2nix -o bun.nix`)
rewrote that tracked file on every update. Harmless while the committed file
is in sync — but main carried a stale web/bun.nix from 1db8f763 to b79d90b4,
so any Deck updated in that window had it rewritten to the *correct* content
and has been sitting dirty ever since. The SDK step has always passed
--ignore-scripts, which is why only web/bun.nix ever went dirty.

Two changes, both in install.sh and update.sh:

  * the web install now passes --ignore-scripts and runs `bun run codegen`
    explicitly. web has two install lifecycle scripts and we want exactly one:
    `prepare` IS `bun run codegen` (orval + paraglide + the i18n check) and is
    required, since src/api/gen, src/paraglide and src/routeTree.gen.ts are
    gitignored and `prebuild` only re-runs orval; `postinstall` is the one that
    writes a committed file. Equivalent to the old behaviour minus bun2nix.

  * --pull restores web/bun.nix and sdk/bun.nix before pulling, which unsticks
    the installs already broken out there. Deliberately NOT `git reset --hard`:
    $SRC is the operator's own checkout and may carry real local work, so a
    still-dirty tree now fails with a message that names the files and the way
    out instead of git's raw abort. Discarding these two is provably lossless —
    regenerating them from the lockfiles is exactly what bun2nix does.

CI already gates the drift that made this visible (scripts/ci/check-bun-nix.sh,
ci.yml), so main cannot ship a stale bun.nix again.
enricobuehler merged commit ce31a9ddfd into main 2026-08-08 21:17:31 +00:00
enricobuehler deleted branch worktree-steamdeck-update-bunnix-dirt 2026-08-08 21:17:39 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#122