Add configurable export error handling policies (bd-exug)
Implements flexible error handling for export operations with four policies: - strict: Fail-fast on any error (default for user exports) - best-effort: Skip errors with warnings (default for auto-exports) - partial: Retry then skip with manifest tracking - required-core: Fail on core data, skip enrichments Key features: - Per-project configuration via `bd config set export.error_policy` - Separate policy for auto-exports: `auto_export.error_policy` - Retry with exponential backoff (configurable attempts/delay) - Optional export manifests documenting completeness - Per-issue encoding error handling This allows users to choose the right trade-off between data integrity and system availability for their specific project needs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
96
internal/export/executor.go
Normal file
96
internal/export/executor.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package export
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// DataType represents a type of data being fetched
|
||||
type DataType string
|
||||
|
||||
const (
|
||||
DataTypeCore DataType = "core" // Issues and dependencies
|
||||
DataTypeLabels DataType = "labels" // Issue labels
|
||||
DataTypeComments DataType = "comments" // Issue comments
|
||||
)
|
||||
|
||||
// FetchResult holds the result of a data fetch operation
|
||||
type FetchResult struct {
|
||||
Success bool
|
||||
Err error
|
||||
Warnings []string
|
||||
}
|
||||
|
||||
// FetchWithPolicy executes a fetch operation with the configured error policy
|
||||
func FetchWithPolicy(ctx context.Context, cfg *Config, dataType DataType, desc string, fn func() error) FetchResult {
|
||||
var result FetchResult
|
||||
|
||||
// Determine if this is core data
|
||||
isCore := dataType == DataTypeCore
|
||||
|
||||
// Execute based on policy
|
||||
switch cfg.Policy {
|
||||
case PolicyStrict:
|
||||
// Fail-fast on any error
|
||||
err := RetryWithBackoff(ctx, cfg.RetryAttempts, cfg.RetryBackoffMS, desc, fn)
|
||||
if err != nil {
|
||||
result.Err = err
|
||||
return result
|
||||
}
|
||||
result.Success = true
|
||||
|
||||
case PolicyBestEffort:
|
||||
// Skip errors with warnings
|
||||
err := RetryWithBackoff(ctx, cfg.RetryAttempts, cfg.RetryBackoffMS, desc, fn)
|
||||
if err != nil {
|
||||
warning := fmt.Sprintf("Warning: %s failed, skipping: %v", desc, err)
|
||||
fmt.Fprintf(os.Stderr, "%s\n", warning)
|
||||
result.Warnings = append(result.Warnings, warning)
|
||||
result.Success = false // Data is missing
|
||||
return result
|
||||
}
|
||||
result.Success = true
|
||||
|
||||
case PolicyPartial:
|
||||
// Retry with backoff, then skip with manifest entry
|
||||
err := RetryWithBackoff(ctx, cfg.RetryAttempts, cfg.RetryBackoffMS, desc, fn)
|
||||
if err != nil {
|
||||
warning := fmt.Sprintf("Warning: %s failed after retries, skipping: %v", desc, err)
|
||||
fmt.Fprintf(os.Stderr, "%s\n", warning)
|
||||
result.Warnings = append(result.Warnings, warning)
|
||||
result.Success = false
|
||||
return result
|
||||
}
|
||||
result.Success = true
|
||||
|
||||
case PolicyRequiredCore:
|
||||
// Fail on core data, skip enrichments
|
||||
if isCore {
|
||||
err := RetryWithBackoff(ctx, cfg.RetryAttempts, cfg.RetryBackoffMS, desc, fn)
|
||||
if err != nil {
|
||||
result.Err = err
|
||||
return result
|
||||
}
|
||||
result.Success = true
|
||||
} else {
|
||||
// Best-effort for enrichments
|
||||
err := RetryWithBackoff(ctx, cfg.RetryAttempts, cfg.RetryBackoffMS, desc, fn)
|
||||
if err != nil {
|
||||
warning := fmt.Sprintf("Warning: %s (enrichment) failed, skipping: %v", desc, err)
|
||||
fmt.Fprintf(os.Stderr, "%s\n", warning)
|
||||
result.Warnings = append(result.Warnings, warning)
|
||||
result.Success = false
|
||||
return result
|
||||
}
|
||||
result.Success = true
|
||||
}
|
||||
|
||||
default:
|
||||
// Unknown policy, fail-fast as safest option
|
||||
result.Err = fmt.Errorf("unknown error policy: %s", cfg.Policy)
|
||||
return result
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user