diff --git a/internal/boxcli/clean.go b/internal/boxcli/clean.go new file mode 100644 index 00000000000..c8c7aeb6eca --- /dev/null +++ b/internal/boxcli/clean.go @@ -0,0 +1,65 @@ +// Copyright 2024 Jetify Inc. and contributors. All rights reserved. +// Use of this source code is governed by the license in the LICENSE file. + +package boxcli + +import ( + "github.com/AlecAivazis/survey/v2" + "github.com/spf13/cobra" + + "go.jetpack.io/devbox/internal/devconfig" + "go.jetpack.io/devbox/internal/devconfig/configfile" + "go.jetpack.io/devbox/internal/lock" + "go.jetpack.io/devbox/internal/shellgen" +) + +type cleanFlags struct { + pathFlag + hard bool +} + +func cleanCmd() *cobra.Command { + flags := &cleanFlags{} + command := &cobra.Command{ + Use: "clean", + Short: "Clean up devbox files from the current directory", + Long: "Cleans up an existing devbox directory. " + + "This will delete .devbox and devbox.lock. ", + RunE: func(cmd *cobra.Command, args []string) error { + if flags.hard { + prompt := &survey.Confirm{ + Message: "Are you sure you want to delete your devbox config?", + } + confirmed := false + if err := survey.AskOne(prompt, &confirmed); err != nil { + return err + } + if !confirmed { + return nil + } + } + return runCleanCmd(cmd, args, flags) + }, + } + + command.Flags().BoolVar(&flags.hard, "hard", false, "Also delete the devbox.json file") + + flags.register(command) + + return command +} + +func runCleanCmd(cmd *cobra.Command, args []string, flags *cleanFlags) error { + path := pathArg(args) + + filesToDelete := []string{ + lock.FileName, + shellgen.DevboxHiddenDirName, + } + + if flags.hard { + filesToDelete = append(filesToDelete, configfile.DefaultName) + } + + return devconfig.Clean(path, filesToDelete, cmd.ErrOrStderr()) +} diff --git a/internal/boxcli/multi/sync.go b/internal/boxcli/multi/sync.go index ac43ea8d2b7..f5f68ff3440 100644 --- a/internal/boxcli/multi/sync.go +++ b/internal/boxcli/multi/sync.go @@ -104,7 +104,7 @@ func collectLockfiles() ([]string, error) { return err } - if !dirEntry.IsDir() && filepath.Base(path) == "devbox.lock" { + if !dirEntry.IsDir() && filepath.Base(path) == lock.FileName { lockfiles = append(lockfiles, path) } diff --git a/internal/boxcli/root.go b/internal/boxcli/root.go index e30ba6a363b..d09555d1fa4 100644 --- a/internal/boxcli/root.go +++ b/internal/boxcli/root.go @@ -66,6 +66,7 @@ func RootCmd() *cobra.Command { command.AddCommand(globalCmd()) command.AddCommand(infoCmd()) command.AddCommand(initCmd()) + command.AddCommand(cleanCmd()) command.AddCommand(installCmd()) command.AddCommand(integrateCmd()) command.AddCommand(listCmd()) diff --git a/internal/devconfig/clean.go b/internal/devconfig/clean.go new file mode 100644 index 00000000000..0e90b195654 --- /dev/null +++ b/internal/devconfig/clean.go @@ -0,0 +1,27 @@ +// Copyright 2024 Jetify Inc. and contributors. All rights reserved. +// Use of this source code is governed by the license in the LICENSE file. + +package devconfig + +import ( + "io" + "os" + + "go.jetpack.io/devbox/internal/fileutil" + "go.jetpack.io/devbox/internal/ux" +) + +func Clean(dir string, filesToDelete []string, w io.Writer) error { + for _, f := range filesToDelete { + if fileutil.Exists(f) { + ux.Finfof(w, "Deleting %s\n", f) + } + if err := os.RemoveAll(dir + f); err != nil { + return err + } + } + + // TODO: should the devbox shell be killed here? + + return nil +} diff --git a/internal/lock/lockfile.go b/internal/lock/lockfile.go index e4a47effa31..b7f90843a55 100644 --- a/internal/lock/lockfile.go +++ b/internal/lock/lockfile.go @@ -21,6 +21,8 @@ import ( "go.jetpack.io/devbox/internal/cuecfg" ) +const FileName = "devbox.lock" + const lockFileVersion = "1" // Lightly inspired by package-lock.json @@ -232,7 +234,7 @@ func (f *File) isDirty() (bool, error) { } func lockFilePath(projectDir string) string { - return filepath.Join(projectDir, "devbox.lock") + return filepath.Join(projectDir, FileName) } func ResolveRunXPackage(ctx context.Context, pkg string) (types.PkgRef, error) { diff --git a/internal/shellgen/generate.go b/internal/shellgen/generate.go index 57d8e37acc9..74df8988285 100644 --- a/internal/shellgen/generate.go +++ b/internal/shellgen/generate.go @@ -20,6 +20,8 @@ import ( "go.jetpack.io/devbox/internal/redact" ) +const DevboxHiddenDirName = ".devbox" + //go:embed tmpl/* var tmplFS embed.FS @@ -43,7 +45,7 @@ func GenerateForPrintEnv(ctx context.Context, devbox devboxer) error { } // Gitignore file is added to the .devbox directory - err = writeFromTemplate(filepath.Join(devbox.ProjectDir(), ".devbox"), plan, ".gitignore", ".gitignore") + err = writeFromTemplate(filepath.Join(devbox.ProjectDir(), DevboxHiddenDirName), plan, ".gitignore", ".gitignore") if err != nil { return errors.WithStack(err) } diff --git a/testscripts/clean/clean.test.txt b/testscripts/clean/clean.test.txt new file mode 100644 index 00000000000..5f179a542a9 --- /dev/null +++ b/testscripts/clean/clean.test.txt @@ -0,0 +1,17 @@ +# Create a new devbox, install a package and run clean + +! exists devbox.json +exec devbox init +exists devbox.json + +exec devbox add go + +exec devbox shell +exists devbox.json +exists devbox.lock +exists .devbox + +exec devbox clean +! exists devbox.lock +! exists .devbox + diff --git a/testscripts/clean/clean_hard.test.txt b/testscripts/clean/clean_hard.test.txt new file mode 100644 index 00000000000..a69873d22b3 --- /dev/null +++ b/testscripts/clean/clean_hard.test.txt @@ -0,0 +1,18 @@ +# Create a new devbox, install a package and run clean hard + +! exists devbox.json +exec devbox init +exists devbox.json + +exec devbox add go + +exec devbox shell +exists devbox.json +exists devbox.lock +exists .devbox + +exec devbox clean --hard +! exists devbox.json +! exists devbox.lock +! exists .devbox +