the website checkout under the root: clone it, update it, say which

`<root>/website` is now checked, and the block says which of three things it is:
not there, there but not a checkout, or a checkout with its branch and its head
commit. the middle one is the state worth being loud about — cloning will refuse,
and the reason is something already sitting in the way.

SSH, hardcoded. the repo is private, so https asks for a username with nobody at a
terminal to answer, and the SSH remote is already authenticated by the owner's key.
hardcoded because this plugin administers exactly one website: a field for it would
be a setting nobody changes and one more thing that can point somewhere wrong. when
the checkout's origin is NOT that URL, the block says so — shown always it is noise,
shown on a mismatch it answers why an update pulled something unexpected.

NOTHING HERE MAY HANG. these run inside an HTTP request, so git gets
GIT_TERMINAL_PROMPT=0 and ssh gets BatchMode=yes. without them a key that lost
access does not fail, it BLOCKS forever waiting for a password at a terminal that
does not exist, and the panel spins until somebody restarts the server. there is a
five-minute kill on top for everything else that can wedge.

update is `--ff-only`, never a merge. a merge commit made by a button in a checkout
nobody is watching is a thing somebody finds days later; refusing is recoverable
and legible. it also refuses on a dirty tree rather than failing halfway — and
dirty is `--untracked-files=no`, because a build artefact or an uncommitted .env is
not a reason to make Update permanently unavailable.

clone takes FULL history, not --depth 1. this is a working checkout somebody will
pull into and read the log of, and shallow turns both into a support question.

it sits inside Root location rather than in a block of its own: the path and the
thing at the path are one question, and split apart somebody could change the root
in one place and read a status about the old one in the other, both looking equally
current. hidden while editing, because it describes what is SAVED.

git's own output is shown verbatim on failure. "Permission denied (publickey)" says
exactly what is wrong; anything written in its place would be a guess at which of a
dozen causes it was.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-29 00:51:44 +01:00
co-authored by Claude Opus 5
parent 9cbbc28c82
commit df394e29a4
5 changed files with 346 additions and 1 deletions
+7
View File
@@ -6,6 +6,7 @@ import { Input } from '@/components/ui/input';
import { readableError } from './errors';
import { DirectoryPicker } from './DirectoryPicker';
import { Block } from './Block';
import { WebsiteCheckout } from './WebsiteCheckout';
import { useFlash } from './useFlash';
/**
@@ -157,6 +158,12 @@ export const RootLocationBlock = () => {
<p className="text-xs text-muted-foreground/80">Not set yet — this is the default until you save it.</p>
)}
{/* The checkout that lives under that path. Same section, because the path and the thing at the
path are one question — split apart, somebody could change the root here and read a status
about the old one there, with both looking equally current. Hidden while editing: it
describes what is SAVED, and a half-typed path has nothing at it. */}
{!editing && <WebsiteCheckout rootPath={saved.rootPath} />}
{/* The block's bottom row. Everything you can DO is here, on its own line, so the path above it
gets the full width. */}
<div className="flex flex-wrap items-center gap-2 border-t border-border pt-3">