From 80c69f226c6b45c862c63d5f5f786dd22e3be0b9 Mon Sep 17 00:00:00 2001 From: Christopher Maher Date: Fri, 10 Jul 2026 09:46:30 -0700 Subject: [PATCH] fix(slicer): give the union commit an explicit git identity The integrate agent builds the union commit in a pod whose clone has no user.name/user.email in any git config scope. A bare `git commit` there dies with "Author identity unknown" (exit 128), so integrate returns GATE-ERROR and reconcile cascade-fails even when every slice is green. Pass the foreman bot identity (foreman ) explicitly via `git -c`, matching repo/branch.go which does the same for the coder's own commits. Add a regression test that runs Integrate with no ambient git identity and asserts the union commit lands under the bot identity; the existing fixture masked this by configuring a local identity. Fixes #1056 Signed-off-by: Christopher Maher --- pkg/foreman/slicer/integrate.go | 11 ++++++-- pkg/foreman/slicer/integrate_test.go | 38 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/pkg/foreman/slicer/integrate.go b/pkg/foreman/slicer/integrate.go index d313d6b2..ceab5f4f 100644 --- a/pkg/foreman/slicer/integrate.go +++ b/pkg/foreman/slicer/integrate.go @@ -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)) } diff --git a/pkg/foreman/slicer/integrate_test.go b/pkg/foreman/slicer/integrate_test.go index 717e7e92..101c6f0f 100644 --- a/pkg/foreman/slicer/integrate_test.go +++ b/pkg/foreman/slicer/integrate_test.go @@ -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) + } +}