Context
JuliaComputing/JuliaHub#24048 folded the custom landing page into the homepage layout editor: the landing page is no longer its own server-side object, it's the greeter widget's metadata.content inside the layout served by GET/POST /api/v1/ui/layout/homepage. #59 moves jh admin landing-page onto that endpoint to unbreak TestAdminLandingPageShow (#58), but keeps the old command surface — the CLI still presents the landing page as a first-class thing and does greeter-specific surgery on the layout to maintain that illusion.
Reviewing #59, @Hetarth02 pointed out that the CLI should follow the platform: stop special-casing the greeter and let admins fetch and save the layout itself as JSON.
Proposal
Add a jh admin homepage command pair that treats the layout as an opaque JSON document, mirroring what jh registry config already does for registries:
jh admin homepage show # GET the layout, pretty-print the JSON array
jh admin homepage update --file layout.json # POST the whole array back
cat layout.json | jh admin homepage update # ... or via stdin
# get / edit / push back
jh admin homepage show > layout.json
jh admin homepage update --file layout.json
readRegistryPayload in registries.go is the precedent for the --file-or-stdin read; registryConfigAddCmd / registryConfigUpdateCmd in main.go are the precedent for the command shape.
Most of the plumbing already exists in landing.go from #59:
fetchHomepageLayout and saveHomepageLayout are exactly the two operations.
layoutWidget's Extra map[string]json.RawMessage passthrough already round-trips unknown fields byte-identically, so the CLI doesn't need to model widget types it doesn't understand.
layoutRequest already carries auth and the status+URL error messages.
What a raw-JSON path would let us drop, if we go the replace route: setGreeterContent, removeGreeter, the add-a-greeter-when-missing transform (geometry + order renumbering) and the "no saved layout" refusal in setLandingPage. It also dissolves the two semantics questions #59 flags — what remove should do when there's no separate "default landing text" any more, and whether update should synthesize a greeter card — because neither arises if the CLI doesn't model the greeter at all.
Open questions
@Hetarth02 — two calls to make here, in order.
First: is a CLI path wanted at all? If raw-JSON layout editing isn't an intended admin workflow — if the layout editor UI is meant to be the only supported way to author one — then this issue is a no-op and the CLI's footprint here should stay as small as it is. Everything below assumes the answer is yes.
Then: replace, or add alongside?
Replace. Drop jh admin landing-page entirely; jh admin homepage show/update is the only surface. Smallest CLI, matches the platform's model exactly, no greeter logic to maintain.
The cost: the case that actually broke is reading and setting the welcome text. jh admin landing-page update '# Welcome' becomes "fetch the array, find the greeter, hand-edit metadata.content, post it back" — awkward from a script, and TestAdminLandingPageShow in the platform's jh-cli-e2e-tests would need rewriting into something that parses the layout and digs out the greeter. It also removes the only path that handles a layout with no greeter yet.
Add alongside. Ship jh admin homepage show/update for the general case and keep landing-page as a thin convenience wrapper over the same two helpers. Two commands to document, but the common operation stays one line and the e2e test keeps working unchanged.
My inclination is add alongside, on the grounds that editing the welcome text is the overwhelmingly common admin task and the wrapper is ~40 lines over helpers we need anyway. But if we'd rather not carry both surfaces, replace is defensible and I'll do that instead.
Notes
- Validation on
update should stay minimal — reject non-array / unparseable JSON, otherwise forward as-is and let the server rule. The layout schema is a frontend concern and will grow widget types the CLI shouldn't have to track.
metadata.renderedContent is recomputed server-side from content and client values are ignored (per the JuliaHub code; not yet exercised with an authenticated write — worth confirming when implementing). It matters here because the raw-JSON path forwards renderedContent verbatim, where setGreeterContent strips it: if the server ever did honour a client value, editing content alone would leave a stale rendered card.
- No DELETE endpoint exists; "remove a widget" is just POSTing a layout without it.
Out of scope
Context
JuliaComputing/JuliaHub#24048 folded the custom landing page into the homepage layout editor: the landing page is no longer its own server-side object, it's the
greeterwidget'smetadata.contentinside the layout served byGET/POST /api/v1/ui/layout/homepage. #59 movesjh admin landing-pageonto that endpoint to unbreakTestAdminLandingPageShow(#58), but keeps the old command surface — the CLI still presents the landing page as a first-class thing and does greeter-specific surgery on the layout to maintain that illusion.Reviewing #59, @Hetarth02 pointed out that the CLI should follow the platform: stop special-casing the greeter and let admins fetch and save the layout itself as JSON.
Proposal
Add a
jh admin homepagecommand pair that treats the layout as an opaque JSON document, mirroring whatjh registry configalready does for registries:readRegistryPayloadin registries.go is the precedent for the--file-or-stdin read;registryConfigAddCmd/registryConfigUpdateCmdin main.go are the precedent for the command shape.Most of the plumbing already exists in landing.go from #59:
fetchHomepageLayoutandsaveHomepageLayoutare exactly the two operations.layoutWidget'sExtra map[string]json.RawMessagepassthrough already round-trips unknown fields byte-identically, so the CLI doesn't need to model widget types it doesn't understand.layoutRequestalready carries auth and the status+URL error messages.What a raw-JSON path would let us drop, if we go the replace route:
setGreeterContent,removeGreeter, the add-a-greeter-when-missing transform (geometry +orderrenumbering) and the "no saved layout" refusal insetLandingPage. It also dissolves the two semantics questions #59 flags — whatremoveshould do when there's no separate "default landing text" any more, and whetherupdateshould synthesize a greeter card — because neither arises if the CLI doesn't model the greeter at all.Open questions
@Hetarth02 — two calls to make here, in order.
First: is a CLI path wanted at all? If raw-JSON layout editing isn't an intended admin workflow — if the layout editor UI is meant to be the only supported way to author one — then this issue is a no-op and the CLI's footprint here should stay as small as it is. Everything below assumes the answer is yes.
Then: replace, or add alongside?
Replace. Drop
jh admin landing-pageentirely;jh admin homepage show/updateis the only surface. Smallest CLI, matches the platform's model exactly, no greeter logic to maintain.The cost: the case that actually broke is reading and setting the welcome text.
jh admin landing-page update '# Welcome'becomes "fetch the array, find the greeter, hand-editmetadata.content, post it back" — awkward from a script, andTestAdminLandingPageShowin the platform'sjh-cli-e2e-testswould need rewriting into something that parses the layout and digs out the greeter. It also removes the only path that handles a layout with no greeter yet.Add alongside. Ship
jh admin homepage show/updatefor the general case and keeplanding-pageas a thin convenience wrapper over the same two helpers. Two commands to document, but the common operation stays one line and the e2e test keeps working unchanged.My inclination is add alongside, on the grounds that editing the welcome text is the overwhelmingly common admin task and the wrapper is ~40 lines over helpers we need anyway. But if we'd rather not carry both surfaces, replace is defensible and I'll do that instead.
Notes
updateshould stay minimal — reject non-array / unparseable JSON, otherwise forward as-is and let the server rule. The layout schema is a frontend concern and will grow widget types the CLI shouldn't have to track.metadata.renderedContentis recomputed server-side fromcontentand client values are ignored (per the JuliaHub code; not yet exercised with an authenticated write — worth confirming when implementing). It matters here because the raw-JSON path forwardsrenderedContentverbatim, wheresetGreeterContentstrips it: if the server ever did honour a client value, editingcontentalone would leave a stale rendered card.Out of scope
jh admin landing-pageto the homepage layout API #59 refuses rather than guess).jh admin landing-page's behaviour before this is decided — it works as of fix(landing): movejh admin landing-pageto the homepage layout API #59.