Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions pkg/foreman/slicer/integrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,16 @@ func Integrate(ctx context.Context, opts IntegrateOptions) (*IntegrateResult, er
}
}

// 4. Commit the union.
// 4. Commit the union. Pass the foreman bot identity explicitly via -c:
// the integrate agent runs in a pod whose clone has no user.name/
// user.email in any git config scope, where a bare `git commit` dies with
// "Author identity unknown" (exit 128). This mirrors repo/branch.go, which
// sets the same identity for the coder's commits.
msg := "integrate: union of " + strings.Join(opts.Slices, " ")
if out, err := run(ctx, opts.RepoDir, "commit", "-m", msg); err != nil {
if out, err := run(ctx, opts.RepoDir,
"-c", "user.name=foreman",
"-c", "user.email=foreman@llmkube.dev",
"commit", "-m", msg); err != nil {
return nil, fmt.Errorf("integrate: commit union: %w: %s", err, strings.TrimSpace(out))
}

Expand Down
38 changes: 38 additions & 0 deletions pkg/foreman/slicer/integrate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,41 @@ func TestIntegrate_MissingOptions(t *testing.T) {
t.Fatal("want error for missing RepoDir")
}
}

// TestIntegrate_NoAmbientGitIdentity reproduces the in-cluster foreman-agent
// pod, where git has no user.name/user.email in any config scope. The slice
// branches were committed by the coder (with an identity), but the pod that
// runs Integrate has none, so the union commit must supply its own or it dies
// with "Author identity unknown" (exit 128). The package's own fixture masks
// this because initRepo sets a LOCAL identity; here we neutralize global and
// system config and strip the local identity before the union commit.
func TestIntegrate_NoAmbientGitIdentity(t *testing.T) {
// Point git's global/system config at nonexistent files so no ambient
// identity from the dev's ~/.gitconfig can leak in and hide the bug.
// t.Setenv makes these process-wide for the test and restores after.
empty := t.TempDir()
t.Setenv("GIT_CONFIG_GLOBAL", filepath.Join(empty, "no-global"))
t.Setenv("GIT_CONFIG_SYSTEM", filepath.Join(empty, "no-system"))

dir := initRepo(t)
sliceBranch(t, dir, "slice-a", map[string]string{"a/new.txt": "AAA\n"})
// Drop the local identity: now the repo has NO identity from any scope,
// exactly like the integrate pod's fresh clone.
git(t, dir, "config", "--unset", "user.email")
git(t, dir, "config", "--unset", "user.name")

res, err := Integrate(context.Background(), IntegrateOptions{
RepoDir: dir, Base: "main", Branch: "integ", Slices: []string{"slice-a"},
})
if err != nil {
t.Fatalf("integrate without ambient git identity: %v", err)
}
// The union commit landed, authored by the foreman bot identity.
got := strings.TrimSpace(git(t, dir, "log", "-1", "--format=%an <%ae>", "integ"))
if !strings.Contains(got, "foreman@llmkube.dev") {
t.Fatalf("union commit author = %q, want the foreman bot identity", got)
}
if res.Branch != "integ" {
t.Fatalf("branch = %q, want integ", res.Branch)
}
}
Loading