scrub GIT_* environment variables in gitInTestRepo

This commit is contained in:
Emil Lerch 2026-06-02 15:42:16 -07:00
parent fdd0c97480
commit 7f204747ad
Signed by: lobo
GPG key ID: A7B62D657EF764F8
2 changed files with 56 additions and 1 deletions

View file

@ -1,5 +1,6 @@
[tools]
prek = "0.4.1"
pre-commit = "4.6.0"
zig = "0.16.0"
zls = "0.16.0"
"ubi:DonIsaac/zlint" = "0.7.9"

View file

@ -672,8 +672,57 @@ test "loadFromBytes: all-empty bytes returns empty portfolio" {
// Kept minimal (one happy-path, one missing-file case); the
// bulk of the logic is in `loadFromBytes`, covered above.
/// Build an environment map from the parent process with the GIT_*
/// variables stripped out.
///
/// When these tests run inside a git hook (pre-commit, prek, etc.),
/// the hook runner sets `GIT_INDEX_FILE`, `GIT_DIR`, and
/// `GIT_WORK_TREE` to point at the outer repo's staging state. Git
/// inherits those env vars unconditionally - `git -C <tmpdir>`
/// changes the CWD but does NOT clear these env vars. The result is
/// that `git init` in our temp dir succeeds but subsequent `git
/// add`/`git commit` operate against the OUTER repo's index, with
/// blob references that don't exist in our temp dir's object store
/// ("invalid object 100644 <hash> for '<outer-repo-path>'").
///
/// The hook runner can't fix this for us; per upstream guidance,
/// hooks (and code shelled out by hooks) that run git against a
/// different repo must explicitly opt out of the inherited env. See
/// https://github.com/j178/prek/issues/1786 for the prek-specific
/// instance and https://pre-commit.com/ for the analogous pre-commit
/// guidance.
fn buildScrubbedEnv(allocator: std.mem.Allocator) !std.process.Environ.Map {
var map = try testing.environ.createMap(allocator);
errdefer map.deinit();
// Strip every GIT_* variable. Iterating-while-removing isn't
// supported on the underlying ArrayHashMap, so collect first
// then remove. Keys are duped because `swapRemove` frees the
// map's owned key buffer, which would otherwise leave us with
// dangling pointers in `keys_to_remove`.
var keys_to_remove: std.ArrayList([]u8) = .empty;
defer {
for (keys_to_remove.items) |k| allocator.free(k);
keys_to_remove.deinit(allocator);
}
var it = map.iterator();
while (it.next()) |entry| {
if (std.mem.startsWith(u8, entry.key_ptr.*, "GIT_")) {
try keys_to_remove.append(allocator, try allocator.dupe(u8, entry.key_ptr.*));
}
}
for (keys_to_remove.items) |key| _ = map.swapRemove(key);
return map;
}
/// Run a one-shot git command in `cwd` for test setup. Panics on
/// failure these tests can't proceed without a working repo.
/// failure - these tests can't proceed without a working repo.
///
/// Uses `buildScrubbedEnv` to drop inherited `GIT_*` env vars; see
/// that function's doc comment for why this matters under git
/// hooks.
fn gitInTestRepo(allocator: std.mem.Allocator, cwd: []const u8, argv: []const []const u8) !void {
const full_argv = try allocator.alloc([]const u8, argv.len + 3);
defer allocator.free(full_argv);
@ -681,8 +730,13 @@ fn gitInTestRepo(allocator: std.mem.Allocator, cwd: []const u8, argv: []const []
full_argv[1] = "-C";
full_argv[2] = cwd;
@memcpy(full_argv[3..], argv);
var env_map = try buildScrubbedEnv(allocator);
defer env_map.deinit();
const result = try std.process.run(allocator, testing.io, .{
.argv = full_argv,
.environ_map = &env_map,
.stdout_limit = .limited(64 * 1024),
});
defer allocator.free(result.stdout);