Documentation
¶
Overview ¶
Package git wraps git shell-outs behind the GitOps interface so the execution and ai_review steps can be tested with a counterfeiter mock. Implementations are responsible for assembling argv slices, capturing stderr, and wrapping errors via bborbe/errors. They MUST NOT use sh -c or any shell-interpolated form — argv only.
Auth model: HTTPS clones with a GitHub token are handled by URL transformation at the call site (cloneURL → https://x-access-token:<tok>@github.com/...). The package itself takes the transformed URL — it does not know about tokens directly.
Deliberately NO Tag method exists on this interface (design § 7.0): versioning + tagging is the github-releaser agent's job on merge. The only push helper hardcodes --no-follow-tags and pushes a single branch ref — a tag physically cannot leak from this agent.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RedactToken ¶
RedactToken exposes the unexported redactToken helper for callers outside this package that need to log wrapped err.Error() strings through the same redaction. Behavior is identical to redactToken.
Types ¶
type BotIdentity ¶
BotIdentity holds the commit author/committer identity. Hardcoded intentionally: the only consumer is github-update-go-agent, and a single value is the contract (mirrors github-releaser-agent).
func DefaultBotIdentity ¶
func DefaultBotIdentity() BotIdentity
DefaultBotIdentity returns the fleet-standard bot identity. The osExecGitOps struct reads this internally on every Commit — there is no override path. Exposed publicly for test assertions.
type ErrorCategory ¶
type ErrorCategory string
ErrorCategory is the closed enum returned by ClassifyError. The steps branch on these values to compose actionable failure messages.
The set is CLOSED — adding a new category requires a design amendment.
const ( // ErrorCategoryAuth — git server rejected credentials (401/403, missing username). ErrorCategoryAuth ErrorCategory = "auth" // ErrorCategoryRepoNotFound — clone target does not exist on the server (404). // NOTE: GitHub returns "Repository not found" for BOTH a typo'd repo URL // AND an unauthenticated request for a private repo. The watcher emit + // App-IAT auth eliminate the private-repo confounder upstream, so a 404 // truly means the repo does not exist (mirrors github-releaser-agent). ErrorCategoryRepoNotFound ErrorCategory = "repo_not_found" // ErrorCategoryProtectedBranchRejected — branch protection rejected the push. ErrorCategoryProtectedBranchRejected ErrorCategory = "protected_branch_rejected" // ErrorCategoryPushNonFastForward — remote moved between clone and push; // controller retry will re-fetch. ErrorCategoryPushNonFastForward ErrorCategory = "push_non_fast_forward" // ErrorCategoryUnexpectedDiff — the update commit touched a forbidden path // (.github/workflows/**). Set DIRECTLY by the execution step's // committed-files guard, NOT by ClassifyError — it is a semantic assertion // on the file set, so there is no stderr fragment to match. ErrorCategoryUnexpectedDiff ErrorCategory = "unexpected_diff" // ErrorCategoryUnknown — message does not match any known fragment. Bug // signal: if this fires repeatedly, add a new substring to the table. ErrorCategoryUnknown ErrorCategory = "unknown" )
func ClassifyError ¶
func ClassifyError(err error) ErrorCategory
ClassifyError maps a git stderr-wrapped error to the closed enum.
Returns the empty-string sentinel `ErrorCategory("")` when err is nil — this distinguishes "no error to classify" from ErrorCategoryUnknown ("an error occurred but no fragment matched").
unexpected_diff is NEVER returned by this function — that category is set by the execution step's guard layer, not at the git-stderr layer.
type GitOps ¶
type GitOps interface {
// CloneAtRef shells out `git clone <cloneURL> <workdir>` (full history —
// the ref may be an arbitrary SHA) and checks out ref. ref may be a
// commit SHA or a branch name (branch names resolve via checkout DWIM
// against origin). cloneURL MUST already include any auth token.
CloneAtRef(ctx context.Context, cloneURL, ref, workdir string) error
// SwitchNewBranch creates and switches to a new local branch
// (git switch -c <branch>).
SwitchNewBranch(ctx context.Context, workdir, branch string) error
// ChangedFiles returns the repo-relative paths of all uncommitted
// changes (staged, unstaged, and untracked) via `git status --porcelain`.
// The execution step uses it to build the explicit commit pathspec and
// to run the workflow-edit guard BEFORE anything is committed.
ChangedFiles(ctx context.Context, workdir string) ([]string, error)
// Commit stages paths (relative to workdir) via explicit `git add -- <paths>`
// and creates a commit with the bot identity. Returns the short SHA
// (7 chars) of the new commit. The bot identity is set per-invocation
// via -c user.name / -c user.email — never writes to the global gitconfig.
Commit(ctx context.Context, workdir, message string, paths ...string) (sha string, err error)
// CommittedFiles returns the repo-relative paths changed by the HEAD
// commit (git diff-tree --no-commit-id --name-only -r HEAD). The
// execution step uses it as a pre-push guard.
CommittedFiles(ctx context.Context, workdir string) ([]string, error)
// Push pushes the current HEAD to origin as refs/heads/<branch>,
// hardcoding --no-follow-tags so a local tag can never reach origin.
Push(ctx context.Context, workdir, branch string) error
// LsRemoteTags shells out `git ls-remote --tags <cloneURL>` and returns
// all SHAs sitting at tag refs (both tag-object SHAs and dereferenced
// ^{} commit SHAs). The ai_review step checks that no branch commit
// carries a tag. cloneURL MUST already include any auth token.
LsRemoteTags(ctx context.Context, cloneURL string) ([]string, error)
// RevList returns the commit SHAs on HEAD that are not reachable from
// base (git rev-list <base>..HEAD) — the branch's own commits.
RevList(ctx context.Context, workdir, base string) ([]string, error)
// ShowFile returns the content of path at the given ref
// (git show <ref>:<path>) — e.g. origin/master:CHANGELOG.md.
ShowFile(ctx context.Context, workdir, ref, path string) ([]byte, error)
}
GitOps is the seam between the steps and the git binary.
All methods are context-aware — callers can cancel mid-operation. workdir is the absolute path to the checkout (created and owned by the caller; the package does not manage workdir lifecycle).
func NewOSExecGitOps ¶
func NewOSExecGitOps() GitOps
NewOSExecGitOps returns a GitOps implementation that shells out to the git binary via os/exec. Zero-arg: the bot identity is constant via DefaultBotIdentity().