feat(git): auto-provision the target repo checkout instead of requiring one - #91
Merged
Conversation
…ng one target_repo.checkout previously had to already exist — extctl would just error out (or silently operate on whatever was there) if the path was missing or stale. That also meant the checkout was implicitly something a developer might have cloned and be working in themselves, which ruled out extctl freely keeping it in sync. git.EnsureCheckout now treats target_repo.checkout as a fixed path extctl owns exclusively: clones it via `gh repo clone` if there's no .git yet, otherwise fetches and hard-resets it onto origin/<default_branch>. Because extctl is the only writer, the hard reset is always safe. It stays a real working-tree clone (not bare) since the gate's e2e stage runs docker compose directly inside it. Wired into cmd/extctl/main.go's PersistentPreRunE, gated to only the commands that actually touch the target repo (gen, poll, gate, approve-plan, approve-stages, release) — slate/stats/version are untouched. The checkout path now defaults to ./.extctl-checkout and is resolved absolute at load time, same as runs_dir. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Lukas Hirt <info@hirt.cz>
The example still showed checkout: '../web-extensions', directly contradicting the comment above it that says it defaults to ./.extctl-checkout — a leftover from before target_repo.checkout was auto-provisioned. Show the actual default explicitly instead, matching how runs_dir/scaffold_dir/idea_pool are documented elsewhere in the same file. Also gitignore /.extctl-checkout/ — it's an extctl-managed working directory, never something to commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Lukas Hirt <info@hirt.cz>
LukasHirt
enabled auto-merge (squash)
July 20, 2026 15:03
TestEnsureCheckout_ClonesWhenMissing/_FetchesAndResetsWhenPresent clone into a fresh directory (seed/otherClone) and then commit into it, but a plain `git clone` carries no local git config and CI runners have no global one configured either — commit failed with "Please tell me who you are". Set a local identity on each clone the same way initRepo already does for its repos (new configureIdentity helper, shared with internal/git's other tests). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Lukas Hirt <info@hirt.cz>
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.
Summary
target_repo.checkoutused to have to already exist as a manually-cloned repo.git.EnsureCheckoutnow treats it as a fixed path extctl owns exclusively: clones it viagh repo cloneif there's no.gityet, otherwise fetches origin and hard-resets it ontoorigin/<default_branch>. Since extctl is the only writer to this path, the hard reset is always safe.docker composedirectly against files inside it.cmd/extctl/main.go'sPersistentPreRunE, scoped to only the commands that actually touch the target repo (gen,poll,gate,approve-plan,approve-stages,release) —slate/stats/versionare unaffected.target_repo.checkoutnow defaults to./.extctl-checkoutwhen unset and is resolved to an absolute path at config-load time, same treatment asruns_dir.Note: independent of #90 (the rebase-before-publish PR) — this branches off
main, not off that one.Test plan
go build ./...,go vet ./...cleango test ./...passes, including newTestEnsureCheckout_ClonesWhenMissing/TestEnsureCheckout_FetchesAndResetsWhenPresentininternal/git(the clone path is tested by swapping in a real localgit clonein place ofgh repo clone, since the latter needs real auth/network)target_repo.checkoutlocally and confirmextctl poll(orgen) auto-clones it viagh repo cloneagainst the realowncloud/web-extensionsremote🤖 Generated with Claude Code