interfaces

package
v1.1.9 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2025 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package interfaces defines the core interfaces for Hitch's dependency injection and testability architecture.

Overview

This package contains interface definitions that enable:

  • Dependency injection throughout the codebase
  • Easy mocking for unit tests
  • Decoupling between packages
  • Flexibility to swap implementations

By programming to interfaces rather than concrete types, Hitch achieves:

  • Better testability (can mock any dependency)
  • Cleaner separation of concerns
  • Easier refactoring and maintenance
  • Support for alternative implementations

Core Interfaces

The package defines interfaces for all major Hitch components:

Repository Interface:

type Repository interface {
    CurrentBranch() (string, error)
    CurrentCommitSHA() (string, error)
    Checkout(ref string) error
    BranchExists(name string) bool
    CreateBranch(name string, fromRef string) error
    DeleteBranch(name string, force bool) error
    Merge(branch string, message string) error
    // ... and more
}

This interface abstracts Git operations, allowing tests to use mock repositories instead of real Git repositories.

Interface Categories

Interfaces are organized by functionality:

Git Operations:

  • Repository: Core Git repository operations
  • RepositoryValidator: Repository validation and health checks

Safety and Testing:

  • TempBranchTester: Safe merge testing in temporary branches

Metadata Management:

  • MetadataReader: Reading hitch.json and metadata
  • MetadataWriter: Writing and updating metadata
  • LockManager: Environment locking and concurrency control

Hook System:

  • HookExecutor: Executing pre/post operation hooks

Usage Patterns

Using interfaces for dependency injection:

// Function accepts interface, not concrete type
func PromoteBranch(repo interfaces.Repository, meta interfaces.MetadataWriter) error {
    // Implementation works with any type that satisfies the interface
    currentBranch, err := repo.CurrentBranch()
    if err != nil {
        return err
    }
    // ... rest of implementation
}

// Production code uses real implementation
repo, _ := git.OpenRepo(".")
writer := metadata.NewWriter(repo)
err := PromoteBranch(repo, writer)

// Test code uses mocks
mockRepo := &MockRepository{...}
mockWriter := &MockMetadataWriter{...}
err := PromoteBranch(mockRepo, mockWriter)

Repository Interface

The Repository interface defines all Git operations needed by Hitch:

Branch operations:

CurrentBranch() (string, error)
BranchExists(name string) bool
CreateBranch(name string, fromRef string) error
DeleteBranch(name string, force bool) error

Checkout and navigation:

Checkout(ref string) error
CurrentCommitSHA() (string, error)

Merge operations:

Merge(branch string, message string) error
HasUncommittedChanges(branch string) (bool, error)

User information:

UserName() (string, error)
UserEmail() (string, error)

Repository state:

WorkingTree() string
IsGitRepository() bool

TempBranchTester Interface

Defines the interface for safe merge testing:

type TempBranchTester interface {
    TestMerge(ctx context.Context, config safety.TestConfig) safety.TestResult
}

This is the core safety mechanism - all operations are tested in temporary branches before being applied to actual environment branches.

LockManager Interface

Manages environment locks for concurrency control:

type LockManager interface {
    StartSession() error
    EndSession()
    AcquireLock(req metadata.LockRequest) (metadata.LockResult, error)
    ReleaseLock(environment, user string) error
    IsLocked(environment string) bool
    RecoverLocks(user string, force bool) (metadata.RecoveryReport, error)
}

Prevents race conditions when multiple users operate on the same environment.

MetadataReader and MetadataWriter

Separate interfaces for reading and writing metadata:

type MetadataReader interface {
    Read() (*metadata.Metadata, error)
    Exists() bool
}

type MetadataWriter interface {
    Write(meta *metadata.Metadata) error
    CreateOrphanBranch() error
}

This separation follows the Interface Segregation Principle - code that only reads metadata doesn't get write capabilities.

HookExecutor Interface

Defines hook execution capabilities:

type HookExecutor interface {
    ExecuteHooks(ctx context.Context, hookType hooks.HookType,
                 hookCtx hooks.HookContext) []hooks.HookResult
}

Allows custom hook execution logic while maintaining a consistent interface.

Testing Benefits

Interfaces enable comprehensive testing without real Git operations:

type MockRepository struct {
    CurrentBranchFunc func() (string, error)
    CheckoutFunc func(ref string) error
    // ... other functions
}

func (m *MockRepository) CurrentBranch() (string, error) {
    if m.CurrentBranchFunc != nil {
        return m.CurrentBranchFunc()
    }
    return "main", nil
}

// In tests
mockRepo := &MockRepository{
    CurrentBranchFunc: func() (string, error) {
        return "feature/test", nil
    },
}

Implementation Requirements

Types implementing these interfaces must:

  • Handle all documented error cases
  • Be thread-safe where indicated
  • Follow the documented contracts
  • Return meaningful error messages

Interface Evolution

When adding methods to interfaces:

  1. Consider backwards compatibility
  2. Add new methods to end of interface
  3. Update all implementations
  4. Update documentation
  5. Consider creating new interface if major changes needed

Real Implementations

Concrete implementations of these interfaces:

  • Repository: git.Repo (internal/git/repo.go)
  • RepositoryValidator: git.RepositoryValidator (internal/git/validator.go)
  • TempBranchTester: safety.TempBranchTester (internal/safety/temp_branch.go)
  • MetadataReader: metadata.Reader (internal/metadata/reader.go)
  • MetadataWriter: metadata.Writer (internal/metadata/writer.go)
  • LockManager: metadata.LockManager (internal/metadata/lock_manager.go)
  • HookExecutor: hooks.HookExecutor (internal/hooks/types.go)

Design Principles

The interfaces follow these design principles:

  • Single Responsibility: Each interface has one clear purpose
  • Interface Segregation: Clients depend only on methods they use
  • Dependency Inversion: Depend on abstractions, not concretions
  • Minimal Surface Area: Interfaces expose only necessary methods

Example: Full Dependency Injection

type PromoteService struct {
    repo         interfaces.Repository
    metaReader   interfaces.MetadataReader
    metaWriter   interfaces.MetadataWriter
    tempTester   interfaces.TempBranchTester
    lockManager  interfaces.LockManager
    hookExecutor interfaces.HookExecutor
}

func NewPromoteService(
    repo interfaces.Repository,
    metaReader interfaces.MetadataReader,
    metaWriter interfaces.MetadataWriter,
    tempTester interfaces.TempBranchTester,
    lockManager interfaces.LockManager,
    hookExecutor interfaces.HookExecutor,
) *PromoteService {
    return &PromoteService{
        repo: repo,
        metaReader: metaReader,
        metaWriter: metaWriter,
        tempTester: tempTester,
        lockManager: lockManager,
        hookExecutor: hookExecutor,
    }
}

func (s *PromoteService) Promote(branch, environment string) error {
    // Use all dependencies through their interfaces
    // Easy to test with mocks
    // Easy to swap implementations
}

Best Practices

When working with interfaces:

  1. Accept interfaces, return structs (in most cases)
  2. Keep interfaces small and focused
  3. Define interfaces in the package that uses them
  4. Don't create interfaces until you need them (YAGNI)
  5. Use embedding for interface composition when appropriate

Dependencies

This package depends on:

  • internal/git (for git.ValidationResult)
  • internal/metadata (for metadata types)
  • internal/safety (for safety types)
  • internal/hooks (for hook types)

However, it only uses types, not implementations, avoiding circular dependencies.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type HookExecutor

type HookExecutor interface {
	ExecuteHooks(ctx context.Context, hookType hooks.HookType, hookCtx hooks.HookContext) []hooks.HookResult
}

HookExecutor defines the interface for executing hooks

type LockManager

type LockManager interface {
	StartSession() error
	EndSession()
	AcquireLock(req metadata.LockRequest) (metadata.LockResult, error)
	ReleaseLock(environment, user string) error
	IsLocked(environment string) bool
	RecoverLocks(user string, force bool) (metadata.RecoveryReport, error)
}

LockManager defines the interface for managing environment locks

type MetadataReader

type MetadataReader interface {
	Read() (*metadata.Metadata, error)
	Exists() bool
}

MetadataReader defines the interface for reading metadata

type MetadataWriter

type MetadataWriter interface {
	Write(meta *metadata.Metadata) error
	CreateOrphanBranch() error
}

MetadataWriter defines the interface for writing metadata

type Repository

type Repository interface {
	// Basic repository operations
	CurrentBranch() (string, error)
	CurrentCommitSHA() (string, error)
	Checkout(ref string) error
	BranchExists(name string) bool
	CreateBranch(name string, fromRef string) error
	DeleteBranch(name string, force bool) error

	// Branch operations
	GetBranches() ([]string, error)
	GetBranchesMatching(pattern string) ([]string, error)

	// Git operations
	Merge(branch string, message string) error
	UserName() (string, error)
	UserEmail() (string, error)
	HasUncommittedChanges(branch string) (bool, error)
	WorkingTree() string
	IsGitRepository() bool
}

Repository defines the interface for Git repository operations This interface allows for better testability and flexibility

type RepositoryValidator

type RepositoryValidator interface {
	ValidateRepository() git.ValidationResult
}

RepositoryValidator defines the interface for repository validation

type TempBranchTester

type TempBranchTester interface {
	TestMerge(ctx context.Context, config safety.TestConfig) safety.TestResult
}

TempBranchTester defines the interface for testing merge operations safely

Jump to

Keyboard shortcuts

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