Skip to content

Commit 1a38f8e

Browse files
committed
fix(repository): guarantee commit identity for fork worktrees (Windows CI)
Windows runners often lack a global git identity, so commits in the fork worktree failed with “Author identity unknown”. The tests set user.name/email only in the source repo, and worktree commits don’t inherit that. Change: - In ensureFork (under LockTypeForkRepo), ensure the fork repo has user.name and user.email. Copy from the source repo if set; otherwise use a local fallback (“container-use” / “container-use@local”). Scope: container-use fork only (no global config). Covers createInitialCommit and all subsequent worktree commits without sprinkling per-commit flags. Signed-off-by: Guillaume de Rouville <guillaume@dagger.io>
1 parent 66611b6 commit 1a38f8e

File tree

3 files changed

+98
-1
lines changed

3 files changed

+98
-1
lines changed

repository/git.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,49 @@ func (r *Repository) addNonBinaryFiles(ctx context.Context, worktreePath string)
481481
return nil
482482
}
483483

484+
// fork worktrees may run on machines without a global identity (Windows CI).
485+
// Setting it up at the bare repo ensures consistent authorship for all worktrees
486+
func (r *Repository) ensureForkIdentity(ctx context.Context) error {
487+
has := func(key string) bool {
488+
v, err := RunGitCommand(ctx, r.forkRepoPath, "config", "--get", key)
489+
return err == nil && strings.TrimSpace(v) != ""
490+
}
491+
492+
needName := !has("user.name")
493+
needEmail := !has("user.email")
494+
if !needName && !needEmail {
495+
return nil
496+
}
497+
498+
// Prefer identity from the user's source repo, else fallback.
499+
name := ""
500+
email := ""
501+
if v, err := RunGitCommand(ctx, r.userRepoPath, "config", "--get", "user.name"); err == nil {
502+
name = strings.TrimSpace(v)
503+
}
504+
if v, err := RunGitCommand(ctx, r.userRepoPath, "config", "--get", "user.email"); err == nil {
505+
email = strings.TrimSpace(v)
506+
}
507+
if name == "" {
508+
name = "container-use"
509+
}
510+
if email == "" {
511+
email = "container-use@local"
512+
}
513+
514+
if needName {
515+
if _, err := RunGitCommand(ctx, r.forkRepoPath, "config", "user.name", name); err != nil {
516+
return err
517+
}
518+
}
519+
if needEmail {
520+
if _, err := RunGitCommand(ctx, r.forkRepoPath, "config", "user.email", email); err != nil {
521+
return err
522+
}
523+
}
524+
return nil
525+
}
526+
484527
func (r *Repository) shouldSkipFile(fileName string) bool {
485528
skipExtensions := []string{
486529
".tar", ".tar.gz", ".tgz", ".tar.bz2", ".tbz2", ".tar.xz", ".txz",

repository/git_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"os"
66
"path/filepath"
7+
"runtime"
78
"strings"
89
"testing"
910

@@ -172,6 +173,58 @@ func TestCommitWorktreeChanges(t *testing.T) {
172173
})
173174
}
174175

176+
func TestEnsureForkSetsIdentityWhenMissing(t *testing.T) {
177+
t.Setenv("HOME", t.TempDir())
178+
if runtime.GOOS == "windows" {
179+
t.Setenv("USERPROFILE", t.TempDir())
180+
}
181+
182+
ctx := context.Background()
183+
src := t.TempDir()
184+
base := t.TempDir()
185+
186+
_, err := RunGitCommand(ctx, src, "init")
187+
require.NoError(t, err)
188+
189+
r, err := OpenWithBasePath(ctx, src, base)
190+
require.NoError(t, err)
191+
192+
name, err := RunGitCommand(ctx, r.forkRepoPath, "config", "--get", "user.name")
193+
require.NoError(t, err)
194+
email, err := RunGitCommand(ctx, r.forkRepoPath, "config", "--get", "user.email")
195+
require.NoError(t, err)
196+
197+
assert.NotEmpty(t, strings.TrimSpace(name))
198+
assert.NotEmpty(t, strings.TrimSpace(email))
199+
}
200+
201+
func TestEnsureForkCopiesIdentityFromSourceRepo(t *testing.T) {
202+
t.Setenv("HOME", t.TempDir())
203+
if runtime.GOOS == "windows" {
204+
t.Setenv("USERPROFILE", t.TempDir())
205+
}
206+
207+
ctx := context.Background()
208+
src := t.TempDir()
209+
base := t.TempDir()
210+
211+
_, err := RunGitCommand(ctx, src, "init")
212+
require.NoError(t, err)
213+
_, err = RunGitCommand(ctx, src, "config", "user.name", "Source User")
214+
require.NoError(t, err)
215+
_, err = RunGitCommand(ctx, src, "config", "user.email", "source@example.com")
216+
require.NoError(t, err)
217+
218+
r, err := OpenWithBasePath(ctx, src, base)
219+
require.NoError(t, err)
220+
221+
name, _ := RunGitCommand(ctx, r.forkRepoPath, "config", "--get", "user.name")
222+
email, _ := RunGitCommand(ctx, r.forkRepoPath, "config", "--get", "user.email")
223+
224+
assert.Equal(t, "Source User", strings.TrimSpace(name))
225+
assert.Equal(t, "source@example.com", strings.TrimSpace(email))
226+
}
227+
175228
// Test helper functions
176229
func writeFile(t *testing.T, dir, name, content string) {
177230
t.Helper()

repository/repository.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ func (r *Repository) ensureFork(ctx context.Context) error {
141141
os.RemoveAll(r.forkRepoPath)
142142
return err
143143
}
144-
return nil
144+
145+
return r.ensureForkIdentity(ctx)
145146
})
146147
}
147148

0 commit comments

Comments
 (0)