Documentation
¶
Overview ¶
Package warmstart implements materialization of git repository warm-start snapshots.
A warm-start snapshot enables incremental fetch by restoring a repository's state from a previous scan, avoiding full re-clone. The artifact contains:
- Raw pack files (*.pack, *.idx, *.promisor, *.rev) from objects/pack/
- A single loose ref file (e.g., refs/heads/main)
- Three required git config values for partial clone support
Public parsing, materialization, filesystem, and git-verification boundaries emit structured error records through the package Logger. Records include the operation and commit SHA plus type-specific context such as member, offset, underlying I/O cause, or repository path and reason. If the structured sink fails, Logger writes a plain-text fallback directly to stderr. Logging is side-effect only: callers still receive the original typed operation error and can inspect it with errors.Is or errors.As.
See docs/research/incremental-fetch-warm-start.md for empirical validation.
Package warmstart provides a reference implementation of the cold/warm clone fallback pattern.
This example demonstrates how to integrate warmstart incremental fetch with fallback to full git clone when warmstart extraction fails.
The pattern is: 1. Attempt to fetch and materialize warmstart artifact from ARMOR 2. If warmstart succeeds, perform incremental fetch (git fetch origin) 3. If warmstart fails, evaluate error type and either:
- Fall back to cold clone (most errors)
- Fail the job (permission errors, disk space, NotAGitRepo)
See docs/runbooks/warmstart-error-handling.md for detailed error handling guidance.
Package warmstart provides structured logging for warmstart tarball parsing operations.
Index ¶
- Constants
- Variables
- func AsOSPermission(err error) bool
- func CloneWithFallback(repoURL string, gitDir string, fetchWarmstart func() ([]byte, error), ...) error
- func CloneWithFallbackAndMetrics(repoURL string, gitDir string, correlationID string, ...) error
- func CollectMissingIdxFiles(members []TarballMember) []string
- func CollectMissingRefFiles(members []TarballMember) []string
- func ComputeSHA256(data []byte) string
- func ExtractWarmStart(tarballPath, targetDir string, commitSHAs ...string) (returnErr error)
- func IdxFileExistsInTarball(packFilename string, members []TarballMember) bool
- func IdxFilenameFromPackFilename(packFilename string) string
- func IsOsPermissionError(err error) bool
- func Materialize(gitDir string, snapshot *WarmStartSnapshot, commitSHAs ...string) (returnErr error)
- func RefFileExistsInTarball(packFilename string, members []TarballMember) bool
- func RefFilenameFromPackFilename(packFilename string) string
- func RunSanityChecks(gitDir string, commitSHAs ...string) (returnErr error)
- func SetLogger(logger *Logger)
- func ShouldFallbackToColdClone(err error) (fallback bool, fatalErr error)
- func UnwrapError(err error) error
- func ValidateRefFiles(packFiles []string) []string
- func ValidateRefPath(path string) error
- func ValidateSHA(sha string) error
- func ValidateSymbolicRefTarget(target string) error
- func VerifyGitFsck(gitDir string, commitSHAs ...string) (returnErr error)
- func VerifyGitLog(gitDir string, commitSHAs ...string) (returnErr error)
- func VerifyLooseRefFormat(gitDir string, refPath string, commitSHAs ...string) (returnErr error)
- func WriteLooseRef(gitDir string, ref Ref, commitSHAs ...string) (returnErr error)
- type Config
- type CorruptionError
- type Error
- func NewCorruptPackError(memberName string, context string, commitSHAs ...string) *Error
- func NewIOError(context string, err error, commitSHAs ...string) *Error
- func NewMissingMemberError(memberName string, commitSHAs ...string) *Error
- func NewMissingMemberErrorWithContext(memberName string, context string, commitSHAs ...string) *Error
- func NewTruncatedError(context string, offset int64, commitSHAs ...string) *Error
- func NewTruncatedMemberError(memberName string, context string, offset int64, commitSHAs ...string) *Error
- type ErrorContext
- type ErrorKind
- type ExampleMetricsEmitter
- type LogEntry
- type Logger
- type MetricEmitter
- type NotAGitRepoError
- type Ref
- type TarballContext
- type TarballMember
- type ValidationError
- type ValidationErrorKind
- type WarmStartSnapshot
Constants ¶
const ( ErrorTypeTruncated = "truncated" ErrorTypeMissingMember = "missing_member" ErrorTypeCorruptPack = "corrupt_pack" ErrorTypeIO = "io" ErrorTypeNotGitRepo = "not_git_repo" ErrorTypeValidation = "validation" ErrorTypeUnknown = "unknown" )
Stable error type values used by structured warmstart log entries.
Variables ¶
var ( // ErrInvalidTarball indicates the tarball is corrupted or truncated. ErrInvalidTarball = errors.New("warmstart: invalid tarball") // ErrMissingPackFiles indicates required pack files are missing. ErrMissingPackFiles = errors.New("warmstart: missing required pack files") // ErrMissingRef indicates the ref file is missing. ErrMissingRef = errors.New("warmstart: missing ref file") // ErrMissingConfig indicates the git config is missing. ErrMissingConfig = errors.New("warmstart: missing git config") // ErrInvalidConfig indicates the git config is malformed. ErrInvalidConfig = errors.New("warmstart: invalid git config") // ErrNotAGitRepo indicates the target directory is not a git repository. ErrNotAGitRepo = errors.New("warmstart: not a git repository") )
var ErrInvalidRefMetadata = fmt.Errorf("warmstart: invalid ref metadata")
ErrInvalidRefMetadata indicates ref metadata is malformed.
var ErrMissingRequiredFile = NewMissingMemberError("test-file")
ErrMissingRequiredFile is a sentinel error for missing required file detection in tests. It is used with errors.Is to verify that missing file errors are properly detected.
var ErrTruncatedFile = NewTruncatedError("test file is truncated", 0)
ErrTruncatedFile is a sentinel error for truncated file detection in tests. It is used with errors.Is to verify that truncated file errors are properly detected.
Functions ¶
func AsOSPermission ¶
AsOSPermission checks if the error chain contains a permission error.
func CloneWithFallback ¶
func CloneWithFallback( repoURL string, gitDir string, fetchWarmstart func() ([]byte, error), coldClone func(url string, dir string) error, ) error
CloneWithFallback attempts warmstart incremental fetch, falling back to cold clone on error.
This function demonstrates the recommended pattern for integrating warmstart with fallback to full git clone. It handles all warmstart error types appropriately:
- Truncated, MissingMember, CorruptPack: Fall back to cold clone - IO with permission/disk errors: Fail immediately (local infrastructure issue) - IO with network errors: Fall back to cold clone - Other with NotAGitRepo: Fail immediately (invalid target directory) - Other unknown errors: Fall back to cold clone for robustness
Parameters:
- repoURL: Git repository URL (e.g., "https://github.com/user/repo")
- gitDir: Target directory for git repository (must be empty or non-existent)
- fetchWarmstart: Function to fetch warmstart artifact from storage
- coldClone: Function to perform full git clone
Returns:
- error: nil on success, error if both warmstart and cold clone fail
Example usage:
err := CloneWithFallback(repoURL, gitDir,
func() ([]byte, error) { return fetchFromARMOR(repoURL) },
func(url, dir string) error { return execGitClone(url, dir) })
if err != nil {
log.Fatalf("Clone failed: %v", err)
}
func CloneWithFallbackAndMetrics ¶
func CloneWithFallbackAndMetrics( repoURL string, gitDir string, correlationID string, fetchWarmstart func() ([]byte, error), coldClone func(url string, dir string) error, metrics MetricEmitter, ) error
CloneWithFallbackAndMetrics is a production-ready version that includes metrics and observability.
This extends CloneWithFallback with: - Structured logging with correlation IDs - Metrics emission for monitoring - Detailed error context propagation
See docs/runbooks/warmstart-error-handling.md for recommended metrics.
func CollectMissingIdxFiles ¶
func CollectMissingIdxFiles(members []TarballMember) []string
CollectMissingIdxFiles collects all missing .idx files across all pack files. It iterates over each pack file in the members list, checks if the corresponding .idx file exists, and collects the names of missing .idx files.
Parameters:
- members: Slice of TarballMember representing files in the tarball
Returns:
- []string: List of missing .idx file names (empty if all present)
Example:
members := []TarballMember{
{Name: "objects/pack/pack-abc.pack", Data: ...},
{Name: "objects/pack/pack-def.pack", Data: ...},
{Name: "objects/pack/pack-abc.idx", Data: ...},
}
missing := CollectMissingIdxFiles(members) // returns ["objects/pack/pack-def.idx"]
func CollectMissingRefFiles ¶
func CollectMissingRefFiles(members []TarballMember) []string
CollectMissingRefFiles collects all missing .ref files across all pack files. It iterates over each pack file in the members list, checks if the corresponding .ref file exists, and collects the names of missing .ref files.
Parameters:
- members: Slice of TarballMember representing files in the tarball
Returns:
- []string: List of missing .ref file names (empty if all present)
Example:
members := []TarballMember{
{Name: "objects/pack/pack-abc.pack", Data: ...},
{Name: "objects/pack/pack-def.pack", Data: ...},
{Name: "objects/pack/pack-abc.ref", Data: ...},
}
missing := CollectMissingRefFiles(members) // returns ["objects/pack/pack-def.ref"]
func ComputeSHA256 ¶
ComputeSHA256 computes the SHA256 hash of data and returns the hex-encoded string. This is a test helper function for byte-for-byte verification tests.
func ExtractWarmStart ¶
ExtractWarmStart extracts a warm-start tarball to a target directory.
This function combines git directory initialization, tarball reading, parsing, and materialization into a single operation. It creates a minimal empty git directory structure at targetDir, then extracts and materializes the warm-start snapshot from the tarball file at tarballPath.
Parameters:
- tarballPath: Path to the warm-start tarball file on disk
- targetDir: Path where the git directory should be created/initialized
Returns:
- error: nil if extraction succeeds, error otherwise
Error types returned:
- *Error with Kind=IO: file I/O errors (reading tarball, creating directories)
- *Error with Kind=Truncated: tarball is truncated or corrupted
- *Error with Kind=MissingMember: required tarball members are missing
- ErrInvalidTarball: tarball format is invalid
- ErrMissingConfig, ErrMissingRef, ErrMissingPackFiles: validation failures
Example:
if err := ExtractWarmStart("/path/to/snapshot.tar", "/path/to/repo.git"); err != nil {
return fmt.Errorf("warm-start extraction failed: %w", err)
}
No network access: This function performs only local filesystem operations. It does not make any HTTP requests or network calls.
func IdxFileExistsInTarball ¶
func IdxFileExistsInTarball(packFilename string, members []TarballMember) bool
IdxFileExistsInTarball checks if a .idx file exists in the tarball for a given .pack file. It uses IdxFilenameFromPackFilename to construct the expected .idx filename and searches the provided member list for a matching file.
Parameters:
- packFilename: The .pack file name (e.g., "objects/pack/pack-abc123.pack")
- members: Slice of TarballMember representing files in the tarball
Returns:
- true if the corresponding .idx file is found in the member list, false otherwise
Example:
packFile := "objects/pack/pack-abc123.pack"
members := []TarballMember{{Name: "objects/pack/pack-abc123.idx", Data: ...}}
found := IdxFileExistsInTarball(packFile, members) // returns true
func IdxFilenameFromPackFilename ¶
IdxFilenameFromPackFilename constructs the expected .idx filename from a .pack filename. It strips the .pack extension and appends .idx. For example: "pack-abc123.pack" becomes "pack-abc123.idx" Edge cases handled:
- No .pack extension: appends .idx to the input as-is
- Multiple dots: only the final .pack extension is stripped
func IsOsPermissionError ¶
IsOsPermissionError checks if an error is an OS permission error.
func Materialize ¶
func Materialize(gitDir string, snapshot *WarmStartSnapshot, commitSHAs ...string) (returnErr error)
Materialize writes the warm-start snapshot to a git directory.
The target directory must be an empty git repository (initialized with `git init --bare` or `git init`). After materialization, the repository will be ready for incremental fetch via `git fetch origin`.
Validation approach: Ref and pack file validation is performed upstream in ParseTarball, not here. By the time Materialize is called, the snapshot has already been validated to ensure all required companion files (.idx, .ref) exist for each .pack file. This separation of concerns allows ParseTarball to fail fast on corrupted input before any filesystem writes occur.
This function focuses solely on idempotent filesystem operations: writing pack files to objects/pack/, creating ref directories, and writing the ref file and git config values. It assumes the snapshot is well-formed.
func RefFileExistsInTarball ¶
func RefFileExistsInTarball(packFilename string, members []TarballMember) bool
RefFileExistsInTarball checks if a .ref file exists in the tarball for a given .pack file. It uses RefFilenameFromPackFilename to construct the expected .ref filename and searches the provided member list for a matching file.
Parameters:
- packFilename: The .pack file name (e.g., "objects/pack/pack-abc123.pack")
- members: Slice of TarballMember representing files in the tarball
Returns:
- true if the corresponding .ref file is found in the member list, false otherwise
Example:
packFile := "objects/pack/pack-abc123.pack"
members := []TarballMember{{Name: "objects/pack/pack-abc123.ref", Data: ...}}
found := RefFileExistsInTarball(packFile, members) // returns true
func RefFilenameFromPackFilename ¶
RefFilenameFromPackFilename constructs the expected .ref filename from a .pack filename. It strips the .pack extension and appends .ref. For example: "pack-abc123.pack" becomes "pack-abc123.ref" Edge cases handled:
- No .pack extension: appends .ref to the input as-is
- Multiple dots: only the final .pack extension is stripped
- Double extensions: "pack-abc123.pack.promisor" would become "pack-abc123.pack.promisor.ref"
func RunSanityChecks ¶
RunSanityChecks runs both git fsck and git log verification on a materialized directory.
This is a convenience function that runs both sanity checks in sequence. It ensures the repository is fully functional after materialization.
Parameters:
- gitDir: Path to the git directory (e.g., "/path/to/repo.git")
Returns:
- error: nil if all checks pass, first error encountered if any check fails
Example:
if err := Materialize(gitDir, snapshot); err != nil {
return err
}
if err := RunSanityChecks(gitDir); err != nil {
return fmt.Errorf("sanity checks failed: %w", err)
}
func SetLogger ¶
func SetLogger(logger *Logger)
SetLogger replaces the default package-level logger with a custom implementation. This allows callers to configure custom logging destinations or disable logging entirely.
func ShouldFallbackToColdClone ¶
ShouldFallbackToColdClone evaluates whether a warmstart error should trigger fallback to cold clone.
This function implements the error handling logic from docs/runbooks/warmstart-error-handling.md. It distinguishes between:
1. Fallback-appropriate errors: Truncated, MissingMember, CorruptPack, most IO and Other errors 2. Fatal errors: Permission errors, disk space errors, NotAGitRepo (should NOT fall back)
Parameters:
- err: Error from ParseTarball or Materialize
Returns:
- fallback: true if caller should fall back to cold clone
- fatalErr: non-nil if error is fatal (do NOT fall back, fail the job)
Error handling rules:
Fallback to cold clone (fallback=true, fatalErr=nil): - Truncated: Artifact is corrupt, unusable - MissingMember: Artifact is incomplete - CorruptPack: Pack data is corrupted - IO (network): Temporary I/O failure - IO (unknown): Other I/O issues - Other (unknown): Unexpected errors
Fail immediately, do NOT fall back (fallback=false, fatalErr=err): - IO (permission): Local infrastructure issue, cold clone will also fail - IO (disk space): Disk full, cold clone will also fail - Other (NotAGitRepo): Invalid target directory
func UnwrapError ¶
UnwrapError recursively unwraps errors to find underlying causes.
func ValidateRefFiles ¶
ValidateRefFiles validates .ref file existence for a given list of .pack files. For each .pack file, it constructs the expected .ref filename and checks if it exists on the filesystem using os.Stat.
Parameters:
- packFiles: List of .pack file paths (e.g., []string{"objects/pack/pack-abc123.pack"})
Returns:
- []string: List of missing .ref file names (empty if all present)
Edge cases handled:
- Empty input: returns empty slice
- Duplicate pack names: each is checked independently (duplicate .ref entries possible)
- Files with non-.pack extensions: still processed (constructs .ref by appending if no .pack suffix)
- Filesystem errors: treats non-existence as missing; other errors are ignored
Example:
packFiles := []string{"objects/pack/pack-abc.pack", "objects/pack/pack-def.pack"}
// If only pack-abc.ref exists on filesystem:
missing := ValidateRefFiles(packFiles) // returns ["objects/pack/pack-def.ref"]
func ValidateRefPath ¶
ValidateRefPath validates a git ref path according to git ref naming rules.
Git ref naming rules (from git-check-ref-format(1)): - Ref names can contain slash (/) for hierarchical naming - Ref name components must not begin with a dot (.) - Ref name components must not contain consecutive dots (..) - Ref names must not contain null bytes - Ref names must not contain question mark (?), asterisk (*), open bracket ([), or backslash (\) - Ref names must not begin or end with a slash - Ref names must not end with a dot (.) - Ref names must not contain consecutive slashes (//) - Ref names must not contain the sequence @{ (used for reflogs) - Ref names must not be the single character @ - Ref names must not contain :\\ (Windows paths)
Parameters:
- path: The ref path to validate (e.g., "refs/heads/main")
Returns:
- error: nil if valid, ValidationError with details if invalid
func ValidateSHA ¶
ValidateSHA validates a git SHA string. Returns nil if the SHA is valid (exactly 40 hexadecimal characters).
func ValidateSymbolicRefTarget ¶
ValidateSymbolicRefTarget validates the target of a symbolic ref.
func VerifyGitFsck ¶
VerifyGitFsck runs git fsck to verify repository integrity without network access.
This function runs 'git fsck --no-full --no-progress' to verify the integrity of the repository object database. It does not require or perform any network operations.
Parameters:
- gitDir: Path to the git directory (e.g., "/path/to/repo.git")
Returns:
- error: nil if fsck passes, error with clear message if corruption detected
Error types returned:
- ErrNotAGitRepo: if gitDir is not a valid git repository
- Error with Kind=CorruptPack: if git fsck detects corruption
Example:
if err := VerifyGitFsck(gitDir); err != nil {
return fmt.Errorf("repository integrity check failed: %w", err)
}
func VerifyGitLog ¶
VerifyGitLog runs git log to verify commit history is accessible without network access.
This function runs 'git log --oneline -n 1' to verify that git can read commit history. It performs only a local read operation and does not fetch from remotes.
Parameters:
- gitDir: Path to the git directory (e.g., "/path/to/repo.git")
Returns:
- error: nil if log succeeds, error if commit history is inaccessible
Error types returned:
- ErrNotAGitRepo: if gitDir is not a valid git repository
- Error with Kind=CorruptPack: if git cannot read commit history due to corruption
Example:
if err := VerifyGitLog(gitDir); err != nil {
return fmt.Errorf("commit history verification failed: %w", err)
}
func VerifyLooseRefFormat ¶
VerifyLooseRefFormat verifies that a loose ref file on disk has the correct format.
For direct refs, the file content must be exactly 41 bytes: 40 hexadecimal characters followed by a newline. For symbolic refs (starting with "ref:"), the content must not have a trailing newline.
This function is useful for testing and validation to ensure ref files are written correctly by WriteLooseRef.
Parameters:
- gitDir: Path to the git directory (e.g., "/path/to/repo.git")
- refPath: Relative ref path (e.g., "refs/heads/main")
Returns:
- error: nil if format is correct, error otherwise
Error types returned:
- *Error with Kind=IO: file read failures
- *Error with Kind=Other: incorrect format (wrong size, invalid SHA, etc.)
Example:
if err := warmstart.VerifyLooseRefFormat("/path/to/repo.git", "refs/heads/main"); err != nil {
t.Fatalf("ref format validation failed: %v", err)
}
func WriteLooseRef ¶
WriteLooseRef writes a single ref as a loose file to the git directory.
The function creates the ref file at the correct loose path (e.g., .git/refs/heads/main) with the content formatted as exactly 40 hex characters followed by a newline (41 bytes total) for direct refs. Symbolic refs (starting with "ref:") are written without a trailing newline.
Parent directories are created automatically if they do not exist (e.g., refs/heads/).
Parameters:
- gitDir: Path to the git directory (e.g., "/path/to/repo.git")
- ref: Ref struct containing Path and SHA fields
Returns:
- error: nil on success, error on failure
Error types returned:
- *Error with Kind=IO: filesystem write failures (permission denied, disk full, etc.)
- *Error with Kind=Other: invalid ref path or SHA format
Example:
ref := warmstart.Ref{Path: "refs/heads/main", SHA: "abc123...def"}
if err := warmstart.WriteLooseRef("/path/to/repo.git", ref); err != nil {
return fmt.Errorf("failed to write ref: %w", err)
}
Types ¶
type Config ¶
type Config struct {
// CoreRepositoryFormatVersion must be "1" for promisor packs.
CoreRepositoryFormatVersion string `json:"core.repositoryformatversion"`
// RemoteOriginPromisor enables promisor pack functionality.
RemoteOriginPromisor string `json:"remote.origin.promisor"`
// RemoteOriginPartialCloneFilter specifies the filter (e.g., "blob:none").
RemoteOriginPartialCloneFilter string `json:"remote.origin.partialclonefilter"`
}
Config represents the git configuration values required for warm-start.
type CorruptionError ¶
type CorruptionError struct {
// Context describes what was corrupted.
Context string
// CommitSHA identifies the commit whose data was corrupted.
CommitSHA string
}
CorruptionError represents data corruption in the tarball. Deprecated: Use Error with Kind=CorruptPack or Kind=Truncated instead.
func (*CorruptionError) Error ¶
func (e *CorruptionError) Error() string
Error implements the error interface.
func (*CorruptionError) GetCommitSHA ¶
func (e *CorruptionError) GetCommitSHA() string
GetCommitSHA returns the commit associated with the error.
type Error ¶
type Error struct {
// Kind is the category of error.
Kind ErrorKind
// Context provides human-readable details about what went wrong.
Context string
// MemberName is the tarball member name (if applicable).
MemberName string
// Offset is the byte offset in the tarball (if applicable).
Offset int64
// Underlying is the original error (if applicable).
Underlying error
// CommitSHA identifies the commit whose warm-start data was being parsed.
CommitSHA string
}
Error is the structured error type for all tarball operation failures.
func NewCorruptPackError ¶
NewCorruptPackError creates an Error with Kind=CorruptPack.
func NewIOError ¶
NewIOError creates an Error with Kind=IO from an io error.
func NewMissingMemberError ¶
NewMissingMemberError creates an Error with Kind=MissingMember.
func NewMissingMemberErrorWithContext ¶
func NewMissingMemberErrorWithContext(memberName string, context string, commitSHAs ...string) *Error
NewMissingMemberErrorWithContext creates an Error with Kind=MissingMember and additional context. The context should provide human-readable details about what went wrong, such as a list of missing files.
func NewTruncatedError ¶
NewTruncatedError creates an Error with Kind=Truncated.
func NewTruncatedMemberError ¶
func NewTruncatedMemberError(memberName string, context string, offset int64, commitSHAs ...string) *Error
NewTruncatedMemberError creates an Error with Kind=Truncated for a specific tarball member.
func (*Error) ErrorLogFormat ¶
func (e *Error) ErrorLogFormat() commiterrors.LogFormat
ErrorLogFormat adapts the warmstart error hierarchy to the repository-wide severity/type/code and what/why/where/how-to-fix logging contract. Error() remains unchanged for callers that depend on its domain-specific text.
func (*Error) GetCommitSHA ¶
GetCommitSHA returns the commit associated with the error.
type ErrorContext ¶
type ErrorContext struct {
Type string `json:"type"` // Error type (truncated, missing_member, corrupt_pack, io, not_git_repo)
Message string `json:"message"` // Human-readable error message
MemberName string `json:"member_name,omitempty"` // Tarball member name (if applicable)
Offset int64 `json:"offset,omitempty"` // Byte offset in tarball (if applicable)
Context string `json:"context,omitempty"` // Additional error context
Cause string `json:"cause,omitempty"` // Underlying error (if applicable)
Path string `json:"path,omitempty"` // Repository or artifact path (if applicable)
Field string `json:"field,omitempty"` // Validation field (if applicable)
Value string `json:"value,omitempty"` // Invalid validation value (if applicable)
RefPath string `json:"ref_path,omitempty"` // Git ref path (if applicable)
StackTrace string `json:"stack_trace,omitempty"` // Stack trace captured at error time
Recovery string `json:"recovery,omitempty"` // Suggested recovery action
Severity string `json:"severity,omitempty"` // Error severity (critical, high, medium, low)
Formatted string `json:"formatted,omitempty"` // Canonical severity/type/code + what/why/where/how-to-fix line
}
ErrorContext contains error details for warmstart operations.
type ErrorKind ¶
type ErrorKind int
ErrorKind represents the category of error that occurred during tarball operations.
const ( // Truncated indicates the tarball was cut off or incomplete. Truncated ErrorKind = iota // MissingMember indicates a required tarball member was not found. MissingMember // CorruptPack indicates pack file data corruption was detected. CorruptPack // IO indicates an underlying input/output error occurred. IO // Other indicates an uncategorized error. Other )
type ExampleMetricsEmitter ¶
type ExampleMetricsEmitter struct{}
ExampleMetricsEmitter is a simple logger-based metrics emitter for testing.
func (*ExampleMetricsEmitter) EmitCounter ¶
func (e *ExampleMetricsEmitter) EmitCounter(name string, labels ...string)
type LogEntry ¶
type LogEntry struct {
Timestamp time.Time `json:"timestamp"` // UTC timestamp when the event occurred
EventType string `json:"event_type"` // "error" or "validation_error"
Tarball TarballContext `json:"tarball"` // Tarball operation context
Error ErrorContext `json:"error,omitempty"` // Error details (if applicable)
Metadata map[string]interface{} `json:"metadata,omitempty"` // Additional metadata
}
LogEntry represents a structured warmstart log entry.
type Logger ¶
type Logger struct {
// contains filtered or unexported fields
}
Logger writes structured logs for warmstart tarball operations and errors.
func NewLogger ¶
func NewLogger() *Logger
NewLogger creates a new warmstart logger that writes to stderr.
func NewLoggerWithHandler ¶
NewLoggerWithHandler creates a warmstart logger around a custom slog handler. Callers can use this to configure log levels, attribute replacement, or a non-JSON destination while retaining warmstart's error-entry schema.
func NewLoggerWithOutput
deprecated
NewLoggerWithOutput creates a warmstart logger from a legacy log.Logger.
Deprecated: use NewLoggerWithWriter, NewLoggerWithHandler, or NewLoggerWithSlog. This compatibility helper keeps existing callers working while all warmstart records are emitted through log/slog.
func NewLoggerWithSlog ¶
NewLoggerWithSlog creates a warmstart logger around an initialized slog logger. This is useful when the application already owns logging setup.
func NewLoggerWithWriter ¶
NewLoggerWithWriter creates a warmstart logger that emits JSON slog records to writer. It is the common initialization path for production and tests.
type MetricEmitter ¶
MetricEmitter is an interface for emitting metrics (placeholder for production metrics system).
type NotAGitRepoError ¶
type NotAGitRepoError struct {
// Path is the directory that was checked.
Path string
// Reason explains why it's not a git repository.
Reason string
// CommitSHA identifies the commit being materialized.
CommitSHA string
}
NotAGitRepoError indicates the target directory is not a git repository. Deprecated: Use Error with Kind=Other instead.
func (*NotAGitRepoError) Error ¶
func (e *NotAGitRepoError) Error() string
Error implements the error interface.
func (*NotAGitRepoError) GetCommitSHA ¶
func (e *NotAGitRepoError) GetCommitSHA() string
GetCommitSHA returns the commit associated with the error.
func (*NotAGitRepoError) Is ¶
func (e *NotAGitRepoError) Is(target error) bool
Is allows errors.Is to match NotAGitRepoError against ErrNotAGitRepo.
type Ref ¶
type Ref struct {
// Path is the reference path (e.g., "refs/heads/main", "refs/tags/v1.0", "HEAD").
Path string
// SHA is the 40-character hexadecimal SHA that the reference points to.
// For symbolic refs, this may start with "ref:" (e.g., "ref: refs/heads/main").
SHA string
}
Ref represents a single git reference with its path and target SHA. Ref captures both direct references (pointing to a commit SHA) and symbolic references (pointing to another reference).
func FindRef ¶
FindRef finds a ref by its path. Returns the ref and true if found, nil and false otherwise.
Parameters:
- refs: Slice of refs to search
- path: Exact ref path to find (e.g., "refs/heads/main")
Returns:
- *Ref: Pointer to the found ref (or nil if not found)
- bool: True if found, false otherwise
func ParseRefMetadata ¶
func ParseRefMetadata(members []TarballMember, commitSHAs ...string) (_ []Ref, returnErr error)
ParseRefMetadata parses ref metadata from tarball members. It extracts ref name → SHA mappings from tarball entries in both legacy and new formats.
The function handles two tarball formats:
- Legacy format: A single "ref" file containing "refpath SHA" (e.g., "refs/heads/main abc123")
- New format: Multiple ref files at their original paths (e.g., "refs/heads/main", "refs/tags/v1.0")
Parameters:
- members: Slice of TarballMember representing files in the tarball
Returns:
- []Ref: Slice of parsed references (may be empty if no refs found)
- error: Error if metadata is malformed (e.g., invalid SHA, empty ref path)
Supported ref types:
- refs/heads/* (branches)
- refs/tags/* (tags)
- refs/remotes/* (remote tracking branches)
- HEAD (symbolic reference to default branch)
Example:
members := []TarballMember{
{Name: "refs/heads/main", Data: []byte("abc123...")},
{Name: "refs/tags/v1.0", Data: []byte("def456...")},
}
refs, err := ParseRefMetadata(members)
// refs would contain [{Path: "refs/heads/main", SHA: "abc123..."}, {Path: "refs/tags/v1.0", SHA: "def456..."}]
func RefsByType ¶
RefsByType filters refs by their type (heads, tags, remotes, or other). Returns a new slice containing only refs of the specified type.
Parameters:
- refs: Slice of refs to filter
- refType: Type of ref to filter ("heads", "tags", "remotes", or "" for all)
Returns:
- []Ref: Filtered slice of refs
Example:
heads := RefsByType(refs, "heads") // All refs/heads/* refs tags := RefsByType(refs, "tags") // All refs/tags/* refs
func (*Ref) IsSymbolic ¶
IsSymbolic returns true if this is a symbolic reference (points to another ref).
func (*Ref) SymbolicTarget ¶
SymbolicTarget returns the target reference for symbolic refs. Returns empty string for direct refs.
func (*Ref) Validate ¶
Validate checks that a Ref has valid path and SHA fields. Returns error if the ref path or SHA is malformed.
This method validates both the ref path format according to git ref naming rules and the SHA format (40 hexadecimal characters for direct refs, or valid ref path for symbolic refs starting with "ref:").
Error types returned:
- *ValidationError with detailed context for validation failures
- Wrapped with ErrInvalidRefMetadata for backward compatibility
type TarballContext ¶
type TarballContext struct {
Operation string `json:"operation"` // Operation being performed (parse, materialize, validate)
CommitSHA string `json:"commit_sha,omitempty"` // Commit SHA associated with the tarball (if known)
FilePath string `json:"file_path,omitempty"` // Path to tarball file or git directory (if applicable)
}
TarballContext contains tarball parsing details.
type TarballMember ¶
TarballMember represents a file in the warm-start tarball.
type ValidationError ¶
type ValidationError struct {
// Kind is the category of validation error.
Kind ValidationErrorKind
// Field is the field being validated (e.g., "Path", "SHA").
Field string
// Value is the actual value that failed validation.
Value string
// RefPath is the ref path (if applicable).
RefPath string
// Context provides additional human-readable details.
Context string
}
ValidationError represents a validation error with detailed context.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
Error implements the error interface.
type ValidationErrorKind ¶
type ValidationErrorKind int
ValidationErrorKind represents the category of validation error.
const ( // ValidationErrEmptyPath indicates the ref path is empty. ValidationErrEmptyPath ValidationErrorKind = iota // ValidationErrEmptySHA indicates the SHA is empty. ValidationErrEmptySHA // ValidationErrInvalidSHALength indicates the SHA is not exactly 40 characters. ValidationErrInvalidSHALength // ValidationErrInvalidSHAChars indicates the SHA contains non-hex characters. ValidationErrInvalidSHAChars // ValidationErrNullByteInPath indicates the ref path contains null bytes. ValidationErrNullByteInPath // ValidationErrInvalidRefPath indicates the ref path violates git ref naming rules. ValidationErrInvalidRefPath // ValidationErrEmptySymbolicTarget indicates a symbolic ref has an empty target. ValidationErrEmptySymbolicTarget )
func (ValidationErrorKind) String ¶
func (k ValidationErrorKind) String() string
type WarmStartSnapshot ¶
type WarmStartSnapshot struct {
// PackFiles contains all pack-related files (.pack, .idx, .promisor, .rev).
PackFiles []TarballMember
// RefPath is the ref path (e.g., "refs/heads/main").
RefPath string
// RefSHA is the SHA the ref points to.
RefSHA string
// Config holds the git configuration.
Config Config
}
WarmStartSnapshot represents the extracted warm-start data.
func ParseTarball ¶
func ParseTarball(data []byte, commitSHAs ...string) (_ *WarmStartSnapshot, returnErr error)
ParseTarball extracts and validates a warm-start tarball. When supplied, commitSHAs[0] is attached to every error returned by this entry point.