Skip to content

Commit d1e3d33

Browse files
committed
Fix: Add useHttpPath to support multiple Git credentials on same host
1 parent b56d91c commit d1e3d33

File tree

2 files changed

+86
-1
lines changed

2 files changed

+86
-1
lines changed

pkg/credentials/gitcreds/basic.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,10 @@ type basicEntry struct {
110110
}
111111

112112
func (be *basicEntry) configBlurb(u string) string {
113-
return fmt.Sprintf("[credential %q]\n username = %s\n", u, be.escapedUsername())
113+
// IMPORTANT: Git credential contexts with useHttpPath=true are required
114+
// for repository-specific credentials on the same host to work correctly.
115+
// Without this, Git only matches by host and uses the first credential found.
116+
return fmt.Sprintf("[credential %q]\n username = %s\n useHttpPath = true\n", u, be.escapedUsername())
114117
}
115118

116119
func (be *basicEntry) escapedUsername() string {

pkg/credentials/gitcreds/creds_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ func TestBasicFlagHandling(t *testing.T) {
6666
helper = store
6767
[credential "https://github.com"]
6868
username = bar
69+
useHttpPath = true
6970
`
7071
if string(b) != expectedGitConfig {
7172
t.Errorf("got: %v, wanted: %v", string(b), expectedGitConfig)
@@ -130,8 +131,10 @@ func TestBasicFlagHandlingTwice(t *testing.T) {
130131
helper = store
131132
[credential "https://github.com"]
132133
username = asdf
134+
useHttpPath = true
133135
[credential "https://gitlab.com"]
134136
username = bleh
137+
useHttpPath = true
135138
`
136139
if string(b) != expectedGitConfig {
137140
t.Errorf("got: %v, wanted: %v", string(b), expectedGitConfig)
@@ -150,6 +153,84 @@ https://bleh:[email protected]
150153
}
151154
}
152155

156+
// TestBasicFlagHandlingMultipleReposSameHost tests the scenario where multiple
157+
// repositories on the same host (e.g., github.com) require different credentials.
158+
// This test verifies that useHttpPath=true is set, which enables path-based credential matching.
159+
func TestBasicFlagHandlingMultipleReposSameHost(t *testing.T) {
160+
credmatcher.VolumePath = t.TempDir()
161+
162+
// Setup credentials for repo1
163+
repo1Dir := credmatcher.VolumeName("repo1-creds")
164+
if err := os.MkdirAll(repo1Dir, os.ModePerm); err != nil {
165+
t.Fatalf("os.MkdirAll(%s) = %v", repo1Dir, err)
166+
}
167+
if err := os.WriteFile(filepath.Join(repo1Dir, corev1.BasicAuthUsernameKey), []byte("user1"), 0o777); err != nil {
168+
t.Fatalf("os.WriteFile(username) = %v", err)
169+
}
170+
if err := os.WriteFile(filepath.Join(repo1Dir, corev1.BasicAuthPasswordKey), []byte("token1"), 0o777); err != nil {
171+
t.Fatalf("os.WriteFile(password) = %v", err)
172+
}
173+
174+
// Setup credentials for repo2
175+
repo2Dir := credmatcher.VolumeName("repo2-creds")
176+
if err := os.MkdirAll(repo2Dir, os.ModePerm); err != nil {
177+
t.Fatalf("os.MkdirAll(%s) = %v", repo2Dir, err)
178+
}
179+
if err := os.WriteFile(filepath.Join(repo2Dir, corev1.BasicAuthUsernameKey), []byte("user2"), 0o777); err != nil {
180+
t.Fatalf("os.WriteFile(username) = %v", err)
181+
}
182+
if err := os.WriteFile(filepath.Join(repo2Dir, corev1.BasicAuthPasswordKey), []byte("token2"), 0o777); err != nil {
183+
t.Fatalf("os.WriteFile(password) = %v", err)
184+
}
185+
186+
fs := flag.NewFlagSet("test", flag.ContinueOnError)
187+
AddFlags(fs)
188+
err := fs.Parse([]string{
189+
"-basic-git=repo1-creds=https://github.com/org/repo1",
190+
"-basic-git=repo2-creds=https://github.com/org/repo2",
191+
})
192+
if err != nil {
193+
t.Fatalf("flag.CommandLine.Parse() = %v", err)
194+
}
195+
196+
t.Setenv("HOME", credmatcher.VolumePath)
197+
if err := NewBuilder().Write(credmatcher.VolumePath); err != nil {
198+
t.Fatalf("Write() = %v", err)
199+
}
200+
201+
b, err := os.ReadFile(filepath.Join(credmatcher.VolumePath, ".gitconfig"))
202+
if err != nil {
203+
t.Fatalf("os.ReadFile(.gitconfig) = %v", err)
204+
}
205+
206+
// Verify that useHttpPath=true is set for both repo-specific credential contexts
207+
expectedGitConfig := `[credential]
208+
helper = store
209+
[credential "https://github.com/org/repo1"]
210+
username = user1
211+
useHttpPath = true
212+
[credential "https://github.com/org/repo2"]
213+
username = user2
214+
useHttpPath = true
215+
`
216+
if string(b) != expectedGitConfig {
217+
t.Errorf("got: %v, wanted: %v", string(b), expectedGitConfig)
218+
}
219+
220+
b, err = os.ReadFile(filepath.Join(credmatcher.VolumePath, ".git-credentials"))
221+
if err != nil {
222+
t.Fatalf("os.ReadFile(.git-credentials) = %v", err)
223+
}
224+
225+
// Verify both credentials are present in the credentials file
226+
expectedGitCredentials := `https://user1:[email protected]/org/repo1
227+
https://user2:[email protected]/org/repo2
228+
`
229+
if string(b) != expectedGitCredentials {
230+
t.Errorf("got: %v, wanted: %v", string(b), expectedGitCredentials)
231+
}
232+
}
233+
153234
func TestBasicFlagHandlingMissingFiles(t *testing.T) {
154235
credmatcher.VolumePath = t.TempDir()
155236
dir := credmatcher.VolumeName("not-found")
@@ -499,6 +580,7 @@ func TestBasicBackslashInUsername(t *testing.T) {
499580
helper = store
500581
[credential "https://github.com"]
501582
username = foo\\bar\\banana
583+
useHttpPath = true
502584
`
503585
if string(b) != expectedGitConfig {
504586
t.Errorf("got: %v, wanted: %v", string(b), expectedGitConfig)

0 commit comments

Comments
 (0)