Sync Git identity into guest VMs
Populate guest /root/.gitconfig from host git config --global during work-disk preparation so plain VM shells can commit. Resolve user.name and user.email from the source repo for vm run and write them only into the imported checkout, preserving repo-specific identity overrides. Update mounted guest .gitconfig through a host temp file plus sudo install instead of direct git config --file writes, since the mounted root-owned work disk blocks Git lockfile creation. Validated with GOCACHE=/tmp/banger-gocache go test ./..., make build, and a live alpine vm create smoke check for guest git config.
This commit is contained in:
parent
f798e1db33
commit
42b4a18c63
5 changed files with 308 additions and 0 deletions
|
|
@ -111,6 +111,8 @@ type vmRunRepoSpec struct {
|
|||
CurrentBranch string
|
||||
BranchName string
|
||||
BaseCommit string
|
||||
GitUserName string
|
||||
GitUserEmail string
|
||||
OverlayPaths []string
|
||||
}
|
||||
|
||||
|
|
@ -1444,6 +1446,15 @@ func inspectVMRunRepo(ctx context.Context, rawPath, branchName, fromRef string)
|
|||
}
|
||||
}
|
||||
|
||||
gitUserName, err := gitResolvedConfigValue(ctx, repoRoot, "user.name")
|
||||
if err != nil {
|
||||
return vmRunRepoSpec{}, fmt.Errorf("resolve git user.name for %s: %w", repoRoot, err)
|
||||
}
|
||||
gitUserEmail, err := gitResolvedConfigValue(ctx, repoRoot, "user.email")
|
||||
if err != nil {
|
||||
return vmRunRepoSpec{}, fmt.Errorf("resolve git user.email for %s: %w", repoRoot, err)
|
||||
}
|
||||
|
||||
overlayPaths, err := listVMRunOverlayPaths(ctx, repoRoot)
|
||||
if err != nil {
|
||||
return vmRunRepoSpec{}, err
|
||||
|
|
@ -1457,6 +1468,8 @@ func inspectVMRunRepo(ctx context.Context, rawPath, branchName, fromRef string)
|
|||
CurrentBranch: currentBranch,
|
||||
BranchName: branchName,
|
||||
BaseCommit: baseCommit,
|
||||
GitUserName: gitUserName,
|
||||
GitUserEmail: gitUserEmail,
|
||||
OverlayPaths: overlayPaths,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -1552,6 +1565,10 @@ func gitTrimmedOutput(ctx context.Context, dir string, args ...string) (string,
|
|||
return strings.TrimSpace(string(output)), nil
|
||||
}
|
||||
|
||||
func gitResolvedConfigValue(ctx context.Context, dir, key string) (string, error) {
|
||||
return gitTrimmedOutput(ctx, dir, "config", "--default", "", "--get", key)
|
||||
}
|
||||
|
||||
func parseNullSeparatedOutput(output []byte) []string {
|
||||
chunks := bytes.Split(output, []byte{0})
|
||||
values := make([]string, 0, len(chunks))
|
||||
|
|
@ -1658,6 +1675,10 @@ func vmRunCloneScript(spec vmRunRepoSpec) string {
|
|||
}
|
||||
script.WriteString("find \"$DIR\" -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} +\n")
|
||||
script.WriteString("git config --global --add safe.directory \"$DIR\"\n")
|
||||
if strings.TrimSpace(spec.GitUserName) != "" && strings.TrimSpace(spec.GitUserEmail) != "" {
|
||||
fmt.Fprintf(&script, "git -C \"$DIR\" config user.name %s\n", shellQuote(spec.GitUserName))
|
||||
fmt.Fprintf(&script, "git -C \"$DIR\" config user.email %s\n", shellQuote(spec.GitUserEmail))
|
||||
}
|
||||
return script.String()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -918,6 +918,10 @@ func TestInspectVMRunRepoUsesRepoRootAndOverlayPaths(t *testing.T) {
|
|||
}
|
||||
|
||||
repoRoot := t.TempDir()
|
||||
globalConfigPath := filepath.Join(t.TempDir(), "global.gitconfig")
|
||||
t.Setenv("GIT_CONFIG_GLOBAL", globalConfigPath)
|
||||
testRunGit(t, repoRoot, "config", "--global", "user.email", "global@example.com")
|
||||
testRunGit(t, repoRoot, "config", "--global", "user.name", "Global User")
|
||||
testRunGit(t, repoRoot, "init")
|
||||
testRunGit(t, repoRoot, "config", "user.email", "test@example.com")
|
||||
testRunGit(t, repoRoot, "config", "user.name", "Banger Test")
|
||||
|
|
@ -968,6 +972,12 @@ func TestInspectVMRunRepoUsesRepoRootAndOverlayPaths(t *testing.T) {
|
|||
if spec.BaseCommit != spec.HeadCommit {
|
||||
t.Fatalf("BaseCommit = %q, want head %q", spec.BaseCommit, spec.HeadCommit)
|
||||
}
|
||||
if spec.GitUserName != "Banger Test" {
|
||||
t.Fatalf("GitUserName = %q, want Banger Test", spec.GitUserName)
|
||||
}
|
||||
if spec.GitUserEmail != "test@example.com" {
|
||||
t.Fatalf("GitUserEmail = %q, want test@example.com", spec.GitUserEmail)
|
||||
}
|
||||
wantOverlay := []string{".gitignore", "dir/keep.txt", "tracked.txt", "untracked.txt"}
|
||||
if !reflect.DeepEqual(spec.OverlayPaths, wantOverlay) {
|
||||
t.Fatalf("OverlayPaths = %v, want %v", spec.OverlayPaths, wantOverlay)
|
||||
|
|
@ -1100,6 +1110,8 @@ func TestRunVMRunCreatesImportsAndAttaches(t *testing.T) {
|
|||
CurrentBranch: "main",
|
||||
BranchName: "feature",
|
||||
BaseCommit: "cafebabe",
|
||||
GitUserName: "Repo User",
|
||||
GitUserEmail: "repo@example.com",
|
||||
OverlayPaths: []string{"tracked.txt", "nested/keep.txt"},
|
||||
}
|
||||
err := runVMRun(
|
||||
|
|
@ -1149,6 +1161,12 @@ func TestRunVMRunCreatesImportsAndAttaches(t *testing.T) {
|
|||
if !strings.Contains(fakeClient.script, `git config --global --add safe.directory "$DIR"`) {
|
||||
t.Fatalf("script = %q, want guest safe.directory config", fakeClient.script)
|
||||
}
|
||||
if !strings.Contains(fakeClient.script, `git -C "$DIR" config user.name 'Repo User'`) {
|
||||
t.Fatalf("script = %q, want guest repo user.name config", fakeClient.script)
|
||||
}
|
||||
if !strings.Contains(fakeClient.script, `git -C "$DIR" config user.email 'repo@example.com'`) {
|
||||
t.Fatalf("script = %q, want guest repo user.email config", fakeClient.script)
|
||||
}
|
||||
if fakeClient.streamSourceDir != repoRoot {
|
||||
t.Fatalf("streamSourceDir = %q, want %q", fakeClient.streamSourceDir, repoRoot)
|
||||
}
|
||||
|
|
@ -1167,6 +1185,19 @@ func TestRunVMRunCreatesImportsAndAttaches(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestVMRunCloneScriptSkipsRepoGitIdentityWhenIncomplete(t *testing.T) {
|
||||
script := vmRunCloneScript(vmRunRepoSpec{
|
||||
RepoName: "repo",
|
||||
HeadCommit: "deadbeef",
|
||||
CurrentBranch: "main",
|
||||
GitUserName: "Repo User",
|
||||
})
|
||||
|
||||
if strings.Contains(script, `git -C "$DIR" config user.name`) || strings.Contains(script, `git -C "$DIR" config user.email`) {
|
||||
t.Fatalf("script = %q, want no repo-local git identity commands", script)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewBangerdCommandRejectsArgs(t *testing.T) {
|
||||
cmd := NewBangerdCommand()
|
||||
cmd.SetArgs([]string{"extra"})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue