Documentation
¶
Overview ¶
Package metadata manages Hitch's metadata storage and operations using Git's orphan branch pattern.
Overview ¶
This package implements Hitch's metadata system, which stores all environment configuration, branch tracking, and lock state in a special Git orphan branch called "hitch-metadata". This approach provides:
- Version-controlled metadata (full audit trail)
- Distributed state (syncs via git push/pull)
- Atomic updates (via Git commits)
- No external database required
Metadata Storage ¶
All metadata is stored in hitch.json on the hitch-metadata orphan branch:
{
"version": "1.0.0",
"environments": {
"dev": {
"base": "main",
"features": ["feature/login", "feature/dashboard"],
"locked": false,
"last_rebuild": "2024-01-15T10:30:00Z"
}
},
"branches": {
"feature/login": {
"created_at": "2024-01-10T09:00:00Z",
"created_by": "user@example.com",
"promoted_to": ["dev", "qa"]
}
},
"config": {
"retention_days_after_merge": 7,
"stale_days_no_activity": 30,
"base_branch": "main"
}
}
Core Components ¶
The package consists of several focused modules:
Metadata Structure (types.go):
- Metadata: Root structure containing all state
- Environment: Per-environment configuration and features
- BranchInfo: Lifecycle tracking for feature branches
- Config: Global configuration settings
Reader/Writer (reader.go, writer.go):
- Reader: Load metadata from hitch-metadata branch
- Writer: Save metadata to hitch-metadata branch
Lock Management (lock_manager.go):
- LockManager: Coordinate environment locks
- Prevent concurrent modifications
- Session-based lock tracking
Recovery Systems (lock_recovery.go, panic_recovery.go, crash_detection.go):
- LockRecovery: Recover from stale locks
- PanicRecovery: Recover from panics and crashes
- CrashDetection: Detect interrupted operations
Reading Metadata ¶
Load metadata from the repository:
import "github.com/DoomedRamen/hitch/internal/metadata"
reader := metadata.NewReader(repo)
// Check if metadata exists
if !reader.Exists() {
return errors.New("hitch not initialized")
}
// Read metadata
meta, err := reader.Read()
if err != nil {
return err
}
// Access environment data
devEnv := meta.Environments["dev"]
fmt.Printf("Dev has %d features\n", len(devEnv.Features))
Writing Metadata ¶
Update and save metadata:
writer := metadata.NewWriter(repo)
// Modify metadata
meta.AddBranchToEnvironment("dev", "feature/login", "user@example.com")
meta.UpdateMeta("user@example.com", "Add feature/login to dev")
// Write back to repository
err := writer.Write(meta, "Add feature/login to dev",
"John Doe", "user@example.com")
if err != nil {
return err
}
The writer:
- Switches to hitch-metadata branch
- Updates hitch.json
- Creates a commit
- Pushes to origin (if configured)
- Switches back to original branch
Environment Management ¶
Environments represent deployment targets (dev, qa, staging, production):
// Add feature to environment
err := meta.AddBranchToEnvironment("dev", "feature/login", "user@example.com")
// Remove feature from environment
err := meta.RemoveBranchFromEnvironment("dev", "feature/login", "user@example.com")
// Get environment features
features := meta.GetEnvironmentFeatures("dev")
// Check if feature is in environment
inDev := meta.IsBranchInEnvironment("dev", "feature/login")
Branch Lifecycle Tracking ¶
Track the complete lifecycle of feature branches:
type BranchInfo struct {
CreatedAt time.Time
CreatedBy string
PromotedTo []string
PromotedHistory []PromotionEvent
MergedToMainAt *time.Time
MergedToMainBy string
LastCommitAt time.Time
LastCommitSHA string
EligibleForCleanupAt *time.Time
}
Each promotion/demotion is tracked in PromotedHistory:
type PromotionEvent struct {
Environment string
PromotedAt time.Time
PromotedBy string
DemotedAt *time.Time
DemotedBy string
}
This provides a complete audit trail of feature movement through environments.
Lock Management ¶
Prevent concurrent modifications using the LockManager:
lockMgr := metadata.NewLockManager(repo, meta)
// Start session (loads current state)
err := lockMgr.StartSession()
if err != nil {
return err
}
defer lockMgr.EndSession()
// Acquire lock
request := metadata.LockRequest{
Environment: "dev",
User: "user@example.com",
Reason: "Rebuilding environment",
Operation: "rebuild",
}
result, err := lockMgr.AcquireLock(request)
if err != nil {
return err
}
defer lockMgr.ReleaseLock("dev", "user@example.com")
// Perform operation while holding lock
// ...
Lock features:
- Session-based: Locks tracked per operation session
- Automatic cleanup: Sessions cleaned up on defer
- Stale detection: Old locks detected and recoverable
- User ownership: Locks tied to specific users
Lock Recovery ¶
Recover from stale or crashed locks:
recovery := metadata.NewLockRecovery(repo)
// Check for stale locks
report, err := recovery.RecoverStaleLocks("user@example.com", false)
if err != nil {
return err
}
fmt.Printf("Recovered %d stale locks\n", report.LocksRecovered)
for _, lock := range report.RecoveredLocks {
fmt.Printf(" - %s (held by %s)\n", lock.Environment, lock.HeldBy)
}
Lock recovery detects:
- Locks held for > 2 hours (stale threshold)
- Locks from crashed operations
- Orphaned session locks
Panic Recovery ¶
Automatic recovery from panics and crashes:
panicRecovery := metadata.NewPanicRecovery(repo) // Set up panic recovery defer panicRecovery.RecoverFromPanic(meta, "promote", "dev")() // If panic occurs, recovery will: // 1. Catch the panic // 2. Release any locks // 3. Restore repository state // 4. Log crash details // 5. Re-panic with context
Crash Detection ¶
Detect interrupted operations:
crashDetector := metadata.NewCrashDetector(repo)
report := crashDetector.DetectCrashedOperations(meta)
if report.HasCrashes {
fmt.Printf("Detected %d crashed operations\n", len(report.CrashedEnvironments))
for _, env := range report.CrashedEnvironments {
fmt.Printf(" - %s: locked by %s\n", env, meta.Environments[env].LockedBy)
}
}
Thread Safety ¶
The Metadata struct includes a sync.RWMutex for thread-safe operations:
meta.mu.Lock() defer meta.mu.Unlock() // Modify metadata safely
All exported methods that modify state are thread-safe.
Metadata Validation ¶
Ensure metadata consistency:
// Check if environment exists
if _, exists := meta.Environments["dev"]; !exists {
return fmt.Errorf("environment 'dev' not found")
}
// Validate branch is tracked
if _, exists := meta.Branches["feature/login"]; !exists {
return fmt.Errorf("branch 'feature/login' not tracked")
}
Orphan Branch Pattern ¶
The hitch-metadata branch is an orphan branch (no parent commits):
- Independent history from main codebase
- Doesn't pollute main branch history
- Can be pushed/pulled separately
- Survives force pushes to main branches
Creating the orphan branch:
writer := metadata.NewWriter(repo) err := writer.CreateOrphanBranch()
Metadata Migrations ¶
Version field supports future migrations:
if meta.Version != expectedVersion {
return fmt.Errorf("metadata version mismatch: got %s, want %s",
meta.Version, expectedVersion)
}
Error Handling ¶
The package defines specific error types in errors.go:
- ErrMetadataNotFound: Metadata branch doesn't exist
- ErrEnvironmentLocked: Environment is locked by another user
- ErrBranchNotFound: Branch not in metadata
- ErrInvalidMetadata: Malformed hitch.json
Testing ¶
Comprehensive test coverage:
- metadata_test.go: Core metadata operations
- reader_writer_test.go: Read/write operations
- lock_manager_test.go: Lock management
- lock_recovery_test.go: Lock recovery
- panic_recovery_test.go: Panic handling
- crash_detection_test.go: Crash detection
Best Practices ¶
- Always use LockManager for operations that modify environments
- Defer lock release to ensure cleanup
- Set up panic recovery for critical operations
- Keep metadata commits atomic (one logical change per commit)
- Provide descriptive commit messages for audit trail
- Validate metadata after loading
- Handle version mismatches gracefully
Example: Complete Operation with All Safety Mechanisms ¶
func PromoteWithSafety(repo *git.Repo, branch, env, user string) error {
// Read metadata
reader := metadata.NewReader(repo)
meta, err := reader.Read()
if err != nil {
return err
}
// Set up lock manager
lockMgr := metadata.NewLockManager(repo, meta)
if err := lockMgr.StartSession(); err != nil {
return err
}
defer lockMgr.EndSession()
// Set up panic recovery
panicRecovery := metadata.NewPanicRecovery(repo)
defer panicRecovery.RecoverFromPanic(meta, "promote", env)()
// Acquire lock
result, err := lockMgr.AcquireLock(metadata.LockRequest{
Environment: env,
User: user,
Reason: "Promoting " + branch,
Operation: "promote",
})
if err != nil {
return err
}
defer lockMgr.ReleaseLock(env, user)
// Perform operation
err = meta.AddBranchToEnvironment(env, branch, user)
if err != nil {
return err
}
// Save metadata
writer := metadata.NewWriter(repo)
meta.UpdateMeta(user, "Promote "+branch+" to "+env)
return writer.Write(meta, "Promote "+branch+" to "+env, "User", user)
}
Index ¶
- Constants
- type BranchInfo
- type BranchNotFoundError
- type Config
- type CrashDetector
- func (cd *CrashDetector) CompleteOperation(opID string) error
- func (cd *CrashDetector) EndSession() error
- func (cd *CrashDetector) GetCrashRecoveryActions() (*CrashRecoveryActions, error)
- func (cd *CrashDetector) Heartbeat() error
- func (cd *CrashDetector) SetEnabled(enabled bool)
- func (cd *CrashDetector) StartOperation(opType, env, user string, context map[string]interface{}) (string, error)
- func (cd *CrashDetector) StartSession() error
- func (cd *CrashDetector) UpdateOperation(opID, status string) error
- type CrashRecoveryActions
- type CrashState
- type Environment
- type EnvironmentLockedError
- type EnvironmentNotFoundError
- type HookConfig
- type HooksConfig
- type InvalidMetadataError
- type LockBackup
- type LockInfo
- type LockManager
- func (lm *LockManager) AcquireLock(req LockRequest) (*LockResult, error)
- func (lm *LockManager) EndSession() error
- func (lm *LockManager) GetStatus() (*LockManagerStatus, error)
- func (lm *LockManager) RecoverLocks(userEmail string, force bool) (*RecoveryReport, error)
- func (lm *LockManager) ReleaseLock(envName, userEmail string) error
- func (lm *LockManager) SetEnabled(enabled bool)
- func (lm *LockManager) StartSession() error
- type LockManagerStatus
- type LockRecoveryConfig
- type LockRecoveryManager
- func (lrm *LockRecoveryManager) CreateBackup() error
- func (lrm *LockRecoveryManager) DetectStaleLocks() ([]string, error)
- func (lrm *LockRecoveryManager) GetBackupInfo() (map[string]interface{}, error)
- func (lrm *LockRecoveryManager) RecoverFromBackup() error
- func (lrm *LockRecoveryManager) RecoverStaleLocks(userEmail string, force bool) ([]string, error)
- func (lrm *LockRecoveryManager) SetConfig(config LockRecoveryConfig)
- func (lrm *LockRecoveryManager) StartAutoBackup()
- func (lrm *LockRecoveryManager) ValidateLockState() error
- type LockRequest
- type LockResult
- type LockState
- type MetaInfo
- type Metadata
- func (m *Metadata) AddBranchToEnvironment(env string, branch string, user string) error
- func (m *Metadata) IsEnvironmentLocked(env string) bool
- func (m *Metadata) IsLockStale(env string) bool
- func (m *Metadata) IsLockStaleUnsafe(env string) bool
- func (m *Metadata) IsLockedByUser(env string, user string) bool
- func (m *Metadata) LockEnvironment(env string, user string, reason string) error
- func (m *Metadata) RemoveBranchFromEnvironment(env string, branch string, user string) error
- func (m *Metadata) UnlockEnvironment(env string) error
- func (m *Metadata) UnlockEnvironmentUnsafe(env string) error
- func (m *Metadata) UpdateMeta(user, command string)
- type MetadataReadError
- type MetadataWriteError
- type Operation
- type PanicInfo
- type PanicRecovery
- func (pr *PanicRecovery) ClearPanicLog()
- func (pr *PanicRecovery) GetPanicLog() []PanicInfo
- func (pr *PanicRecovery) GetRecoveryStats() map[string]interface{}
- func (pr *PanicRecovery) SafeLockOperation(meta *Metadata, envName, userEmail, reason string) error
- func (pr *PanicRecovery) SafeMetadataOperation(operation string, fn func() error) error
- func (pr *PanicRecovery) SafeUnlockOperation(meta *Metadata, envName string) error
- func (pr *PanicRecovery) SetEnabled(enabled bool)
- func (pr *PanicRecovery) SetMaxRecoveries(max int)
- func (pr *PanicRecovery) WithLockRecovery(meta *Metadata, operation string, envName string, fn func() error) (err error)
- func (pr *PanicRecovery) WithRecovery(operation string, context interface{}, fn func() error) (err error)
- type PromotionEvent
- type Reader
- type RebuildState
- type RecoveryReport
- type RecoveryState
- type Webhook
- type Writer
- func (w *Writer) GetLastCommitHash() string
- func (w *Writer) WithAutoPush(enabled bool) *Writer
- func (w *Writer) WithPushCallback(callback func(error)) *Writer
- func (w *Writer) Write(m *Metadata, commitMessage string, author string, authorEmail string) error
- func (w *Writer) WriteInitial(m *Metadata, author string, authorEmail string) error
- func (w *Writer) WriteMetadataWithHooks(m *Metadata, commitMessage string, author string, authorEmail string) error
Constants ¶
const ( MetadataBranch = "hitch-metadata" MetadataFile = "hitch.json" )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type BranchInfo ¶
type BranchInfo struct {
CreatedAt time.Time `json:"created_at"`
CreatedBy string `json:"created_by,omitempty"`
PromotedTo []string `json:"promoted_to"`
PromotedHistory []PromotionEvent `json:"promoted_history,omitempty"`
MergedToMainAt *time.Time `json:"merged_to_main_at,omitempty"`
MergedToMainBy string `json:"merged_to_main_by,omitempty"`
LastCommitAt time.Time `json:"last_commit_at,omitempty"`
LastCommitSHA string `json:"last_commit_sha,omitempty"`
LastRebuildSHA string `json:"last_rebuild_sha,omitempty"` // Commit SHA when last included in a rebuild
EligibleForCleanupAt *time.Time `json:"eligible_for_cleanup_at,omitempty"`
}
BranchInfo tracks the lifecycle of a feature branch
func (*BranchInfo) IsEligibleForCleanup ¶ added in v0.1.5
func (b *BranchInfo) IsEligibleForCleanup() bool
IsEligibleForCleanup checks if a branch is eligible for cleanup
type BranchNotFoundError ¶
type BranchNotFoundError struct {
Branch string
}
BranchNotFoundError is returned when a branch doesn't exist
func (*BranchNotFoundError) Error ¶
func (e *BranchNotFoundError) Error() string
type Config ¶
type Config struct {
RetentionDaysAfterMerge int `json:"retention_days_after_merge"`
StaleDaysNoActivity int `json:"stale_days_no_activity"`
BaseBranch string `json:"base_branch"`
LockTimeoutMinutes int `json:"lock_timeout_minutes"`
AutoRebuildOnPromote bool `json:"auto_rebuild_on_promote"`
ConflictStrategy string `json:"conflict_strategy"`
NotificationWebhooks []Webhook `json:"notification_webhooks,omitempty"`
}
Config holds global configuration
type CrashDetector ¶ added in v0.1.21
type CrashDetector struct {
// contains filtered or unexported fields
}
CrashDetector detects and handles crashes during metadata operations
func NewCrashDetector ¶ added in v0.1.21
func NewCrashDetector(stateFile string) *CrashDetector
NewCrashDetector creates a new crash detector
func (*CrashDetector) CompleteOperation ¶ added in v0.1.21
func (cd *CrashDetector) CompleteOperation(opID string) error
CompleteOperation marks an operation as completed
func (*CrashDetector) EndSession ¶ added in v0.1.21
func (cd *CrashDetector) EndSession() error
EndSession ends the current crash detection session
func (*CrashDetector) GetCrashRecoveryActions ¶ added in v0.1.21
func (cd *CrashDetector) GetCrashRecoveryActions() (*CrashRecoveryActions, error)
GetCrashRecoveryActions returns actions needed to recover from a crash
func (*CrashDetector) Heartbeat ¶ added in v0.1.21
func (cd *CrashDetector) Heartbeat() error
Heartbeat updates the heartbeat timestamp
func (*CrashDetector) SetEnabled ¶ added in v0.1.21
func (cd *CrashDetector) SetEnabled(enabled bool)
SetEnabled enables or disables crash detection
func (*CrashDetector) StartOperation ¶ added in v0.1.21
func (cd *CrashDetector) StartOperation(opType, env, user string, context map[string]interface{}) (string, error)
StartOperation starts tracking an operation
func (*CrashDetector) StartSession ¶ added in v0.1.21
func (cd *CrashDetector) StartSession() error
StartSession starts a new crash detection session
func (*CrashDetector) UpdateOperation ¶ added in v0.1.21
func (cd *CrashDetector) UpdateOperation(opID, status string) error
UpdateOperation updates the status of an operation
type CrashRecoveryActions ¶ added in v0.1.21
type CrashRecoveryActions struct {
SessionID string `json:"session_id"`
CrashTime time.Time `json:"crash_time"`
ProcessID int `json:"process_id"`
StaleEnvs []string `json:"stale_envs"`
FailedOps []*Operation `json:"failed_ops"`
InProgressOps []*Operation `json:"in_progress_ops"`
}
CrashRecoveryActions contains recovery actions after a crash
type CrashState ¶ added in v0.1.21
type CrashState struct {
SessionID string `json:"session_id"`
StartTime time.Time `json:"start_time"`
LastHeartbeat time.Time `json:"last_heartbeat"`
Operations map[string]*Operation `json:"operations"`
LockedEnvs map[string]string `json:"locked_envs"` // env -> user
MetadataHash string `json:"metadata_hash"`
ProcessID int `json:"process_id"`
}
CrashState represents the state of ongoing operations
type Environment ¶
type Environment struct {
Base string `json:"base"`
Features []string `json:"features"`
Locked bool `json:"locked"`
LockedBy string `json:"locked_by,omitempty"`
LockedAt time.Time `json:"locked_at,omitempty"`
LockedReason string `json:"locked_reason,omitempty"`
LastRebuild time.Time `json:"last_rebuild,omitempty"`
LastRebuildCommit string `json:"last_rebuild_commit,omitempty"`
RebuildState *RebuildState `json:"rebuild_state,omitempty"`
RecoveryState *RecoveryState `json:"recovery_state,omitempty"`
}
Environment represents a deployment environment (dev, qa, etc.)
type EnvironmentLockedError ¶
EnvironmentLockedError is returned when an environment is locked by another user
func (*EnvironmentLockedError) Error ¶
func (e *EnvironmentLockedError) Error() string
type EnvironmentNotFoundError ¶
type EnvironmentNotFoundError struct {
Environment string
}
EnvironmentNotFoundError is returned when an environment doesn't exist
func (*EnvironmentNotFoundError) Error ¶
func (e *EnvironmentNotFoundError) Error() string
type HookConfig ¶ added in v0.1.21
type HookConfig struct {
Name string `json:"name"`
Type string `json:"type"` // "builtin" or "command"
Command string `json:"command,omitempty"` // for type=command
WorkingDir string `json:"working_dir,omitempty"` // optional working directory
Args []string `json:"args,omitempty"` // arguments for the command
Config map[string]string `json:"config,omitempty"` // configuration for builtin hooks
Required bool `json:"required"` // whether failure should block operation
}
HookConfig represents a single hook configuration
type HooksConfig ¶ added in v0.1.21
type HooksConfig struct {
PrePromote []HookConfig `json:"pre-promote,omitempty"`
PostPromote []HookConfig `json:"post-promote,omitempty"`
PreRelease []HookConfig `json:"pre-release,omitempty"`
PostRelease []HookConfig `json:"post-release,omitempty"`
PreMetadataWrite []HookConfig `json:"pre-metadata-write,omitempty"` // Before switching to hitch-metadata branch
PostMetadataWrite []HookConfig `json:"post-metadata-write,omitempty"` // After returning from hitch-metadata branch
PreRebuild []HookConfig `json:"pre-rebuild,omitempty"` // Before starting rebuild
PostRebuild []HookConfig `json:"post-rebuild,omitempty"` // After completing rebuild
ConflictDetected []HookConfig `json:"conflict-detected,omitempty"` // When merge conflicts detected
ResumeRebuild []HookConfig `json:"resume-rebuild,omitempty"` // When resuming failed rebuild
}
HooksConfig represents hook configuration (avoiding circular import)
type InvalidMetadataError ¶
InvalidMetadataError is returned when metadata format is invalid
func (*InvalidMetadataError) Error ¶
func (e *InvalidMetadataError) Error() string
func (*InvalidMetadataError) Unwrap ¶
func (e *InvalidMetadataError) Unwrap() error
type LockBackup ¶ added in v0.1.21
type LockBackup struct {
Timestamp time.Time `json:"timestamp"`
Locks map[string]LockState `json:"locks"`
Config LockRecoveryConfig `json:"config"`
}
LockBackup represents a backup of lock state for recovery
type LockInfo ¶ added in v0.1.21
type LockInfo struct {
LockedBy string `json:"locked_by"`
LockedAt time.Time `json:"locked_at"`
Reason string `json:"reason,omitempty"`
IsStale bool `json:"is_stale"`
}
LockInfo contains information about a lock
type LockManager ¶ added in v0.1.21
type LockManager struct {
// contains filtered or unexported fields
}
LockManager provides comprehensive lock management with recovery capabilities
func NewLockManager ¶ added in v0.1.21
func NewLockManager(meta *Metadata, workingDir string) *LockManager
NewLockManager creates a new lock manager
func (*LockManager) AcquireLock ¶ added in v0.1.21
func (lm *LockManager) AcquireLock(req LockRequest) (*LockResult, error)
AcquireLock acquires a lock for an environment with full recovery support
func (*LockManager) EndSession ¶ added in v0.1.21
func (lm *LockManager) EndSession() error
EndSession ends the current lock management session
func (*LockManager) GetStatus ¶ added in v0.1.21
func (lm *LockManager) GetStatus() (*LockManagerStatus, error)
GetStatus returns the status of the lock manager
func (*LockManager) RecoverLocks ¶ added in v0.1.21
func (lm *LockManager) RecoverLocks(userEmail string, force bool) (*RecoveryReport, error)
RecoverLocks attempts to recover from crash or stale locks
func (*LockManager) ReleaseLock ¶ added in v0.1.21
func (lm *LockManager) ReleaseLock(envName, userEmail string) error
ReleaseLock releases a lock for an environment with full recovery support
func (*LockManager) SetEnabled ¶ added in v0.1.21
func (lm *LockManager) SetEnabled(enabled bool)
SetEnabled enables or disables the lock manager
func (*LockManager) StartSession ¶ added in v0.1.21
func (lm *LockManager) StartSession() error
StartSession starts a new lock management session
type LockManagerStatus ¶ added in v0.1.21
type LockManagerStatus struct {
Enabled bool `json:"enabled"`
SessionActive bool `json:"session_active"`
CurrentLocks map[string]LockInfo `json:"current_locks"`
BackupInfo map[string]interface{} `json:"backup_info,omitempty"`
PanicRecoveryStats map[string]interface{} `json:"panic_recovery_stats,omitempty"`
}
LockManagerStatus contains the status of the lock manager
type LockRecoveryConfig ¶ added in v0.1.21
type LockRecoveryConfig struct {
BackupInterval time.Duration `json:"backup_interval"`
MaxBackups int `json:"max_backups"`
StaleLockTimeout time.Duration `json:"stale_lock_timeout"`
CrashDetection bool `json:"crash_detection"`
AutoRecovery bool `json:"auto_recovery"`
}
LockRecoveryConfig contains configuration for lock recovery
type LockRecoveryManager ¶ added in v0.1.21
type LockRecoveryManager struct {
// contains filtered or unexported fields
}
LockRecoveryManager handles lock recovery operations
func NewLockRecoveryManager ¶ added in v0.1.21
func NewLockRecoveryManager(meta *Metadata, backupDir string) *LockRecoveryManager
NewLockRecoveryManager creates a new lock recovery manager
func (*LockRecoveryManager) CreateBackup ¶ added in v0.1.21
func (lrm *LockRecoveryManager) CreateBackup() error
CreateBackup creates a backup of current lock state
func (*LockRecoveryManager) DetectStaleLocks ¶ added in v0.1.21
func (lrm *LockRecoveryManager) DetectStaleLocks() ([]string, error)
DetectStaleLocks identifies locks that may be stale due to crashes
func (*LockRecoveryManager) GetBackupInfo ¶ added in v0.1.21
func (lrm *LockRecoveryManager) GetBackupInfo() (map[string]interface{}, error)
GetBackupInfo returns information about existing backups
func (*LockRecoveryManager) RecoverFromBackup ¶ added in v0.1.21
func (lrm *LockRecoveryManager) RecoverFromBackup() error
RecoverFromBackup attempts to recover lock state from the latest backup
func (*LockRecoveryManager) RecoverStaleLocks ¶ added in v0.1.21
func (lrm *LockRecoveryManager) RecoverStaleLocks(userEmail string, force bool) ([]string, error)
RecoverStaleLocks attempts to recover from stale locks
func (*LockRecoveryManager) SetConfig ¶ added in v0.1.21
func (lrm *LockRecoveryManager) SetConfig(config LockRecoveryConfig)
SetConfig updates the lock recovery configuration
func (*LockRecoveryManager) StartAutoBackup ¶ added in v0.1.21
func (lrm *LockRecoveryManager) StartAutoBackup()
StartAutoBackup starts automatic backup goroutine
func (*LockRecoveryManager) ValidateLockState ¶ added in v0.1.21
func (lrm *LockRecoveryManager) ValidateLockState() error
ValidateLockState checks for lock consistency issues
type LockRequest ¶ added in v0.1.21
type LockRequest struct {
Environment string
User string
Reason string
Timeout time.Duration
Force bool
}
LockRequest represents a lock acquisition request
type LockResult ¶ added in v0.1.21
type LockResult struct {
Success bool
Message string
LockAcquired bool
WasStale bool
RecoveredFrom []string
}
LockResult represents the result of a lock operation
type LockState ¶ added in v0.1.21
type LockState struct {
Environment string `json:"environment"`
Locked bool `json:"locked"`
LockedBy string `json:"locked_by,omitempty"`
LockedAt time.Time `json:"locked_at,omitempty"`
Reason string `json:"reason,omitempty"`
}
LockState represents the state of a single lock
type MetaInfo ¶
type MetaInfo struct {
InitializedAt time.Time `json:"initialized_at"`
InitializedBy string `json:"initialized_by,omitempty"`
LastModifiedAt time.Time `json:"last_modified_at"`
LastModifiedBy string `json:"last_modified_by,omitempty"`
LastCommand string `json:"last_command,omitempty"`
HitchVersion string `json:"hitch_version"`
}
MetaInfo contains metadata about the metadata itself
type Metadata ¶
type Metadata struct {
Version string `json:"version"`
Environments map[string]Environment `json:"environments"`
Branches map[string]BranchInfo `json:"branches"`
Config Config `json:"config"`
Hooks *HooksConfig `json:"hooks,omitempty"`
Meta MetaInfo `json:"metadata"`
Recovery *RecoveryState `json:"recovery,omitempty"`
// contains filtered or unexported fields
}
Metadata represents the complete hitch.json structure
func NewMetadata ¶
NewMetadata creates a new Metadata structure with defaults
func (*Metadata) AddBranchToEnvironment ¶
AddBranchToEnvironment adds a branch to an environment's feature list (thread-safe)
func (*Metadata) IsEnvironmentLocked ¶
IsEnvironmentLocked checks if an environment is locked
func (*Metadata) IsLockStale ¶
IsLockStale checks if a lock is older than the timeout (thread-safe)
func (*Metadata) IsLockStaleUnsafe ¶ added in v0.1.21
IsLockStaleUnsafe checks if a lock is older than the timeout (unsafe, assumes mutex held)
func (*Metadata) IsLockedByUser ¶
IsLockedByUser checks if an environment is locked by a specific user
func (*Metadata) LockEnvironment ¶
LockEnvironment locks an environment (thread-safe)
func (*Metadata) RemoveBranchFromEnvironment ¶
RemoveBranchFromEnvironment removes a branch from an environment's feature list
func (*Metadata) UnlockEnvironment ¶
UnlockEnvironment unlocks an environment (thread-safe)
func (*Metadata) UnlockEnvironmentUnsafe ¶ added in v0.1.21
UnlockEnvironmentUnsafe unlocks an environment without locking (assumes caller holds mutex)
func (*Metadata) UpdateMeta ¶
UpdateMeta updates the metadata modification tracking
type MetadataReadError ¶
MetadataReadError is returned when metadata cannot be read
func (*MetadataReadError) Error ¶
func (e *MetadataReadError) Error() string
func (*MetadataReadError) Unwrap ¶
func (e *MetadataReadError) Unwrap() error
type MetadataWriteError ¶
MetadataWriteError is returned when metadata cannot be written
func (*MetadataWriteError) Error ¶
func (e *MetadataWriteError) Error() string
func (*MetadataWriteError) Unwrap ¶
func (e *MetadataWriteError) Unwrap() error
type Operation ¶ added in v0.1.21
type Operation struct {
ID string `json:"id"`
Type string `json:"type"` // lock, unlock, promote, demote, rebuild, cleanup
Environment string `json:"environment,omitempty"`
User string `json:"user"`
StartTime time.Time `json:"start_time"`
Status string `json:"status"` // starting, in_progress, completing, completed, failed
Context map[string]interface{} `json:"context,omitempty"`
}
Operation represents an ongoing operation
type PanicInfo ¶ added in v0.1.21
type PanicInfo struct {
Timestamp time.Time `json:"timestamp"`
Message string `json:"message"`
Stack []string `json:"stack"`
Operation string `json:"operation"`
Context interface{} `json:"context,omitempty"`
}
PanicInfo contains information about a recovered panic
type PanicRecovery ¶ added in v0.1.21
type PanicRecovery struct {
// contains filtered or unexported fields
}
PanicRecovery handles panic recovery for metadata operations
func NewPanicRecovery ¶ added in v0.1.21
func NewPanicRecovery() *PanicRecovery
NewPanicRecovery creates a new panic recovery handler
func (*PanicRecovery) ClearPanicLog ¶ added in v0.1.21
func (pr *PanicRecovery) ClearPanicLog()
ClearPanicLog clears the panic log
func (*PanicRecovery) GetPanicLog ¶ added in v0.1.21
func (pr *PanicRecovery) GetPanicLog() []PanicInfo
GetPanicLog returns the panic log
func (*PanicRecovery) GetRecoveryStats ¶ added in v0.1.21
func (pr *PanicRecovery) GetRecoveryStats() map[string]interface{}
GetRecoveryStats returns recovery statistics
func (*PanicRecovery) SafeLockOperation ¶ added in v0.1.21
func (pr *PanicRecovery) SafeLockOperation(meta *Metadata, envName, userEmail, reason string) error
SafeLockOperation performs a lock operation with panic recovery
func (*PanicRecovery) SafeMetadataOperation ¶ added in v0.1.21
func (pr *PanicRecovery) SafeMetadataOperation(operation string, fn func() error) error
SafeMetadataOperation performs a metadata operation with panic recovery
func (*PanicRecovery) SafeUnlockOperation ¶ added in v0.1.21
func (pr *PanicRecovery) SafeUnlockOperation(meta *Metadata, envName string) error
SafeUnlockOperation performs an unlock operation with panic recovery
func (*PanicRecovery) SetEnabled ¶ added in v0.1.21
func (pr *PanicRecovery) SetEnabled(enabled bool)
SetEnabled enables or disables panic recovery
func (*PanicRecovery) SetMaxRecoveries ¶ added in v0.1.21
func (pr *PanicRecovery) SetMaxRecoveries(max int)
SetMaxRecoveries sets the maximum number of recoveries allowed
func (*PanicRecovery) WithLockRecovery ¶ added in v0.1.21
func (pr *PanicRecovery) WithLockRecovery(meta *Metadata, operation string, envName string, fn func() error) (err error)
WithLockRecovery executes a function with panic recovery and automatic lock cleanup
func (*PanicRecovery) WithRecovery ¶ added in v0.1.21
func (pr *PanicRecovery) WithRecovery(operation string, context interface{}, fn func() error) (err error)
WithRecovery executes a function with panic recovery
type PromotionEvent ¶
type PromotionEvent struct {
Environment string `json:"environment"`
PromotedAt time.Time `json:"promoted_at"`
PromotedBy string `json:"promoted_by,omitempty"`
DemotedAt *time.Time `json:"demoted_at,omitempty"`
DemotedBy string `json:"demoted_by,omitempty"`
}
PromotionEvent records a single promotion/demotion event
type Reader ¶
type Reader struct {
// contains filtered or unexported fields
}
Reader handles reading metadata from the hitch-metadata branch
func NewReader ¶
func NewReader(repo *git.Repository) *Reader
NewReader creates a new metadata reader
type RebuildState ¶ added in v1.1.6
type RebuildState struct {
EnvironmentName string `json:"environment_name"`
StartedAt time.Time `json:"started_at"`
StartedBy string `json:"started_by"`
FailedAt time.Time `json:"failed_at"`
FailedFeature string `json:"failed_feature,omitempty"`
ConflictFiles []string `json:"conflict_files,omitempty"`
SuccessfulFeatures []string `json:"successful_features,omitempty"`
RemainingFeatures []string `json:"remaining_features,omitempty"`
TempBranch string `json:"temp_branch,omitempty"`
Failed bool `json:"failed"`
Interactive bool `json:"interactive"`
}
RebuildState tracks the state of a failed rebuild for resuming
type RecoveryReport ¶ added in v0.1.21
type RecoveryReport struct {
Timestamp time.Time `json:"timestamp"`
RecoveredLocks []string `json:"recovered_locks"`
FailedRecovers []string `json:"failed_recovers"`
ValidatedLocks []string `json:"validated_locks"`
IssuesFound []string `json:"issues_found"`
}
RecoveryReport contains the results of a lock recovery operation
type RecoveryState ¶ added in v1.1.8
type RecoveryState struct {
Operation string `json:"operation"` // promote, rebuild, release, demote
Environment string `json:"environment"` // dev, qa, etc.
Feature string `json:"feature,omitempty"` // feature branch name
StartedAt time.Time `json:"started_at"`
StartedBy string `json:"started_by"`
FailedAt time.Time `json:"failed_at"`
Error string `json:"error"` // Error message
OriginalBranch string `json:"original_branch"` // Branch we were on when operation failed
RecoveryActions []string `json:"recovery_actions"` // Recommended recovery actions
CompletedActions []string `json:"completed_actions"` // Actions already completed
CanResume bool `json:"can_resume"` // Whether this operation can be resumed
NeedsManual bool `json:"needs_manual"` // Whether manual intervention is needed
ResumeCommand string `json:"resume_command"` // Command to resume
}
RecoveryState tracks the state of any failed operation for intelligent recovery
type Webhook ¶
type Webhook struct {
URL string `json:"url"`
Events []string `json:"events"`
Headers map[string]string `json:"headers,omitempty"`
}
Webhook represents a notification webhook configuration
type Writer ¶
type Writer struct {
// contains filtered or unexported fields
}
Writer handles writing metadata to the hitch-metadata branch
func NewWriter ¶
func NewWriter(repo *git.Repository) *Writer
NewWriter creates a new metadata writer with auto-push enabled by default
func (*Writer) GetLastCommitHash ¶ added in v0.1.21
GetLastCommitHash returns the hash of the last commit written by this writer
func (*Writer) WithAutoPush ¶ added in v1.1.10
WithAutoPush configures whether to auto-push metadata changes
func (*Writer) WithPushCallback ¶ added in v1.1.10
WithPushCallback sets a callback to handle push results This allows the caller to log warnings or errors without failing the operation
func (*Writer) Write ¶
Write writes metadata to the hitch-metadata branch It uses optimistic concurrency control with force-with-lease
func (*Writer) WriteInitial ¶
WriteInitial creates the hitch-metadata branch and writes initial metadata
func (*Writer) WriteMetadataWithHooks ¶ added in v1.1.6
func (w *Writer) WriteMetadataWithHooks(m *Metadata, commitMessage string, author string, authorEmail string) error
WriteMetadataWithHooks writes metadata with hooks executed by the caller This allows the cmd package to handle hook execution without circular imports