metadata

package
v1.1.13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2025 License: MIT Imports: 14 Imported by: 0

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

  1. Always use LockManager for operations that modify environments
  2. Defer lock release to ensure cleanup
  3. Set up panic recovery for critical operations
  4. Keep metadata commits atomic (one logical change per commit)
  5. Provide descriptive commit messages for audit trail
  6. Validate metadata after loading
  7. 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

View Source
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

type EnvironmentLockedError struct {
	Environment string
	LockedBy    string
	LockedAt    time.Time
}

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

type InvalidMetadataError struct {
	Reason string
	Err    error
}

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

func NewMetadata(environments []string, baseBranch string, user string) *Metadata

NewMetadata creates a new Metadata structure with defaults

func (*Metadata) AddBranchToEnvironment

func (m *Metadata) AddBranchToEnvironment(env string, branch string, user string) error

AddBranchToEnvironment adds a branch to an environment's feature list (thread-safe)

func (*Metadata) IsEnvironmentLocked

func (m *Metadata) IsEnvironmentLocked(env string) bool

IsEnvironmentLocked checks if an environment is locked

func (*Metadata) IsLockStale

func (m *Metadata) IsLockStale(env string) bool

IsLockStale checks if a lock is older than the timeout (thread-safe)

func (*Metadata) IsLockStaleUnsafe added in v0.1.21

func (m *Metadata) IsLockStaleUnsafe(env string) bool

IsLockStaleUnsafe checks if a lock is older than the timeout (unsafe, assumes mutex held)

func (*Metadata) IsLockedByUser

func (m *Metadata) IsLockedByUser(env string, user string) bool

IsLockedByUser checks if an environment is locked by a specific user

func (*Metadata) LockEnvironment

func (m *Metadata) LockEnvironment(env string, user string, reason string) error

LockEnvironment locks an environment (thread-safe)

func (*Metadata) RemoveBranchFromEnvironment

func (m *Metadata) RemoveBranchFromEnvironment(env string, branch string, user string) error

RemoveBranchFromEnvironment removes a branch from an environment's feature list

func (*Metadata) UnlockEnvironment

func (m *Metadata) UnlockEnvironment(env string) error

UnlockEnvironment unlocks an environment (thread-safe)

func (*Metadata) UnlockEnvironmentUnsafe added in v0.1.21

func (m *Metadata) UnlockEnvironmentUnsafe(env string) error

UnlockEnvironmentUnsafe unlocks an environment without locking (assumes caller holds mutex)

func (*Metadata) UpdateMeta

func (m *Metadata) UpdateMeta(user, command string)

UpdateMeta updates the metadata modification tracking

type MetadataReadError

type MetadataReadError struct {
	Reason string
	Err    error
}

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

type MetadataWriteError struct {
	Reason string
	Err    error
}

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

func (*Reader) Exists

func (r *Reader) Exists() bool

Exists checks if the hitch-metadata branch exists

func (*Reader) Read

func (r *Reader) Read() (*Metadata, error)

Read reads the metadata from the hitch-metadata branch. Returns the Metadata struct containing environment configurations and feature lists. Returns a MetadataReadError if the hitch-metadata branch doesn't exist.

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

func (w *Writer) GetLastCommitHash() string

GetLastCommitHash returns the hash of the last commit written by this writer

func (*Writer) WithAutoPush added in v1.1.10

func (w *Writer) WithAutoPush(enabled bool) *Writer

WithAutoPush configures whether to auto-push metadata changes

func (*Writer) WithPushCallback added in v1.1.10

func (w *Writer) WithPushCallback(callback func(error)) *Writer

WithPushCallback sets a callback to handle push results This allows the caller to log warnings or errors without failing the operation

func (*Writer) Write

func (w *Writer) Write(m *Metadata, commitMessage string, author string, authorEmail string) error

Write writes metadata to the hitch-metadata branch It uses optimistic concurrency control with force-with-lease

func (*Writer) WriteInitial

func (w *Writer) WriteInitial(m *Metadata, author string, authorEmail string) error

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL