Preflight comparison transaction names
This commit is contained in:
@@ -24,6 +24,11 @@ const (
|
||||
DestinationNotEmpty DestinationErrorKind = "not_empty"
|
||||
DestinationUnrecognized DestinationErrorKind = "unrecognized"
|
||||
DestinationInspection DestinationErrorKind = "inspection"
|
||||
|
||||
maxComparisonComponentBytes = 255
|
||||
temporaryRandomBytes = 10
|
||||
stagingDirectoryPattern = ".weatherreporter-staging-*"
|
||||
backupDirectoryPattern = ".weatherreporter-backup-*"
|
||||
)
|
||||
|
||||
// DestinationError provides inspectable context without making filesystem
|
||||
@@ -82,6 +87,9 @@ func PlanDestination(workingDirectory, target string, replace bool) (Destination
|
||||
if target == workingDirectory {
|
||||
return DestinationPlan{}, newDestinationError(DestinationWorkingDirectory, target, nil)
|
||||
}
|
||||
if err := validateTransactionSiblingNames(target); err != nil {
|
||||
return DestinationPlan{}, newDestinationError(DestinationInvalidPath, target, err)
|
||||
}
|
||||
|
||||
plan := DestinationPlan{WorkingDirectory: workingDirectory, Target: target, Replace: replace}
|
||||
info, err := os.Lstat(target)
|
||||
@@ -119,6 +127,22 @@ func PlanDestination(workingDirectory, target string, replace bool) (Destination
|
||||
return plan, nil
|
||||
}
|
||||
|
||||
func validateTransactionSiblingNames(target string) error {
|
||||
if len(filepath.Base(target)) > maxComparisonComponentBytes {
|
||||
return fmt.Errorf("destination name exceeds the %d-byte limit", maxComparisonComponentBytes)
|
||||
}
|
||||
for _, pattern := range []string{stagingDirectoryPattern, backupDirectoryPattern} {
|
||||
if !temporaryPatternFitsComponentLimit(pattern) {
|
||||
return fmt.Errorf("comparison transaction sibling pattern exceeds the %d-byte limit", maxComparisonComponentBytes)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func temporaryPatternFitsComponentLimit(pattern string) bool {
|
||||
return len(strings.Replace(pattern, "*", strings.Repeat("0", temporaryRandomBytes), 1)) <= maxComparisonComponentBytes
|
||||
}
|
||||
|
||||
func absoluteCleanPath(value string) (string, error) {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return "", fmt.Errorf("path is required")
|
||||
@@ -480,7 +504,7 @@ func publish(ctx context.Context, plan DestinationPlan, bundle LogicalBundle, op
|
||||
if err := os.MkdirAll(filepath.Dir(plan.Target), 0o755); err != nil {
|
||||
return PublicationResult{}, fmt.Errorf("create comparison destination parent %q: %w", filepath.Dir(plan.Target), err)
|
||||
}
|
||||
temporaryDirectory, err := os.MkdirTemp(filepath.Dir(plan.Target), "."+filepath.Base(plan.Target)+".staging-")
|
||||
temporaryDirectory, err := os.MkdirTemp(filepath.Dir(plan.Target), stagingDirectoryPattern)
|
||||
if err != nil {
|
||||
return PublicationResult{}, fmt.Errorf("create comparison staging directory: %w", err)
|
||||
}
|
||||
@@ -516,7 +540,7 @@ func publish(ctx context.Context, plan DestinationPlan, bundle LogicalBundle, op
|
||||
return PublicationResult{Committed: true}, nil
|
||||
}
|
||||
|
||||
backupDirectory, err := uniqueSiblingPath(filepath.Dir(currentPlan.Target), "."+filepath.Base(currentPlan.Target)+".backup-")
|
||||
backupDirectory, err := uniqueSiblingPath(filepath.Dir(currentPlan.Target), backupDirectoryPattern)
|
||||
if err != nil {
|
||||
return PublicationResult{}, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user