Utility functions for Git repository operations and SHA/ref validation.
The gitutil package contains helpers for:
- Validating hex strings (e.g. commit SHAs).
- Extracting base repository slugs from action paths.
- Finding the root directory of the current Git repository using pure Go filesystem traversal.
- Reading file contents from the
HEADcommit via agitsubprocess.
| Variable | Type | Description |
|---|---|---|
ErrNotGitRepository |
error |
Sentinel error returned by FindGitRoot and FindGitRootFrom when no .git entry is found while traversing up to the filesystem root |
Behavioral contracts:
IsHexStringMUST returnfalsefor the empty string.IsValidFullSHAMUST require exactly 40 lowercase hexadecimal characters; mixed-case or shorter strings MUST returnfalse.IsValidFullSHACaseInsensitiveMUST require exactly 40 hexadecimal characters and accept uppercase and lowercase letters.ValidateGitRefMUST return an error for an empty ref, a ref starting with-, a ref containing a NUL byte, or a ref containing...ValidateGitPathMUST return an error for an empty path, a path starting with-, an absolute path, or a path that is..or starts with../afterpath.Clean.FindGitRootandFindGitRootFromMUST returnErrNotGitRepository(not a wrapped error) when the filesystem root is reached without finding a.gitentry.FindGitRootFromMUST accept both.gitdirectories (normal repositories) and.gitfiles whose content begins withgitdir:(worktrees and submodules).GetwdandUserHomeDirMUST wrap the underlyingoserror with actionable recovery guidance rather than returning a barefmt.Errorf("...: %w", err).ReadFileFromHEADMUST return an error whengitRootis empty.ReadFileFromHEADMUST return an error whenfilePathresolves to a path outsidegitRoot.
import "github.com/github/gh-aw/pkg/gitutil"
// Validate a commit SHA
if gitutil.IsValidFullSHA(commitSHA) {
fmt.Println("Valid 40-character commit SHA")
}
// Validate a ref before passing it to a git subprocess
if err := gitutil.ValidateGitRef(userSuppliedRef); err != nil {
return fmt.Errorf("unsafe git ref: %w", err)
}
// Validate a path before passing it to a git subprocess
if err := gitutil.ValidateGitPath(userSuppliedPath); err != nil {
return fmt.Errorf("unsafe git path: %w", err)
}
// Find the git repository root (pure Go, no git subprocess)
root, err := gitutil.FindGitRoot()
if errors.Is(err, gitutil.ErrNotGitRepository) {
return err
} else if err != nil {
return err
}
// Find the git root starting from a specific directory
root, err = gitutil.FindGitRootFrom("/some/subdir")
if err != nil {
return fmt.Errorf("not in a git repository: %w", err)
}
// Read a file from the HEAD commit (prefer absolute paths under root)
content, err := gitutil.ReadFileFromHEAD(filepath.Join(root, "go.mod"), root)All exported functions are safe for concurrent use. The SHA-validation functions (IsHexString, IsValidFullSHA) are pure functions with no shared state. FindGitRoot and FindGitRootFrom read only the filesystem and the process working directory. ReadFileFromHEAD spawns a git subprocess per call with no shared state.
Internal:
github.com/github/gh-aw/pkg/logger— debug logging
External (runtime):
gitexecutable onPATH— required only byReadFileFromHEAD
FindGitRootandFindGitRootFromuse pure Go filesystem traversal (walking up the directory tree looking for.git), avoiding the need for agitexecutable onPATH. This is important for Rosetta 2 compatibility on macOS ARM64 and restricted environments wheregitmay not be available.FindGitRootFromverifies worktree marker file content (must begin withgitdir:) in addition to existence, guarding against false positives from unrelated files named.git.ReadFileFromHEADrequiresgitonPATHbecause resolving a literal tree entry and reading the resulting blob withgit ls-tree/git cat-fileavoidsgit show HEAD:pathinterpolation and is more reliable than re-implementing pack-file parsing in pure Go.
This specification is automatically maintained by the spec-extractor workflow.
