docs: document the fork customization workflow in SELF_UPDATE - #5929
docs: document the fork customization workflow in SELF_UPDATE#5929tzioup wants to merge 1 commit into
Conversation
The branch-and-rebase workflow for fork users lived only in the UpdateTab fork panel, which renders only when `isFork` is true and which a fork user sees only if they open that tab. SELF_UPDATE.md covered the update mechanism (pull, the switch to main, pre-checkout stashing) but never the workflow it implies. Writes the loop out under a new "Running a customized fork" section: keep main a clean mirror, private work on a branch, rebase after each sync, and then reinstall, rebuild and restart, which update.sh cannot do because it always finishes on main. Notes that install order matters, since npm install rewrites client/package.json and re-stales an earlier build. Also documents a confusing side effect nobody had written down: a clean rebase rewinds and rolls forward the working tree, so every touched file gets a new mtime and getInstallState reports a stale build plus stale deps in all four workspaces even though no dependency changed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🔴 Changes requested
The new section is accurate, well scoped, and correctly placed after "Fork UI: three distinct buttons". One blocking problem: the prescribed install command uses bare npm install, which contradicts the repo's own install path and can dirty tracked lockfiles.
Scope: docs-only — a docs/README.md index row plus a new "Running a customized fork" section in docs/SELF_UPDATE.md
⛔ Blocking (1)
docs/SELF_UPDATE.md:108— Barenpm installcontradicts the repo's own install path
💡 Non-blocking (2)
docs/SELF_UPDATE.md:112— Ordering rationale is downstream of the line 108 fixdocs/SELF_UPDATE.md:106—./update.shdrops Windows; every other mention in this file pairs the two
Test evidence
- ❌
git apply --check (full patch)— thedocs/README.mdhunk no longer applies against base c88fb24 — aMANAGED_APP_UPDATES.mdrow landed between the SELF_UPDATE.md and DEPS.md rows after this branch was cut. Pure context drift, so a rebase is required - ✅
git apply --check --include=docs/SELF_UPDATE.md— applies cleanly;--include=docs/README.mdis the only failing half - ⏭️
npx vitest— the disposable review worktree has no node_modules in root/client/server and the stage forbids package downloads, so the attempt was refused at the registry
Notes
- No docs-index or markdown-link guard test exists under
scripts/orserver/that this change would exercise, so the untested surface is prose only — verification here was by reading the applied section against the code it describes. - The
docs/README.mdhunk needs a rebase before this can merge. That is context drift from a change that landed after the branch was cut, not an authoring error.
Claims verified against the code
update.shalways ends onmain— it force-checks-out main and then installs, builds, and restarts there (update.sh:132-143)npm run buildandnpm run pm2:restartexist (package.json:18,package.json:22)getInstallState()lives inserver/services/installState.jsand is mtime-basedDEP_WORKSPACES = ['.', 'client', 'server', 'autofixer']renders.asroot, matching the "all four workspaces" claim, andmanifest-neweris a real reason string (server/services/installState.js:46,174)client/package.jsongenuinely is a staleBuild input —isClientSourceNewer()stats every file directly underclient/(server/services/installState.js:118-135), so the install-then-build ordering claim is mechanically correctgh repo syncfast-forward and the 409FORK_DIVERGEDresponse matchserver/routes/update.js:179and the existingdocs/SELF_UPDATE.md:74- the
UpdateTabfork tip the section references exists (UpdateTab.jsx:444)
| ```bash | ||
| git checkout main && ./update.sh | ||
| git checkout <your-branch> && git rebase main | ||
| for d in . client server autofixer; do (cd "$d" && npm install); done |
There was a problem hiding this comment.
⛔ Blocking — Bare npm install contradicts the repo's own install path
PortOS's install path deliberately uses npm install --no-save in all four of these workspaces — update.sh:96 and :102, update.ps1:137 and :144 — with an in-tree comment stating why: "This is an installation/reconciliation path, not dependency authoring. --no-save still honors package-lock.json but prevents older npm versions from rewriting newer lockfile metadata (for example libc fields)."
All four lockfiles are tracked in git (package-lock.json, client/, server/, autofixer/), so a fork user who copy-pastes this line can have npm rewrite tracked lockfiles on their private branch. The wrong outcome is exactly the friction this section exists to prevent: on the next cycle git checkout main && ./update.sh finds a dirty tree, stashes it (update.sh:132-143), and hands the user back a git checkout <branch> && git stash pop recovery step — and the rebase now carries lockfile churn the branch never intended.
| for d in . client server autofixer; do (cd "$d" && npm install); done | |
| for d in . client server autofixer; do (cd "$d" && npm install --no-save); done |
| npm run build && npm run pm2:restart | ||
| ``` | ||
|
|
||
| Order matters. `npm install` rewrites `client/package.json`, so building before installing leaves |
There was a problem hiding this comment.
💡 Non-blocking — Ordering rationale is downstream of the line 108 fix
npm install --no-save does not write client/package.json, so once the command above uses --no-save the stated cause ("npm install rewrites client/package.json") no longer holds and this paragraph documents a mechanism the prescribed command no longer triggers.
The install-before-build order is still worth keeping — a dependency change must land before the bundle is built — so restate the reason rather than deleting it. Tying it to the reinstall touching build inputs under client/ generally, which isClientSourceNewer() (server/services/installState.js:118-135) stats wholesale, keeps the paragraph true either way.
| `update.sh` always finishes on `main`, so it cannot do this half for you: | ||
|
|
||
| ```bash | ||
| git checkout main && ./update.sh |
There was a problem hiding this comment.
💡 Non-blocking — ./update.sh drops Windows; every other mention in this file pairs the two
Line 5 (update.sh / update.ps1), line 19, and line 47 all name both, and update.ps1 implements the identical flow — so a Windows fork user copy-pasting ./update.sh gets nothing.
Prefer the cross-platform entry point the root manifest already provides: npm run update (package.json:44) dispatches to update.ps1 on win32 and update.sh elsewhere. Otherwise mirror this file's existing convention and write ./update.sh / update.ps1. The prose on line 103 has the same omission.
| git checkout main && ./update.sh | |
| git checkout main && npm run update |
Summary
The branch-and-rebase workflow for fork users lives in exactly one place today: the
UpdateTabfork panel (
client/src/components/apps/tabs/UpdateTab.jsx:443-444). That panel renders only whenisForkis true, and only if the user opens that tab.docs/SELF_UPDATE.mddocuments the updatemechanism thoroughly — the pull, the forced switch to
main, pre-checkout stashing and recovery— but never the workflow that mechanism implies.
This adds one section,
## Running a customized fork, after "Fork UI: three distinct buttons".No code change.
It writes out the loop the tip states in short form, plus the half that is currently undocumented
anywhere: after rebasing your branch onto
mainyou have to reinstall, rebuild and restart, sinceupdate.shalways finishes onmainand cannot do it for you. It also notes that install ordermatters, because
npm installrewritesclient/package.jsonand re-stales an earlier build.Finally it documents a side effect that reads as a fault and is not one: a clean rebase rewinds the
working tree to the branch's older base and rolls it forward, giving every touched file a new
mtime, so
getInstallState()reports a stale build and stale deps in all four workspaces with nodependency changed and no build input edited.
Test plan
No code paths change, so there is nothing to unit test. Every claim was reproduced on a fork
install running 2.56.0:
main.getInstallState()immediately returnedstaleBuild: trueandstaleDeps.stale: trueforroot,client,serverandautofixer,each with
reason: "manifest-newer", though the branch touches no manifest.npm run buildalone:staleBuildcleared,staleDeps.stalestayed true.npm installacross the four workspaces:staleDeps.stalecleared andstaleBuildreturnedto true, because the install rewrote
client/package.json. Rebuilding after the install clearedboth, which is the order the new section prescribes.
main: with the sametree on disk,
main's copy ofinstallState.jscomputedstaleBuild: truewhile the branch'scomputed
false, and the live status endpoint agreed with the branch.getInstallState()has nocache and
updateChecker.js:284calls it per request, so the reading is live.package.json; this is a prose-only change to one file.