safety

package
v0.2.2 Latest Latest
Warning

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

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

Documentation

Overview

Package safety provides the core safety mechanism for Hitch operations through temporary branch testing.

Overview

This package implements Hitch's fundamental safety pattern: testing all operations in temporary branches before applying them to actual environment branches. This prevents failed merges, conflicts, and broken states from affecting production environments.

The safety mechanism is implemented by TempBranchTester, which is used by all Hitch operations that modify repository state (promote, release, rebuild).

Safety Pattern

Every potentially dangerous operation follows this pattern:

  1. Note the current branch
  2. Create a temporary test branch from the target base
  3. Attempt the merge/operation in the temporary branch
  4. Run hooks (if configured)
  5. If successful: proceed with actual operation
  6. If failed: abort and report errors
  7. Always: cleanup temp branch and return to original branch

This ensures the repository is never left in a broken state.

Core Type

The main type is TempBranchTester:

type TempBranchTester struct {
    repo *git.Repo
}

Create a tester for any repository:

tester := safety.NewTempBranchTester(repo)

TestMerge Operation

The primary operation is TestMerge, which safely tests merging a source branch into a target:

config := safety.TestConfig{
    SourceBranch: "feature/login",
    TargetBranch: "dev",
    BaseBranch:   "main",
    Operation:    "promote",
    Context:      "dev",
}

result := tester.TestMerge(ctx, config)
if !result.Success {
    return fmt.Errorf("merge test failed: %v", result.Error)
}

// Safe to proceed with actual operation
// ...

Test Configuration

TestConfig specifies all parameters for the safety test:

type TestConfig struct {
    // Source branch to merge (e.g., feature/login)
    SourceBranch string

    // Target branch to merge into (e.g., dev)
    TargetBranch string

    // Base branch for creating temp branch (usually same as target or target's base)
    BaseBranch string

    // Operation type for naming temp branch (promote, release, rebuild)
    Operation string

    // Hook executor to run during testing (optional)
    HookExecutor *hooks.HookExecutor

    // Hook context to pass to hooks (optional)
    HookContext hooks.HookContext

    // Hook type to run during testing (e.g., hooks.PrePromote)
    HookType hooks.HookType

    // Additional context for error messages (e.g., environment name)
    Context string
}

Test Results

TestResult indicates success or failure:

type TestResult struct {
    Success bool    // Whether the test succeeded
    Error   error   // Error if test failed
    Message string  // Additional information
}

Check results and proceed accordingly:

result := tester.TestMerge(ctx, config)
if result.Success {
    fmt.Println("✓ Merge test passed - safe to proceed")
} else {
    fmt.Printf("✗ Merge test failed: %v\n", result.Error)
    return result.Error
}

Temporary Branch Naming

Temporary branches follow a consistent naming pattern:

{BaseBranch}-hitch-{Operation}-test

Examples:

  • main-hitch-promote-test (testing promote to main)
  • dev-hitch-release-test (testing release from dev)
  • main-hitch-rebuild-test (testing rebuild of main-based env)

This makes temp branches easy to identify and cleanup.

Complete Test Flow

The TestMerge operation performs these steps:

  1. Pre-flight validation: - Check for unstaged changes (blocks if found) - Validate repository state

  2. State preservation: - Note current branch - Store working directory state

  3. Temp branch creation: - Create temp branch from base branch - Checkout temp branch

  4. Merge testing: - Attempt to merge source branch - Check for conflicts - Verify merge succeeded

  5. Hook execution (if configured): - Execute pre-operation hooks - Check hook results - Block if required hooks fail

  6. Cleanup: - Return to original branch - Delete temp branch - Restore working state

  7. Return result: - Report success or failure - Include descriptive error messages

Safety Guarantees

The safety mechanism guarantees:

  • Original branch is never modified during testing
  • Temp branches are always cleaned up (even on panic)
  • Repository is restored to original state
  • No changes persisted if test fails
  • Clear error messages for all failure cases

Error Handling

Comprehensive error detection for:

Unstaged changes:

Error: "Cannot {operation} with uncommitted changes"
Provides helpful guidance for resolving

Merge conflicts:

Error: "Merge conflict detected when merging {source} into {target}"
Lists conflicting files

Hook failures:

Error: "Required hook '{hook_name}' failed"
Includes hook output

Repository errors:

Error: "Failed to create temp branch: {reason}"
Detailed error context

Hook Integration

TestMerge integrates with the hook system:

hookExecutor := hooks.NewHookExecutor(hooksConfig)
hookContext := hooks.HookContext{
    Branch:      "feature/login",
    Environment: "dev",
    UserEmail:   "user@example.com",
    UserName:    "John Doe",
    BaseBranch:  "main",
}

config := safety.TestConfig{
    SourceBranch: "feature/login",
    TargetBranch: "dev",
    BaseBranch:   "main",
    Operation:    "promote",
    HookExecutor: hookExecutor,
    HookContext:  hookContext,
    HookType:     hooks.PrePromote,
}

result := tester.TestMerge(ctx, config)

Hooks are executed AFTER the merge succeeds but BEFORE cleanup, ensuring:

  • Hooks run in the merged state
  • Hook failures prevent actual operation
  • Temp branch includes hook results

Context and Cancellation

TestMerge respects context cancellation:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()

result := tester.TestMerge(ctx, config)

If context is cancelled:

  • Current operation is stopped
  • Temp branch is cleaned up
  • Original branch is restored
  • Error indicates cancellation

Usage in Commands

All commands use TempBranchTester before modifying branches:

Promote command:

tester := safety.NewTempBranchTester(repo)
config := safety.TestConfig{
    SourceBranch: featureBranch,
    TargetBranch: envBranch,
    BaseBranch:   baseBranch,
    Operation:    "promote",
    Context:      environment,
}

result := tester.TestMerge(ctx, config)
if !result.Success {
    return result.Error
}

// Now safe to actually promote
// ...

Release command:

config := safety.TestConfig{
    SourceBranch: envBranch,
    TargetBranch: "main",
    BaseBranch:   "main",
    Operation:    "release",
    Context:      environment,
}

result := tester.TestMerge(ctx, config)
if !result.Success {
    return fmt.Errorf("release blocked: %v", result.Error)
}

Testing

The package includes comprehensive tests:

  • temp_branch_test.go: Core functionality tests
  • temp_branch_edge_cases_test.go: Edge cases and error paths

Tests verify:

  • Successful merge scenarios
  • Conflict detection
  • Cleanup on success
  • Cleanup on failure
  • Hook execution
  • Hook failure handling
  • Branch restoration
  • Long branch names
  • Special characters in names
  • Sequential operations

All tests use isolated Docker environments (dockertest build tag).

Performance Considerations

TempBranchTester is fast because:

  • Uses local git operations (no network)
  • Creates branches (cheap in git)
  • Doesn't copy files
  • Minimal overhead compared to actual operations

Typical test time: 50-200ms for simple merges

Best Practices

  1. Always test before modifying environment branches
  2. Include hooks in test configuration when applicable
  3. Use descriptive Operation and Context values for clarity
  4. Handle test failures gracefully with user-friendly messages
  5. Respect context cancellation for long-running operations
  6. Log test results for debugging and audit trails

Example: Complete Safe Operation

func SafePromote(repo *git.Repo, feature, env string) error {
    // Create safety tester
    tester := safety.NewTempBranchTester(repo)

    // Configure test
    config := safety.TestConfig{
        SourceBranch: feature,
        TargetBranch: env,
        BaseBranch:   "main",
        Operation:    "promote",
        Context:      env,
    }

    // Test the merge
    fmt.Printf("Testing merge of %s into %s...\n", feature, env)
    result := tester.TestMerge(context.Background(), config)

    if !result.Success {
        return fmt.Errorf("safety test failed: %w", result.Error)
    }

    fmt.Println("✓ Safety test passed")

    // Now safe to proceed with actual operation
    err := actuallyPromote(repo, feature, env)
    if err != nil {
        return fmt.Errorf("promote failed: %w", err)
    }

    fmt.Println("✓ Promoted successfully")
    return nil
}

This pattern ensures operations never break the repository, making Hitch safe to use even with complex branch structures and merge scenarios.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type TempBranchTester

type TempBranchTester struct {
	// contains filtered or unexported fields
}

TempBranchTester provides reusable temp branch testing functionality

func NewTempBranchTester

func NewTempBranchTester(repo *hitchgit.Repo) *TempBranchTester

NewTempBranchTester creates a new temp branch tester for safe merge testing. The tester performs all operations in temporary branches to ensure the original repository state is preserved. This is the core safety mechanism for all Hitch operations.

func (*TempBranchTester) TestMerge

func (t *TempBranchTester) TestMerge(ctx context.Context, config TestConfig) TestResult

TestMerge performs the complete temp branch testing according to your safety pattern: 1. CRITICAL: Pre-flight validation before any operations 2. Takes note of pre-command branch 3. Makes a clone branch of the target with unique name 4. Tries merging source into cloned target 5. Checks if successful (no errors and hooks pass) 6. If failed: returns to pre-command branch, deletes cloned target 7. If successful: returns to pre-command branch, deletes cloned target SAFETY: Original repository is never modified during testing

type TestConfig

type TestConfig struct {
	// Source branch to merge (e.g., feature branch)
	SourceBranch string
	// Target branch to merge into (e.g., environment branch)
	TargetBranch string
	// Base branch for creating temp branch (usually same as target or target's base)
	BaseBranch string
	// Operation type for naming (e.g., "promote", "release", "rebuild")
	Operation string
	// Hook executor to run during testing (optional)
	HookExecutor *hooks.HookExecutor
	// Hook context to pass to hooks (optional)
	HookContext hooks.HookContext
	// Hook type to run during testing (e.g., hooks.PrePromote)
	HookType hooks.HookType
	// Additional context for error messages (e.g., environment name)
	Context string
}

TestConfig contains configuration for temp branch testing

type TestResult

type TestResult struct {
	Success bool
	Error   error
	Message string
}

TestResult contains the result of temp branch testing

Jump to

Keyboard shortcuts

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