diff --git a/pkg/commands/assemble.go b/pkg/commands/assemble.go index dcd3323e..fdfae40e 100644 --- a/pkg/commands/assemble.go +++ b/pkg/commands/assemble.go @@ -2,6 +2,7 @@ package commands import ( "context" + "errors" "fmt" "regexp" "slices" @@ -163,6 +164,12 @@ func (ac *AssembleCommand) createItem(ctx context.Context, item manifest.Item, d } _, err := createCmd.Execute(ctx, opts) if err != nil { + var alreadyExists *ContainerAlreadyExistsError + if errors.As(err, &alreadyExists) { + ac.progress.Done() + ac.printer.Println("%s already exists", item.Name) + return nil + } ac.progress.Fail() return err } diff --git a/pkg/commands/assemble_test.go b/pkg/commands/assemble_test.go index 0cc983d3..fcbc414e 100644 --- a/pkg/commands/assemble_test.go +++ b/pkg/commands/assemble_test.go @@ -29,6 +29,19 @@ func getEnterOptions(spy testutil.ContainerManagerSpy, index int) containermanag return spy.Enter[index][0].(containermanager.EnterOptions) } +func TestAssembleCommand_CreateSkipsExistingBox(t *testing.T) { + mock := &testutil.MockContainerManager{ + ExistsFn: func(string) bool { return true }, + } + cmd := newTestAssembleCommand(mock) + + err := cmd.Execute(context.Background(), commands.AssembleOptions{ + Items: []manifest.Item{{Name: "existing", Image: "alpine:3.21"}}, + }) + require.NoError(t, err, "an already-existing box must be skipped, not an error") + assert.Empty(t, mock.Spy.Remove, "skipping an existing box must not trigger cleanup") +} + func TestAssembleCommand_SetupBox_StartNowTrue(t *testing.T) { mock := &testutil.MockContainerManager{} cmd := newTestAssembleCommand(mock)