feat(repo): add entire repo mirror use to repoint a clone at a mirror - #1875
Open
entire[bot] wants to merge 1 commit into
Open
feat(repo): add entire repo mirror use to repoint a clone at a mirror#1875entire[bot] wants to merge 1 commit into
entire repo mirror use to repoint a clone at a mirror#1875entire[bot] wants to merge 1 commit into
Conversation
Closes the last manual step in mirror onboarding. Previously adopting a
mirror meant reading the clone URL out of `mirror get` and hand-running
`git remote set-url` — the `mirror use` verb from the standalone entiredb
CLI was deliberately not ported ("a git-config + git-remote-entire concern
outside the control-plane API").
`use` is the local half of that concern and nothing more: it resolves the
repo's pullable placements, picks one, and rewrites local git config. It
creates no server-side state — an unmirrored repo errors with a pointer at
`mirror create`.
- Bare `entire repo mirror use` resolves the repo from the clone's own
remotes, lists its placements, and prompts for the cluster when there is
more than one.
- On a terminal it then asks whether to repoint the remote (keeping the old
URL as `upstream`) or add the mirror under a separate name.
- Non-interactively it repoints `--remote` (default `origin`) and preserves
the replaced URL under `--upstream`; `--upstream ''` discards it. A
cluster can be pinned with `--cluster` or the `[cluster-host]` positional.
- The replaced URL is always echoed (credential-redacted), and an existing
`upstream` is never clobbered, so the previous state stays recoverable.
Repo identity and the write target are kept as separate roles: `--remote`
names what gets rewritten, which need not exist yet, so identity resolves
from the target remote first and then `origin`.
The cluster picker is factored out of `repo clone` as `selectPlacement` +
`placementPicker` so both verbs share one selection path with per-verb
wording; `repo clone`'s messages are unchanged.
Option order in the replace-or-add prompt is load-bearing and commented as
such: huh answers an unreadable accessible prompt with the first option and
a nil error, so the first branch must match what the same flags do
non-interactively. A test pins that invariant.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 01KYSGR4W1ZATZRW47J510EZGJ
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new entire repo mirror use subcommand to complete mirror onboarding by repointing (or adding) a local git remote to an Entire mirror URL, using the same placement selection logic as entire repo clone.
Changes:
- Add
repo mirror usecommand to rewrite local git remotes based on resolved pullable mirror placements, with interactive/accessible prompting and a non-interactive flag-driven path. - Factor mirror placement selection into a shared
selectPlacementhelper (used by bothrepo cloneandrepo mirror use) with per-verb prompt wording. - Add unit tests for remote-name validation, planning/apply behavior, interactive-choice invariants, and command registration; update the JSON-flag drift guard and CLI architecture docs.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| cmd/entire/cli/repo_mirror.go | Registers the new use subcommand under repo mirror and updates subtree description. |
| cmd/entire/cli/repo_mirror_use.go | Implements entire repo mirror use: resolves upstream repo, selects placement, and applies safe git-remote updates with reporting/prompting. |
| cmd/entire/cli/repo_mirror_use_test.go | Adds unit tests covering validation, planning, apply behavior, prompt invariants, and error surfacing. |
| cmd/entire/cli/repo_clone.go | Extracts shared placement selection/picker wording into selectPlacement + placementPicker. |
| cmd/entire/cli/corecmd_json_flag_test.go | Updates command surface expectations to include repo mirror use (non-JSON side-effect verb). |
| CLAUDE.md | Documents the mirror use exception within the repo mirror subtree behavior. |
Comment on lines
+237
to
+240
| default: | ||
| // Unreachable with the options above (huh always writes one of them). | ||
| // Kept so an unrecognised value can never fall through into a write. | ||
| return mirrorUseChoice{}, NewSilentError(errors.New("no remote update selected")) |
Comment on lines
+61
to
+69
| var gitRunner = func(ctx context.Context, dir string, args ...string) (string, error) { | ||
| cmd := exec.CommandContext(ctx, "git", args...) | ||
| cmd.Dir = dir | ||
| out, err := cmd.Output() | ||
| if err != nil { | ||
| return "", fmt.Errorf("git %s: %w", strings.Join(args, " "), err) | ||
| } | ||
| return strings.TrimSpace(string(out)), nil | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
https://entire.io/gh/entireio/cli/trails/955
Closes the last manual step in mirror onboarding. Adopting a mirror previously meant reading the clone URL out of
mirror getand hand-runninggit remote set-url— themirror useverb from the standalone entiredb CLI was deliberately not ported ("a git-config + git-remote-entire concern outside the control-plane API").useis the local half of that concern and nothing more: it resolves the repo's pullable placements, picks one, and rewrites local git config. It creates no server-side state — an unmirrored repo errors with a pointer atmirror create.Surface
entire repo mirror useresolves the repo from the clone's own remotes, lists its placements, and prompts for the cluster when there is more than one.upstream) or add the mirror under a separate name.--remotedirectly and preserves the replaced URL under--upstream.gitremote.RedactURL), and an existingupstreamis never clobbered, so the previous state stays recoverable.Notes for review
Two roles for a remote name.
--remoteis the write target, which need not exist yet, so repo identity resolves from the target remote first and thenorigin. Conflating the two was a bug during development:use --remote entireon a fresh clone failed because it tried to read coordinates from the remote itwas about to create.
Shared picker. The cluster picker is factored out of
repo cloneasselectPlacement+placementPicker, so both verbs share one selection path with per-verb wording.repo clone's messages and behavior are unchanged(
selectCloneTargetis now a thin wrapper).Option order is load-bearing. huh answers an unreadable accessible prompt by writing the FIRST option's value and returning a nil error (verified by probe — it does not leave the bound value untouched and does not cancel). So the first branch of the replace-or-add prompt must produce the same writes the same flags produce non-interactively, or a Ctrl+D would silently diverge from the documented default.
TestPromptMirrorRemoteChoice_FirstOptionMatchesNonInteractivepins that invariant. Worth knowing separately:repo clone's picker shares this property (closed stdin under a forced TTY clones from the first cluster); left alone as out of scope here.Verification
mise run lint— 0 issuesmise run test— 8503 pass. The--jsondrift guard(
TestControlPlaneJSONFlag_OnlyOnHonoringCommands) correctly caught the newcommand; classified
falsealongside the other side-effect verbs.mise run test:integration— 448 passmise run test:e2e:canary— 4/4gh/entireio/cli,repointed
origin, andgit ls-remotereturned real refs through therewritten URL. Both interactive branches driven end-to-end with
ACCESSIBLE=1.No CHANGELOG entry: entries there carry PR links and are assembled per release.