mirror of
https://github.com/boshu2/agentops.git
synced 2026-09-14 15:08:13 +08:00
5d61bf8a3f
Fixes the three gremlins observed 2026-07-18 (bead age-gate-scripts-worktree-gitdir-p62wo): 1. **Gate scripts broke under hook env from linked worktrees** (#919 regression): new scripts/lib/repo-root.sh (GIT_*-scrubbed resolution, BASH_SOURCE anchor, worktree-correct) swept across 32 scripts. Proof: this PR's own push ran the pre-push gates from a linked worktree and passed. 2. **core.bare=true recurrence**: mechanism proven (leaked GIT_DIR + no-arg git init --bare rewrites the SHARED config); writer traced with high confidence to a live Gas City city process (concrete suspect: gascity cmd_rig_test.go runGitInTest, unscrubbed env + no-arg init --bare — fork-side fix filed separately). Defense: new always.git-config-hygiene gate fails fast on core.bare/test identities with the repair command inline (--self-test proves fail-closed). 3. **Test/test@test.com identity leak**: writer convicted — our own pre-push chain leaked GIT_DIR into go test, whose helpers wrote the shared config via git -C (mechanism proven). Cut at both ends: validate-go-fast scrubs env for children; every test git-helper set-site scrubs; check-test-isolation gains a WARN-ratchet rule (baseline 3 → FAIL at 0). Gates: shellcheck rc=0 across 36 scripts; go build/vet clean; 655+379+149 tests; hygiene self-test; both originally-failing validators pass under the repro env.
88 lines
2.6 KiB
Go
88 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// gitDiscoveryEnv strips GIT_DIR/GIT_WORK_TREE/GIT_COMMON_DIR/GIT_INDEX_FILE
|
|
// from the environment so git discovery resolves the repository from the
|
|
// working directory, never from a leaked parent-process override. A hook-leaked
|
|
// GIT_DIR is not just a read hazard: a "scoped" `git -C <dir> config ...` under
|
|
// it writes the LEAKED repo's shared config (age-gate-scripts-worktree-gitdir-p62wo).
|
|
func gitDiscoveryEnv() []string {
|
|
env := make([]string, 0, len(os.Environ()))
|
|
for _, entry := range os.Environ() {
|
|
switch {
|
|
case strings.HasPrefix(entry, "GIT_DIR="):
|
|
continue
|
|
case strings.HasPrefix(entry, "GIT_WORK_TREE="):
|
|
continue
|
|
case strings.HasPrefix(entry, "GIT_COMMON_DIR="):
|
|
continue
|
|
case strings.HasPrefix(entry, "GIT_INDEX_FILE="):
|
|
continue
|
|
default:
|
|
env = append(env, entry)
|
|
}
|
|
}
|
|
return env
|
|
}
|
|
|
|
// gitChangedFiles lists worktree-modified paths (read-only, bounded) for
|
|
// handoff evidence. Returns nil when git is unavailable or the tree is clean.
|
|
func gitChangedFiles(cwd string, limit int) []string {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1200*time.Millisecond)
|
|
defer cancel()
|
|
command := exec.CommandContext(ctx, "git", "diff", "--name-only", "HEAD")
|
|
command.Dir = cwd
|
|
command.Env = gitDiscoveryEnv()
|
|
out, err := command.Output()
|
|
if err != nil || strings.TrimSpace(string(out)) == "" {
|
|
return nil
|
|
}
|
|
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
|
|
if limit > 0 && len(lines) > limit {
|
|
lines = lines[:limit]
|
|
}
|
|
result := make([]string, 0, len(lines))
|
|
for _, line := range lines {
|
|
if line = strings.TrimSpace(line); line != "" {
|
|
result = append(result, line)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// resolveRepoRoot is read-only discovery. AgentOps does not mutate Git state.
|
|
func resolveRepoRoot(cwd string) (string, error) {
|
|
command := exec.Command("git", "rev-parse", "--show-toplevel")
|
|
command.Dir = cwd
|
|
command.Env = gitDiscoveryEnv()
|
|
output, err := command.Output()
|
|
if err != nil {
|
|
return "", fmt.Errorf("resolve git repo root: %w", err)
|
|
}
|
|
return strings.TrimSpace(string(output)), nil
|
|
}
|
|
|
|
// getCurrentBranch is optional read-only metadata for handoff artifacts.
|
|
func getCurrentBranch(cwd string) (string, error) {
|
|
command := exec.Command("git", "branch", "--show-current")
|
|
command.Dir = cwd
|
|
command.Env = gitDiscoveryEnv()
|
|
output, err := command.Output()
|
|
if err != nil {
|
|
return "", fmt.Errorf("read current branch: %w", err)
|
|
}
|
|
branch := strings.TrimSpace(string(output))
|
|
if branch == "" {
|
|
return "", fmt.Errorf("detached HEAD")
|
|
}
|
|
return branch, nil
|
|
}
|