Skip to content

Commit e4b867a

Browse files
authored
Merge pull request #3718 from rajatvig/fix-insteadof-url-mismatch
2 parents bf39a7e + acb59be commit e4b867a

File tree

2 files changed

+82
-5
lines changed

2 files changed

+82
-5
lines changed

internal/job/checkout.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -538,15 +538,35 @@ func (e *Executor) updateRemoteURL(ctx context.Context, gitDir, repository strin
538538

539539
// First check what the existing remote is, for both logging and debugging
540540
// purposes.
541-
args := []string{"remote", "get-url", "origin"}
541+
542+
// Check if there are multiple URLs configured (e.g., via git remote set-url --add).
543+
args := []string{"config", "--get-all", "remote.origin.url"}
542544
if gitDir != "" {
543545
args = append([]string{"--git-dir", gitDir}, args...)
544546
}
545-
gotURL, err := e.shell.Command("git", args...).RunAndCaptureStdout(ctx)
547+
allURLs, err := e.shell.Command("git", args...).RunAndCaptureStdout(ctx)
546548
if err != nil {
547549
return false, err
548550
}
549551

552+
var gotURL string
553+
urls := strings.Split(strings.TrimSpace(allURLs), "\n")
554+
if len(urls) > 1 {
555+
// Multiple URLs configured - fall back to git remote get-url which
556+
// handles this correctly (returns primary fetch URL).
557+
args = []string{"remote", "get-url", "origin"}
558+
if gitDir != "" {
559+
args = append([]string{"--git-dir", gitDir}, args...)
560+
}
561+
gotURL, err = e.shell.Command("git", args...).RunAndCaptureStdout(ctx)
562+
if err != nil {
563+
return false, err
564+
}
565+
} else {
566+
// Single URL - use config output directly to avoid insteadOf transformation.
567+
gotURL = urls[0]
568+
}
569+
550570
if gotURL == repository {
551571
// No need to update anything
552572
return false, nil

internal/job/integration/checkout_integration_test.go

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ func TestCheckingOutLocalGitProjectWithShortCommitHash(t *testing.T) {
297297
// Git should attempt to fetch the shortHash, but fail. Then fallback to fetching
298298
// all the heads and tags and checking out the short commit hash.
299299
git.ExpectAll([][]any{
300-
{"remote", "get-url", "origin"},
300+
{"config", "--get-all", "remote.origin.url"},
301301
{"clean", "-ffxdq"},
302302
{"fetch", "--", "origin", shortCommitHash},
303303
{"config", "remote.origin.fetch"},
@@ -615,7 +615,7 @@ func TestCheckoutErrorIsRetried(t *testing.T) {
615615

616616
// But assert which ones are called
617617
git.ExpectAll([][]any{
618-
{"remote", "get-url", "origin"},
618+
{"config", "--get-all", "remote.origin.url"},
619619
{"clean", "-fdq"},
620620
{"fetch", "-v", "--", "origin", "main"},
621621
{"checkout", "-f", "FETCH_HEAD"},
@@ -678,7 +678,7 @@ func TestFetchErrorIsRetried(t *testing.T) {
678678

679679
// But assert which ones are called
680680
git.ExpectAll([][]any{
681-
{"remote", "get-url", "origin"},
681+
{"config", "--get-all", "remote.origin.url"},
682682
{"clean", "-ffxdq"},
683683
{"fetch", "-v", "--prune", "--depth=1", "--", "origin", "main"},
684684
{"clone", "-v", "--depth=1", "--", tester.Repo.Path, "."},
@@ -1096,6 +1096,63 @@ func TestGitCheckoutWithoutCommitResolvedAndNoMetaData(t *testing.T) {
10961096
tester.RunAndCheck(t, env...)
10971097
}
10981098

1099+
func TestMultipleRemoteURLsFallsBackToGetURL(t *testing.T) {
1100+
t.Parallel()
1101+
1102+
tester, err := NewExecutorTester(mainCtx)
1103+
if err != nil {
1104+
t.Fatalf("NewExecutorTester() error = %v", err)
1105+
}
1106+
defer tester.Close()
1107+
1108+
env := []string{
1109+
"BUILDKITE_GIT_CLONE_FLAGS=-v",
1110+
"BUILDKITE_GIT_CLEAN_FLAGS=-fdq",
1111+
"BUILDKITE_GIT_FETCH_FLAGS=-v",
1112+
}
1113+
1114+
// Simulate state from a previous checkout
1115+
if err := os.MkdirAll(tester.CheckoutDir(), 0o755); err != nil {
1116+
t.Fatalf("error creating dir to clone from: %s", err)
1117+
}
1118+
cmd := exec.Command("git", "clone", "-v", "--", tester.Repo.Path, ".")
1119+
cmd.Dir = tester.CheckoutDir()
1120+
if _, err = cmd.Output(); err != nil {
1121+
t.Fatalf("error cloning test repo: %s", err)
1122+
}
1123+
1124+
// Add a second remote URL to simulate multi-URL configuration
1125+
cmd = exec.Command("git", "remote", "set-url", "--add", "origin", "https://example.com/extra.git")
1126+
cmd.Dir = tester.CheckoutDir()
1127+
if _, err = cmd.Output(); err != nil {
1128+
t.Fatalf("error adding second remote URL: %s", err)
1129+
}
1130+
1131+
// Actually execute git commands, but with expectations
1132+
git := tester.
1133+
MustMock(t, "git").
1134+
PassthroughToLocalCommand()
1135+
1136+
// Assert the expected git commands - should call config --get-all first,
1137+
// then fall back to remote get-url when multiple URLs are detected
1138+
git.ExpectAll([][]any{
1139+
{"config", "--get-all", "remote.origin.url"},
1140+
{"remote", "get-url", "origin"},
1141+
{"clean", "-fdq"},
1142+
{"fetch", "-v", "--", "origin", "main"},
1143+
{"checkout", "-f", "FETCH_HEAD"},
1144+
{"clean", "-fdq"},
1145+
{"--no-pager", "log", "-1", "HEAD", "-s", "--no-color", gitShowFormatArg},
1146+
})
1147+
1148+
// Mock out the meta-data calls to the agent after checkout
1149+
agent := tester.MockAgent(t)
1150+
agent.Expect("meta-data", "exists", job.CommitMetadataKey).AndExitWith(1)
1151+
agent.Expect("meta-data", "set", job.CommitMetadataKey).WithStdin(commitPattern)
1152+
1153+
tester.RunAndCheck(t, env...)
1154+
}
1155+
10991156
type subDirMatcher struct {
11001157
dir string
11011158
}

0 commit comments

Comments
 (0)