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:
- Note the current branch
- Create a temporary test branch from the target base
- Attempt the merge/operation in the temporary branch
- Run hooks (if configured)
- If successful: proceed with actual operation
- If failed: abort and report errors
- 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:
Pre-flight validation: - Check for unstaged changes (blocks if found) - Validate repository state
State preservation: - Note current branch - Store working directory state
Temp branch creation: - Create temp branch from base branch - Checkout temp branch
Merge testing: - Attempt to merge source branch - Check for conflicts - Verify merge succeeded
Hook execution (if configured): - Execute pre-operation hooks - Check hook results - Block if required hooks fail
Cleanup: - Return to original branch - Delete temp branch - Restore working state
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 ¶
- Always test before modifying environment branches
- Include hooks in test configuration when applicable
- Use descriptive Operation and Context values for clarity
- Handle test failures gracefully with user-friendly messages
- Respect context cancellation for long-running operations
- 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 ¶
TestResult contains the result of temp branch testing