chore(selfhost): script git-backed self-host updates, ignore stray backups - #4151
Conversation
…ckups (#1660) The maintainer host deploy is now a clean checkout tracking origin/main, but two gaps remained: nothing stopped an ad-hoc operator snapshot (e.g. `cp file file.bak-notes-20260707`) from silently dirtying `git status`, and the "pull upstream changes" flow was undocumented process rather than a script -- an operator had to remember to run git pull, then deploy-selfhost-prebuilt.sh, in that order, with no guard against a diverged local history quietly producing a merge commit. Add trailing `*.bak-*`/`*.backup-*` catch-alls to .gitignore (verified via `git ls-files` that nothing tracked matches either pattern), and add scripts/selfhost-update.sh: a thin wrapper around `git fetch` + `git merge --ff-only` + the existing deploy-selfhost-prebuilt.sh rebuild step + selfhost-post-update-check.sh. It refuses to proceed -- with no side effects and no script invoked -- on a dirty working tree, a checkout that isn't on the expected branch, or a non-fast-forward divergence, so it never rebases, force-merges, or picks a side on the operator's behalf. Document the new script in the self-hosting operations docs (the existing "Updating and rolling back" section) alongside the manual two-step flow it wraps, and note that .env, gittensory-config/, .deploy-backups/, and any *.local override files all already survive an update untouched.
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
gittensory-ui | d6e5c3f | Commit Preview URL Branch Preview URL |
Jul 08 2026, 08:03 AM |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4151 +/- ##
=======================================
Coverage 93.72% 93.72%
=======================================
Files 385 385
Lines 36243 36243
Branches 13281 13281
=======================================
Hits 33968 33968
Misses 1618 1618
Partials 657 657 🚀 New features to boost your workflow:
|
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ Gittensory review result - manual review recommendedReview updated: 2026-07-08 08:16:30 UTC
⏸️ Suggested Action - Manual Review
Review summary Nits — 7 non-blocking
Linked issue satisfactionPartially addressed Review context
Contributor next steps
Signal definitions
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
…date script (#4158) selfhost-update.sh's ff-only merge failure previously printed the same "diverged history" message for two unrelated causes: a genuine non-fast-forward divergence, and a bad SELFHOST_UPDATE_BRANCH/ SELFHOST_UPDATE_REMOTE override pointing at a ref that doesn't exist at all (git merge --ff-only fails the same way for both). Add an explicit `git rev-parse --verify` check right after the fetch so a missing ref gets its own distinct, more actionable error instead of being misreported as a divergence. Also give a checkout in a detached HEAD state (where `git rev-parse --abbrev-ref HEAD` literally returns "HEAD") its own message instead of the generic branch-mismatch one, which read oddly ("currently on 'HEAD', expected 'main'") and suggested a nonsensical SELFHOST_UPDATE_BRANCH=HEAD override. Also fix an inaccurate comment in test/unit/docs-selfhost-git-deploy-hygiene.test.ts's tracked-file-shadowing test: .gitignore has no effect on files git already tracks (verified: `git add -A` still stages a modification to an already-tracked-but-now-ignored file). The real risk the test guards against is the opposite direction -- a future PR adding a genuinely new tracked file whose name happens to match the pattern would have it silently excluded from `git status`'s untracked list and from `git add -A`/`git add .`, not "untracked on reclone" as the comment previously and incorrectly claimed. Add test coverage for both new error paths (detached HEAD, and SELFHOST_UPDATE_BRANCH naming a branch the remote doesn't have) to selfhost-update-script.test.ts. Both nits were flagged by the Gittensory Orb review on #4151 (merged). Closes #4156
Summary
Follow-up to #1660's Git-backed conversion: the maintainer host deploy is already a clean checkout
tracking
origin/main(done directly on the live host, outside this PR). Two gaps remained:.gitignorealready ignoresgittensory-config.backup-*/and
.deploy-backups/(specific, known backup locations), but nothing catches an arbitraryad-hoc operator snapshot anywhere else in the tree — e.g.
cp docker-compose.yml docker-compose.yml.bak-notes-20260707before hand-editing something. That file sits untracked anddirties
git statusindefinitely, with nothing to stop it.scripts/deploy-selfhost-prebuilt.sh(rebuild) andscripts/selfhost-post-update-check.sh(post-update health probe, docs(selfhost): define safe update and rollback flow #1823) already existed, but"pull upstream changes" itself was undocumented manual process, not a script: the docs showed a
plain two-line
git pull+./scripts/deploy-selfhost-prebuilt.shsequence with no guard — adiverged local history would let
git pullsilently create a merge commit (or fail confusingly),and a dirty tree would get rebuilt with whatever local edits happened to be present.
This PR closes both gaps:
.gitignorecatch-alls,*.bak-*and*.backup-*, placed after the existingnarrower patterns they generalize. Verified via
git ls-files | grep -E '\.bak-|\.backup-'(empty) that nothing currently tracked matches either pattern before adding them, and pinned that
check as a permanent regression test (see Validation).
scripts/selfhost-update.sh— a thin wrapper:git fetch→git merge --ff-only→deploy-selfhost-prebuilt.sh→selfhost-post-update-check.sh. It refuses to proceed, with aclear error and no side effects and no wrapped script invoked, on a dirty working tree, a
checkout not on the expected branch (
mainby default, override withSELFHOST_UPDATE_BRANCH), or a non-fast-forward divergence — it never rebases, force-merges, orpicks a side for the operator.
SELFHOST_UPDATE_REMOTEandSELFHOST_SKIP_POST_UPDATE_CHECKarethe two other overrides.
docs.self-hosting-operations.tsx(apps/gittensory-ui) alongside the manual two-step flow itwraps, plus an explicit note that
.env,gittensory-config/,.deploy-backups/, and any*.localoverride files all already survive an update untouched (all already gitignored, nonetouched by this script).
Closes #1660.
Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Closes #1660).Validation
git diff --checknpm run actionlint(no workflow files touched; ran anyway as part of the full gate)npm run typecheck— clean, twice (before and after the pre-push rebase)npm run test:coveragelocally — 584 test files passed / 2 skipped, 11915 tests passed / 7 skipped, unsharded. Nosrc/**/packages/**lines changed (only.gitignore, one docs route,scripts/**,test/**), so there is no new Codecovcodecov/patchobligation — ran it anyway to confirm nothing broke.npm run test:workersnpm run build:mcpnpm run test:mcp-packnpm run ui:openapi:checknpm run ui:lintnpm run ui:typechecknpm run ui:buildnpm audit --audit-level=moderate— 0 vulnerabilitiestest/unit/selfhost-update-script.test.ts— real end-to-end execution ofscripts/selfhost-update.sh(viaspawnSync) against a throwaway local git remote, withdeploy-selfhost-prebuilt.sh/selfhost-post-update-check.shswapped for logging stubs: happypath (fetch → ff → rebuild → verify, HEAD lands on the new upstream tip), already-up-to-date
no-op,
SELFHOST_SKIP_POST_UPDATE_CHECK=1, rebuild-step failure stops before the health check,dirty tree / wrong branch / non-fast-forward divergence all refuse with no script invoked and no
HEAD movement, a non-default branch via
SELFHOST_UPDATE_BRANCH, a renamed remote viaSELFHOST_UPDATE_REMOTE, and running outside a git checkout entirely. 10/10 passing.test/unit/docs-selfhost-git-deploy-hygiene.test.ts— drift guard (matches the existingdocs-selfhost-*.test.tsconvention) asserting the.gitignorepatterns exist and are orderedafter the narrower ones they generalize, a real
git ls-filescheck that nothing trackedmatches either pattern (not just a static approximation), the script's safety invariants
(never
git push/reset --hard/--force/clean -f/merge --no-ff), and that the docsreference the script and its guarantees. 8/8 passing.
shellcheck scripts/selfhost-update.sh(clean) and manually exercised the script in anad hoc sandbox before writing the permanent tests.
If any required check was skipped, explain why:
npm run test:engine-parityandnpm run db:migrations:check/npm run db:schema-drift:check/npm run selfhost:env-reference:check/npm run selfhost:validate-observability/npm run cf-typegen:checkaren't in this template's checklist but are part ofnpm run test:ciand all passed (
test:ciran green end-to-end twice: once before the pre-push rebase, once after,since the rebase picked up one same-file, different-section upstream commit — see Notes).
gittensory-mcppre-submit predictors (check_before_start,validate_linked_issue,check_slop_risk,lint_pr_text,predict_gate): those need aninteractive GitHub device-flow login this session can't complete, and as the repo owner this PR is
held for manual merge rather than auto-closed on an adverse gate signal.
Safety
ui:openapi:checkconfirms no drift.)UI Evidencesection below with JPG/JPEG or PNG screenshots arranged as organized, captioned, clickable thumbnails. SVG screenshots are not used as review evidence. Review-only screenshots or recordings are not committed to the repository. (No visual/layout change — new paragraphs and code blocks inside the existing "Updating and rolling back" section of an already-shipped docs page; see UI Evidence below.)UI Evidence
Not applicable: this PR only adds prose paragraphs and
<CodeBlock>snippets inside the existing"Updating and rolling back" section of
docs.self-hosting-operations.tsx— no new component,layout, route, or styling. The rendered page structure (headings,
Callout/FeatureRow/CodeBlockprimitives already in use throughout the page) is identical to what is already live; only the words
and code samples describing the update flow changed.
Notes
origin/maintip before pushing (git fetch origin && git rebase origin/main); one upstream commit (perf(selfhost): document retention/concurrency sizing, fix stale runner docs #4142, merged during this session) touched the same docs filebut a different section (the runner CPU/memory table around line ~330-420, versus my edits around
line ~870-995) — rebase applied with zero conflicts, and both
npm run typecheckand the two newtest files were re-run clean afterward.
scripts/*.shanddocker-compose.ymlfor anyexisting
.bak/.backupusage before adding the ignore patterns (onlyscripts/backup.sh'sunrelated
sqlite3 ".backup"SQL command, not a filename pattern); confirmed the "real rebuildstep" by grepping every
scripts/*.shfordocker compose ... buildrather than guessing(
scripts/deploy-selfhost-prebuilt.shis the only source-based rebuild path, already documentedas "Path 2: build from the current git checkout").
scripts/**carries no Codecovcodecov/patchobligation (onlysrc/**/packages/**aremeasured), so the execution-based test suite above is extra rigor, not a coverage requirement.