Skip to content

Smooth mirror onboarding: entire repo mirror create wizard#1519

Merged
pjbgf merged 16 commits into
chore/refresh-openapi-specfrom
cor-645-cli-smooth-mirror-onboarding-flow
Jun 25, 2026
Merged

Smooth mirror onboarding: entire repo mirror create wizard#1519
pjbgf merged 16 commits into
chore/refresh-openapi-specfrom
cor-645-cli-smooth-mirror-onboarding-flow

Conversation

@toothbrush

@toothbrush toothbrush commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

https://entire.io/gh/entireio/cli/trails/660

Adds an interactive onboarding flow and modernizes mirror creation.

User impact

  • entire repo mirror create (no args) → interactive wizard: pick repos, pick regions, watch Docker-style per-mirror progress (processing → ready), then a summary table with clone URLs.
  • Clone readiness now polls the new Mirror.status API instead of probing smart-HTTP info/refs — faster, and surfaces failed/suspended directly.
  • Regions come from GET /api/v1/clusters; the wizard's default selection is jurisdiction-aware (from /me).
  • New hidden entire auth token prints the active control-plane bearer for scripting/curl.

Unchanged

  • One-shot entire repo mirror create <github-url> [cluster-host] keeps its fixed default cluster and behavior; remove/collaborators untouched.

Stacked on #1518 (OpenAPI refresh) — retarget to main once that merges.

🤖 Generated with Claude Code


Note

Medium Risk
Large user-facing change to mirror create (parallel API calls, new polling semantics) plus a hidden command that prints live bearer tokens; behavior is well-guarded (TLS, empty stdout on failure) but affects control-plane onboarding paths.

Overview
Adds interactive mirror onboarding when entire repo mirror create runs with no arguments: onboardable repos from the API, regions from GET /api/v1/clusters, jurisdiction-aware default region selection from /me, then parallel (repo × region) creates with per-line progress and a summary table plus clone URLs.

Clone readiness no longer probes smart-HTTP info/refs or mints repo-scoped tokens for the wait loop. One-shot and wizard paths share createAndAwaitMirror, which polls control-plane Mirror.status via awaitMirrorReady and surfaces ready, failed, and suspended consistently; suspended placements now point users at support instead of an internal admin command.

Also adds hidden entire auth token (same bearer resolution as the API client, stdout-only for scripting), extends auth profile with jurisdiction, and treats cancelled wizard contexts as clean exits in handleFormCancellation. The explicit create <github-url> [cluster-host] form keeps a fixed default cluster; catalog-based region picking is wizard-only.

Reviewed by Cursor Bugbot for commit e5692a0. Configure here.

toothbrush and others added 12 commits June 25, 2026 11:38
Run `entire repo mirror create` with no args to onboard repos interactively:
verifies auth, multi-select repos to mirror, multi-select regions, then creates
every (repo, region) mirror in parallel and prints the clone URLs. The
positional `create <github-url> [cluster-host]` form is unchanged.

Regions come from entire-core's GET /api/v1/clusters (ListClusters). The
endpoint isn't deployed yet, so the /clusters spec fragment is hand-authored
into core.openapi.json and the ogen client regenerated; a real spec refresh is
idempotent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: ee0c83e58bfc
Display "Fetching available repos" and "Fetching regions" spinners before each
picker so the network pause isn't a blank screen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: d5dc5f40492b
Switch both `repo mirror create <url>` and the onboarding wizard onto one shared
createAndAwaitMirror path that polls GetMirror for the new Mirror.status
(processing/ready/failed/suspended) instead of the smart-HTTP info/refs probe.
This drops the repo-scoped token dance and data-plane round trip; suspension and
clone failure now surface directly from the control plane.

Removes the now-dead info/refs probe machinery (waitForMirrorClone,
mirrorAdvertisesHead, checkProbeRedirect, the probe HTTP client and token
source) and their tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 94e331061b08
`entire repo mirror create github.com/octocat/hello-world` (no cluster-host) now
resolves the target from GET /api/v1/clusters — the is_default cluster (or the
sole one) — instead of the hardcoded constant, matching the wizard. Prints the
chosen cluster; errors asking for an explicit [cluster-host] when the catalog is
empty or has no clear default.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: cfcf2050076d
Prints the active control-plane bearer to stdout so curl/scripts can auth
without digging the JWT out of the keychain:

  curl -H "Authorization: Bearer $(entire auth token)" "$CORE/api/v1/clusters"

Honors ENTIRE_TOKEN verbatim, else resolves and refreshes the active context's
login JWT (same bearer the API client uses). Hidden; errors and the
not-logged-in hint go to stderr so stdout stays clean for command substitution.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: c9e38d421945
is_default is per-jurisdiction (each of au/eu/us has its own default), so the
single hardcoded "first default" was wrong. Resolve the caller's jurisdiction
from /me and:

- one-shot `repo mirror create <url>` (no cluster-host): pick the is_default
  cluster for that jurisdiction;
- wizard: still list every cluster (so you can mirror into any), but pre-select
  only your jurisdiction's default instead of all three.

Errors asking for an explicit [cluster-host] when the jurisdiction is unknown or
has no default. Surfaces the jurisdiction in the wizard's "Signed in as" line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: a03b61382aa9
Revert the one-shot `repo mirror create <url>` to the fixed defaultClusterHost
when [cluster-host] is omitted; catalog/jurisdiction-based cluster guessing is
now confined to the no-args interactive wizard, so non-interactive invocations
(and scripts) keep stable, predictable defaults. remove/collaborators were
already on the fixed default and are unchanged.

Drops the one-shot-only resolveDefaultClusterHost/callerJurisdiction/
pickDefaultRegionHost helpers; the wizard keeps its jurisdiction-aware region
pre-selection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: a03599d356ff
The region multi-select already lists every cluster, but on a short terminal
huh's scrollable viewport shows only the top rows — so the pre-checked default
(the caller's jurisdiction) could sit below the fold, making it look like one
unrelated option. List the caller's-jurisdiction clusters first so the visible
top row is the relevant, pre-selected default; trim the description to reclaim a
row. Other jurisdictions remain selectable by scrolling.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 27ad338c331d
huh sizes an unset multi-select height to (rendered option lines − title/
description rows), so a short list (3 regions) collapsed to ~1 visible row while
a long repo list looked fine. Set an explicit Height = option count + header
slack on both pickers so every option is visible (still scrolls past the
terminal). Region picker is no longer stuck showing one option at a time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 6e993f6cd3aa
Replace the single "Creating N mirror(s)…" spinner with one live line per
(repo, region), each updating independently as its CreateMirror + clone poll
advance (processing → ready), then the existing summary table. Lines read
"owner/repo @ <cluster-host>  <spinner|✓|✗> <status>". A per-poll status
callback now flows through awaitMirrorReady/createAndAwaitMirror (one-shot
passes nil and keeps its single spinner). Non-TTY degrades to one printed line
per mirror as it finalizes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 18dbd0a73461
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 4477af226732
@toothbrush toothbrush requested a review from a team as a code owner June 25, 2026 03:34
Copilot AI review requested due to automatic review settings June 25, 2026 03:34
Comment thread cmd/entire/cli/repo_mirror_create_wizard.go
Comment thread cmd/entire/cli/repo_mirror.go
Comment thread cmd/entire/cli/repo_mirror_create_wizard.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR smooths mirror onboarding by adding a zero-argument interactive wizard for entire repo mirror create, and updates clone-readiness waiting to use the control-plane Mirror.status lifecycle instead of probing the data-plane smart-HTTP endpoint. It also adds a hidden entire auth token helper for scripting/curl access.

Changes:

  • Add an interactive repo mirror create wizard to select repos and regions and create mirrors in parallel with live progress + a results table.
  • Replace clone readiness probing with GET /mirrors/{id} status polling (processingready / failed / suspended).
  • Add hidden entire auth token command and document it.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
cmd/entire/cli/repo_mirror.go Routes repo mirror create to wizard when no args; refactors create+wait into shared helpers and updates one-shot output.
cmd/entire/cli/repo_mirror_test.go Replaces old probe/finish-create tests with status-polling and one-shot reporting tests.
cmd/entire/cli/repo_mirror_probe.go Implements awaitMirrorReady polling against Mirror.status and updates suspension messaging helper.
cmd/entire/cli/repo_mirror_create_wizard.go New interactive wizard: repo/region selection, parallel create+poll, live progress, and summary reporting.
cmd/entire/cli/repo_mirror_create_wizard_test.go Unit tests for wizard helpers (selection filtering, host parsing, ordering, progress non-TTY).
cmd/entire/cli/repo_mirror_collaborators.go Updates example invocation for collaborators add.
cmd/entire/cli/auth.go Adds hidden entire auth token command and includes jurisdiction in auth profile for wizard defaults.
cmd/entire/cli/auth_token_test.go Tests env-token printing and not-logged-in behavior for entire auth token.
CLAUDE.md Documents the hidden auth token command in the CLI command layout.

Comment thread cmd/entire/cli/repo_mirror_create_wizard.go
Comment thread cmd/entire/cli/repo_mirror_create_wizard.go Outdated
Comment thread cmd/entire/cli/auth.go
- Wizard: gate on an interactive terminal with a clear pointer at the
  non-interactive form; run the pickers via RunWithContext so Ctrl+C/ctx
  cancellation is handled cleanly (Cursor).
- Wizard: a cancelled run no longer reports in-flight mirrors as failures —
  exit quietly (Cursor).
- createAndAwaitMirror: restore the suspension check for an existing empty
  placement (one GetMirror) so suspended empties surface resume guidance
  instead of a benign "nothing to clone" (Cursor).
- Wizard: suspended/failed results now carry the mirror id + resume command in
  the failure summary, matching the one-shot (Copilot).
- auth token: enforce HTTPS on the core URL unless --insecure-http-auth, like
  auth status (Copilot).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: e2c7535c9951
@toothbrush

Copy link
Copy Markdown
Contributor Author

Addressed the review findings in 7a0ca5c:

  • Wizard skips huh conventions (Cursor) — added an interactive.CanPromptInteractively() gate (clear message pointing at the non-interactive create <github-url> [cluster-host] form), and switched both pickers to form.RunWithContext(ctx).
  • Empty mirrors skip suspension check (Cursor)createAndAwaitMirror now does one GetMirror for an existing empty placement and surfaces suspended + resume guidance instead of the benign "nothing to clone" (restores the old finishMirrorCreate behavior; fresh creates can't be suspended, so they're skipped).
  • Wizard treats cancel as failures (Cursor) — a cancelled run (Ctrl+C) now exits quietly via SilentError instead of reporting in-flight mirrors as "N mirror(s) failed".
  • Suspended/failed lack mirror id + guidance (Copilot) — wizard results for suspended/failed now carry the mirror id and resume command, matching the one-shot's explainSuspendedMirror.
  • auth token HTTPS enforcement (Copilot) — now enforces api.RequireSecureURL on the core URL unless --insecure-http-auth, like auth status.

Not changed — false positive: Copilot flagged the for i, t := range targets { g.Go(...) } goroutine as capturing shared loop vars. This repo is Go 1.26 (per-iteration loop variables since 1.22), so the capture is safe — the copyloopvar linter actually removed the explicit i, t := i, t copy earlier as redundant.

Added tests for the TTY gate and non-TTY progress; mise run check green.

@toothbrush

Copy link
Copy Markdown
Contributor Author

@cursor review

Comment thread cmd/entire/cli/repo_mirror_create_wizard.go
Comment thread cmd/entire/cli/repo_mirror_create_wizard.go Outdated
- handleFormCancellation: also treat a cancelled/expired context as a clean
  cancel, so a RunWithContext form whose context is cancelled exits quietly
  instead of "prompt failed: huh: context canceled" (Cursor).
- Drop internal terminology from suspended/failed user messages: the wizard now
  says "the mirror is suspended; contact support" / "the initial clone failed;
  contact support", and explainSuspendedMirror points at support instead of the
  internal `entire-core admin mirrors resume` command (Cursor).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 9ea51e10ee42
@toothbrush

Copy link
Copy Markdown
Contributor Author

@cursor review

Comment thread cmd/entire/cli/repo_mirror_probe.go Outdated
awaitMirrorReady previously aborted the whole wait on any GetMirror error, so a
brief network/API glitch during a long initial clone failed mirror create even
though the clone was still progressing. Retry on the poll interval, giving up
only after maxConsecutivePollErrors consecutive failures (resets on success) or
when the context ends — a persistent error (deleted mirror, revoked auth) still
surfaces instead of spinning to the deadline. (Cursor)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 5d285b69feaf
@toothbrush

Copy link
Copy Markdown
Contributor Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit e5692a0. Configure here.

hostFromPublicURL rejected any non-empty path, so a catalog entry like
https://aws-us-east-2.entire.io/ (Path == "/") was dropped in clustersToRegions
— silently removing clusters and risking "no regions available to mirror into"
if the control plane emits trailing slashes. publicUrl is a trusted catalog
field, so accept a bare "/" path; real paths/queries/fragments/userinfo are
still refused. (Trail review)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 32af87a69f30
@pjbgf pjbgf merged commit e2f6e63 into chore/refresh-openapi-spec Jun 25, 2026
9 checks passed
@pjbgf pjbgf deleted the cor-645-cli-smooth-mirror-onboarding-flow branch June 25, 2026 10:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants