Documentation
¶
Overview ¶
Package hooks manages declarative, user-owned Git hook templates.
Index ¶
- Constants
- func AppendEvent(path string, event Event) error
- func AppendEvents(path string, events []Event) error
- func RefreshManagedShims(repoPath, configPath, wbExecutable, projectsRoot string) (bool, error)
- func ReplayPendingMetrics(repoPath, configPath, projectsRoot string) (int, error)
- func RepositoryRoot(path string) (string, error)
- func RunSecureHooksGitHelper(args []string) int
- func SecureExecutionWriteRoots(repoPath, configPath, projectsRoot string) ([]string, error)
- type ActiveProfile
- type ApplyOptions
- type ApplyResult
- type BlockMetrics
- type BlockRunResult
- type CachedGHPRLookup
- type CheckReport
- type Classification
- type DailyMetrics
- type Event
- type ExecutionLayout
- type Finding
- type HookBlock
- type HookConfig
- type MetricsConfig
- type MetricsPolicy
- type MetricsSummary
- type PRLookup
- type PendingMetricsReceipt
- type Policy
- type ProfileDefinition
- type ProfileDefinitionConfig
- type ProfileDetection
- type ProfilesConfig
- type RefUpdate
- type ResolvedHook
- type RunOptions
- type RunResult
Constants ¶
const ( BuiltinPreCommit = "builtin:pre-commit" BuiltinPrePush = "builtin:pre-push" )
const ( BuiltinGoPreCommit = "builtin:go-pre-commit" BuiltinGoPrePush = "builtin:go-pre-push" BuiltinNodePrePush = "builtin:node-pre-push" BuiltinWorktreeGuard = "builtin:worktree-guard" )
const ( // TierSkip runs neither lint nor test. TierSkip pushTier = 0 // TierLint runs lint/vet only. This is the fast lane: a feature branch // with no open pull request yet. TierLint pushTier = 1 // TierFull runs lint/vet and the full test suite. This is a publication // push: the default branch, a tag, or a branch with an open pull request. TierFull pushTier = 2 )
const CheckpointRefPrefix = "refs/wb/checkpoints/"
CheckpointRefPrefix marks a WB checkpoint ref. A push confined to this namespace is a fast-persistence path, never a landing receipt: nothing about a checkpoint ref implies the work merged or landed anywhere.
const DefaultBranchEnv = "WB_DEFAULT_BRANCH"
DefaultBranchEnv lets a repository or a fleet policy assert its default branch explicitly, skipping local detection entirely. It never makes a network call either way; this is purely for the rare local checkout where origin/HEAD was never recorded.
const EventSchemaVersion = 1
const PolicyVersion = 1
const SecureHooksGitHelperArgument = "--wb-internal-hooks-git"
SecureHooksGitHelperArgument selects the private WB child-process path that enters an inherited repository descriptor before writing core.hooksPath. It is handled before normal CLI parsing and is not a user command.
Variables ¶
This section is empty.
Functions ¶
func AppendEvent ¶
func AppendEvents ¶ added in v0.4.0
func RefreshManagedShims ¶ added in v0.22.2
RefreshManagedShims upgrades an already-managed hook installation before a command creates a new worktree. It deliberately leaves repositories without WB-managed hooks alone; a conflicting or malformed managed installation fails the caller before it can create a split-layout checkout.
func ReplayPendingMetrics ¶ added in v0.67.11
func RepositoryRoot ¶
RepositoryRoot resolves path to the enclosing non-bare Git worktree.
func RunSecureHooksGitHelper ¶ added in v0.22.2
RunSecureHooksGitHelper is the child-side handoff for configuring core.hooksPath. The parent supplies the already-open repository as fd 3; only this short-lived child changes directory via fchdir before executing an absolute Git path, so a path substitution cannot redirect the config write.
func SecureExecutionWriteRoots ¶ added in v0.67.11
Types ¶
type ActiveProfile ¶ added in v0.4.0
type ApplyOptions ¶
type ApplyOptions struct {
RepoPath string
ConfigPath string
WBExecutable string
// ProjectsRoot is persisted in every shim so hooks keep the same checkout
// policy when WB was invoked with a non-default --projects-root.
ProjectsRoot string
// WBHome is persisted in every shim so hooks preserve an explicit WB_HOME
// and do not recreate mixed-home state after a later shell invocation.
WBHome string
// WBHomeAllowsLegacy marks a shim installed from the normal default layout.
// Such a shim pins its default write home but must still guard legacy linked
// worktrees until migration completes.
WBHomeAllowsLegacy bool
Repair bool
Force bool
Now func() time.Time
// contains filtered or unexported fields
}
type ApplyResult ¶
type ApplyResult struct {
Report CheckReport
Actions []string
}
func Apply ¶
func Apply(options ApplyOptions) (ApplyResult, error)
Apply installs or repairs WB's local shims. It never overwrites unmanaged hook files unless Force is set, and forced replacements are backed up.
type BlockMetrics ¶ added in v0.4.0
type BlockRunResult ¶ added in v0.4.0
type CachedGHPRLookup ¶ added in v0.65.0
type CachedGHPRLookup struct {
RepoRoot string
RepoSlug string
CachePath string
TTL time.Duration
Timeout time.Duration
Now func() time.Time
RunGH runGHFunc
}
CachedGHPRLookup is the enrichment path in push-tier's PR-status decision. It is deliberately the SECOND thing consulted, never the first: a fresh positive cache entry (a signal WB itself already wrote) wins over asking GitHub again. Negative answers are revalidated because PR creation makes them stale immediately. Every gh call has a hard timeout.
func NewCachedGHPRLookup ¶ added in v0.65.0
func NewCachedGHPRLookup(repoRoot string) *CachedGHPRLookup
NewCachedGHPRLookup builds the production lookup for repoRoot: cache under the user's XDG state directory (matching hook-metrics' own convention), repository slug from the configured origin remote, gh invoked through console.Env() so it can never block on a prompt or a pager.
func (*CachedGHPRLookup) OpenPullRequest ¶ added in v0.65.0
func (l *CachedGHPRLookup) OpenPullRequest(branch string) (open bool, known bool)
OpenPullRequest answers whether branch has an open pull request in RepoSlug. known is false whenever the answer cannot be established without exceeding the bounded, offline-safe budget above: no cache entry, an expired one, gh missing, gh erroring, or the timeout firing. A failed or unknown lookup is never cached, so the next push tries again rather than freezing on a bad answer.
type CheckReport ¶
type CheckReport struct {
RepoRoot string `json:"repo_root"`
ManagedPath string `json:"managed_path"`
ConfigPaths []string `json:"config_paths,omitempty"`
Hooks []string `json:"hooks"`
ProfilesAuto bool `json:"profiles_auto"`
ActiveProfiles []ActiveProfile `json:"active_profiles,omitempty"`
// ExcludedProfiles makes explicit policy exceptions auditable even though
// they intentionally contribute no hook blocks. In particular, disabling
// the default worktree admission guard must never look like an ordinary
// healthy installation with no explanation.
ExcludedProfiles []string `json:"excluded_profiles,omitempty"`
HookBlocks map[string][]string `json:"hook_blocks,omitempty"`
MetricsPath string `json:"metrics_path,omitempty"`
Findings []Finding `json:"findings,omitempty"`
}
func Check ¶
func Check(repoPath, configPath, wbExecutable, projectsRoot string) (CheckReport, error)
Check validates config, core.hooksPath, generated shims, and executability without changing repository state.
type Classification ¶ added in v0.65.0
type Classification struct {
Tier pushTier
Reason string
}
Classification is the tier decision for one pre-push invocation, always paired with a one-line, human-readable Reason so an agent can tell "fast tier, no PR" from "hung" or "full tier, publication push" without guessing.
func ClassifyPendingPush ¶ added in v0.65.0
func ClassifyPendingPush(stdin io.Reader, repoRoot string) (Classification, error)
ClassifyPendingPush is the entry point `wb hooks push-tier` calls: it reads Git's pre-push ref list from stdin (unless stdin is a terminal, in which case there is no ref list to read and reading it would hang forever waiting for a human who was never asked to type anything — see shouldReplicateStdin for the same hazard), determines the repository's default branch from purely local Git state, and classifies the push.
A terminal stdin (an agent or a human running the hook by hand) has no ref list to classify from, so it reports the tier as explicitly unknown rather than guessing: Tier 1, with a reason that says why, exactly like any other unresolved PR-status case. CI remains the real gate for a publication push either way.
func ClassifyPushTier ¶ added in v0.65.0
func ClassifyPushTier(updates []RefUpdate, defaultBranch string, lookup PRLookup) Classification
ClassifyPushTier decides the tier for one composed set of pushed-ref updates. It implements the founder-approved publication rule (option b): a publication push is one whose remote ref is the repository's default branch, OR is a tag, OR names a branch with an open pull request. Anything else pushed to refs/heads/* is the fast lane: lint only. A deletion or a WB checkpoint-ref push carries no requirement of its own; if every pushed ref is one of those two, the whole push skips both lint and test.
defaultBranch may be empty when it cannot be determined locally (never fetched, detached remote HEAD); an empty value simply never matches, so an unresolvable default branch degrades to the same publication test as any other branch (PR lookup) rather than guessing.
func (Classification) ExitCode ¶ added in v0.65.0
func (c Classification) ExitCode() int
ExitCode is the fixed process-exit encoding `wb hooks push-tier` uses to hand its decision to the calling shell template: 0, 1, or 2.
func (Classification) RunLint ¶ added in v0.65.0
func (c Classification) RunLint() bool
RunLint reports whether the Tier 1 lint/vet block should run.
func (Classification) RunTest ¶ added in v0.65.0
func (c Classification) RunTest() bool
RunTest reports whether the Tier 2 test block should run.
type DailyMetrics ¶
type DailyMetrics struct {
Date string `json:"date"`
Commits int `json:"commits"`
PushAttempts int `json:"push_attempts"`
CommitChecks int `json:"commit_checks"`
HookFailures int `json:"hook_failures"`
HookRuns int `json:"hook_runs"`
TotalDurationMS int64 `json:"total_duration_ms"`
AverageDurationMS int64 `json:"average_duration_ms"`
}
type Event ¶
type Event struct {
SchemaVersion int `json:"schema_version"`
Timestamp time.Time `json:"timestamp"`
Repository string `json:"repository"`
Hook string `json:"hook"`
Profile string `json:"profile,omitempty"`
Block string `json:"block,omitempty"`
Action string `json:"action"`
Outcome string `json:"outcome"`
DurationMS int64 `json:"duration_ms"`
Commit string `json:"commit,omitempty"`
Branch string `json:"branch,omitempty"`
OS string `json:"os"`
Arch string `json:"arch"`
Labels map[string]string `json:"labels,omitempty"`
}
func ReadEvents ¶
type ExecutionLayout ¶ added in v0.67.11
type ExecutionLayout struct {
Root string
CacheRoot string
GoPath string
GoCache string
GoModCache string
GoTmpDir string
XDGCacheHome string
ReportRoot string
PendingMetricsRoot string
}
ExecutionLayout is the private writable state a hook may use without touching the repository under test or requiring authority to the whole home directory.
func ResolveExecutionLayout ¶ added in v0.67.11
func ResolveExecutionLayout(repoRoot, projectsRoot string) (ExecutionLayout, error)
type HookBlock ¶ added in v0.4.0
type HookBlock struct {
ID string
Profile string
Hook ResolvedHook
}
type HookConfig ¶
type HookConfig struct {
Template string `yaml:"template" json:"template,omitempty"`
Disabled bool `yaml:"disabled" json:"disabled,omitempty"`
}
HookConfig selects a script template for one Git hook. Relative template paths are resolved from the YAML file that declares them.
type MetricsConfig ¶
type MetricsConfig struct {
Enabled *bool `yaml:"enabled" json:"enabled,omitempty"`
Path string `yaml:"path" json:"path,omitempty"`
Labels map[string]string `yaml:"labels" json:"labels,omitempty"`
}
MetricsConfig controls local hook-event collection. Enabled is a pointer so a repository policy can explicitly override a global true/false value.
type MetricsPolicy ¶
type MetricsSummary ¶
type MetricsSummary struct {
From string `json:"from"`
Through string `json:"through"`
RepositoryFilter string `json:"repository_filter,omitempty"`
Commits int `json:"commits"`
PushAttempts int `json:"push_attempts"`
CommitChecks int `json:"commit_checks"`
HookFailures int `json:"hook_failures"`
HookRuns int `json:"hook_runs"`
AverageDurationMS int64 `json:"average_duration_ms"`
Days []DailyMetrics `json:"days"`
Blocks []BlockMetrics `json:"blocks,omitempty"`
}
type PRLookup ¶ added in v0.65.0
PRLookup answers whether one branch has an open pull request. known is false whenever the answer cannot be established locally or within a bounded, offline-safe check; a caller must treat that as "unknown", never as "no", and must never let "unknown" silently escalate to the full tier.
type PendingMetricsReceipt ¶ added in v0.67.11
type Policy ¶
type Policy struct {
RepoRoot string
ConfigPaths []string
Hooks map[string]ResolvedHook
ProfilesAuto bool
ProfileSelections map[string]bool
ProfileDefinitions map[string]ProfileDefinition
ActiveProfiles []ActiveProfile
Metrics MetricsPolicy
ExplicitPath string
}
Policy is the effective configuration after built-ins, the user's global policy, and the repository policy have been layered in that order.
func LoadPolicy ¶
LoadPolicy loads ~/.config/wb/hooks.yaml and .wb/hooks.yaml when present. An explicit path replaces those discovery locations but still layers on top of WB's conservative built-in templates.
type ProfileDefinition ¶ added in v0.4.0
type ProfileDefinition struct {
Name string
Order int
Detection ProfileDetection
Hooks map[string]ResolvedHook
ConfigPath string
}
type ProfileDefinitionConfig ¶ added in v0.4.0
type ProfileDefinitionConfig struct {
Order *int `yaml:"order"`
Detect *ProfileDetection `yaml:"detect"`
Hooks map[string]HookConfig `yaml:"hooks"`
}
ProfileDefinitionConfig describes a language, toolchain, product, or other repository profile. Detection paths are repository-relative and may contain filepath.Glob patterns.
type ProfileDetection ¶ added in v0.4.0
type ProfilesConfig ¶ added in v0.4.0
type ProfilesConfig struct {
Auto *bool `yaml:"auto"`
Include []string `yaml:"include"`
Exclude []string `yaml:"exclude"`
Definitions map[string]ProfileDefinitionConfig `yaml:"definitions"`
}
ProfilesConfig controls automatic detection and profile composition. Each active profile contributes at most one block to each configured hook type.
type RefUpdate ¶ added in v0.65.0
RefUpdate is one line of the pushed-ref list Git streams on pre-push stdin: "<local ref> <local sha1> <remote ref> <remote sha1>".
func ParseRefUpdates ¶ added in v0.65.0
ParseRefUpdates reads Git's pre-push stdin protocol. Blank lines are skipped; anything else that does not carry exactly four fields is rejected rather than silently ignored, so a malformed invocation is never misclassified as "nothing to push".
type ResolvedHook ¶
type ResolvedHook struct {
Name string
Template string
Builtin bool
Disabled bool
ConfigPath string
}
ResolvedHook is a validated hook entry ready to execute.
type RunOptions ¶
type RunResult ¶
type RunResult struct {
ExitCode int
Duration time.Duration
Blocks []BlockRunResult
MetricsError error
}
func Run ¶
func Run(options RunOptions) (RunResult, error)
Run executes the configured base and active-profile blocks in the repository root and records compact local events. Hook arguments and streams are passed through unchanged; composed pre-push blocks each receive the complete stdin.