Add explicit force replacement workflow
This commit is contained in:
@@ -30,6 +30,7 @@ type Backend interface {
|
||||
Walk(ctx context.Context, prefix string, opts WalkOptions, fn WalkFunc) error
|
||||
HasAny(ctx context.Context, prefix string) (bool, error)
|
||||
DeleteManagedBundle(ctx context.Context, bundlePath string, managedOutputPaths []string, opts DeleteOptions) error
|
||||
DeletePrefix(ctx context.Context, prefix string, opts DeleteOptions) error
|
||||
}
|
||||
|
||||
type WalkOptions struct {
|
||||
|
||||
@@ -29,6 +29,7 @@ const (
|
||||
OpWalk = "walk"
|
||||
OpHasAny = "has any"
|
||||
OpDeleteManagedBundle = "delete managed bundle"
|
||||
OpDeletePrefix = "delete prefix"
|
||||
OpRegisterBackend = "register backend"
|
||||
OpOpenBackend = "open backend"
|
||||
)
|
||||
|
||||
@@ -205,6 +205,41 @@ func (b *Backend) DeleteManagedBundle(ctx context.Context, bundlePath string, ma
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Backend) DeletePrefix(ctx context.Context, prefix string, opts storage.DeleteOptions) error {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := storage.ValidatePrefix(prefix); err != nil {
|
||||
return err
|
||||
}
|
||||
if prefix != "" && !b.exists(prefix) && !b.hasChild(prefix) {
|
||||
if opts.IgnoreMissing {
|
||||
return nil
|
||||
}
|
||||
return storage.NewError(storage.OpDeletePrefix, backendName, prefix, storage.ErrNotFound, nil)
|
||||
}
|
||||
for path := range b.files {
|
||||
if path == prefix || entryBelow(prefix, path) {
|
||||
delete(b.files, path)
|
||||
}
|
||||
}
|
||||
for path := range b.symlinks {
|
||||
if path == prefix || entryBelow(prefix, path) {
|
||||
delete(b.symlinks, path)
|
||||
}
|
||||
}
|
||||
for path := range b.dirs {
|
||||
if path != "" && (path == prefix || entryBelow(prefix, path)) {
|
||||
delete(b.dirs, path)
|
||||
}
|
||||
}
|
||||
if opts.PruneEmptyDirs {
|
||||
b.pruneEmptyParents(parentOf(prefix))
|
||||
}
|
||||
b.dirs[""] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Backend) ensureParents(path string) {
|
||||
parent := parentOf(path)
|
||||
for parent != "" {
|
||||
|
||||
@@ -140,6 +140,30 @@ func TestBackendManagedDeletion(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendDeletePrefixStaysWithinPrefix(t *testing.T) {
|
||||
backend := New()
|
||||
mustWrite(t, backend, "bundle/report.md", "report")
|
||||
mustWrite(t, backend, "bundle/nested/old.txt", "old")
|
||||
mustWrite(t, backend, "bundle-sibling/keep.txt", "keep")
|
||||
mustWrite(t, backend, "outside.txt", "outside")
|
||||
|
||||
if err := backend.DeletePrefix(context.Background(), "bundle", storage.DeleteOptions{IgnoreMissing: true, PruneEmptyDirs: true}); err != nil {
|
||||
t.Fatalf("DeletePrefix() error = %v", err)
|
||||
}
|
||||
if _, err := backend.Stat(context.Background(), "bundle/report.md"); !storage.IsNotFound(err) {
|
||||
t.Fatalf("deleted file stat error = %v, want not found", err)
|
||||
}
|
||||
if _, err := backend.Stat(context.Background(), "bundle/nested/old.txt"); !storage.IsNotFound(err) {
|
||||
t.Fatalf("deleted nested file stat error = %v, want not found", err)
|
||||
}
|
||||
if _, err := backend.Stat(context.Background(), "bundle-sibling/keep.txt"); err != nil {
|
||||
t.Fatalf("sibling stat error = %v", err)
|
||||
}
|
||||
if _, err := backend.Stat(context.Background(), "outside.txt"); err != nil {
|
||||
t.Fatalf("outside stat error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendHasAnyAndWalkStop(t *testing.T) {
|
||||
backend := New()
|
||||
found, err := backend.HasAny(context.Background(), "missing")
|
||||
|
||||
@@ -206,3 +206,7 @@ func (b walkBackend) HasAny(context.Context, string) (bool, error) {
|
||||
func (b walkBackend) DeleteManagedBundle(context.Context, string, []string, DeleteOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b walkBackend) DeletePrefix(context.Context, string, DeleteOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user