| /* |
| Copyright © 2021 Aspect Build Systems Inc |
| |
| Not licensed for re-use. |
| */ |
| |
| package clean |
| |
| import ( |
| "fmt" |
| |
| "github.com/manifoldco/promptui" |
| "github.com/spf13/cobra" |
| "github.com/spf13/viper" |
| |
| "aspect.build/cli/pkg/aspecterrors" |
| "aspect.build/cli/pkg/bazel" |
| "aspect.build/cli/pkg/ioutils" |
| ) |
| |
| const ( |
| skipPromptKey = "clean.skip_prompt" |
| |
| ReclaimOption = "Reclaim disk space for this workspace (same as bazel clean)" |
| ReclaimAllOption = "Reclaim disk space for all Bazel workspaces" |
| NonIncrementalOption = "Prepare to perform a non-incremental build" |
| InvalidateReposOption = "Invalidate all repository rules, causing them to recreate external repos" |
| WorkaroundOption = "Workaround inconsistent state in the output tree" |
| |
| outputBaseHint = `It's faster to perform a non-incremental build by choosing a different output base. |
| Instead of running 'clean' you should use the --output_base flag. |
| Run 'aspect help clean' for more info. |
| ` |
| syncHint = `It's faster to invalidate repository rules by using the sync command. |
| Instead of running 'clean' you should run 'aspect sync --configure' |
| Run 'aspect help clean' for more info. |
| ` |
| fileIssueHint = `Bazel is a correct build tool, and it should not be possible to get inconstent state. |
| We highly recommend you file a bug reporting this problem so that the offending rule |
| implementation can be fixed. |
| ` |
| |
| rememberLine1 = "You can skip this prompt to make 'aspect clean' behave the same as 'bazel clean'\n" |
| rememberLine2 = "Remember this choice and skip the prompt in the future" |
| ) |
| |
| type SelectRunner interface { |
| Run() (int, string, error) |
| } |
| |
| type PromptRunner interface { |
| Run() (string, error) |
| } |
| |
| // Clean represents the aspect clean command. |
| type Clean struct { |
| ioutils.Streams |
| bzl bazel.Spawner |
| isInteractiveMode bool |
| |
| Behavior SelectRunner |
| Workaround PromptRunner |
| Remember PromptRunner |
| Prefs viper.Viper |
| |
| Expunge bool |
| ExpungeAsync bool |
| } |
| |
| // New creates a Clean command. |
| func New( |
| streams ioutils.Streams, |
| bzl bazel.Spawner, |
| isInteractiveMode bool) *Clean { |
| return &Clean{ |
| Streams: streams, |
| isInteractiveMode: isInteractiveMode, |
| bzl: bzl, |
| } |
| } |
| |
| func NewDefault(isInteractive bool) *Clean { |
| c := New( |
| ioutils.DefaultStreams, |
| bazel.New(), |
| isInteractive) |
| c.Behavior = &promptui.Select{ |
| Label: "Clean can have a few behaviors. Which do you want?", |
| Items: []string{ |
| ReclaimOption, |
| ReclaimAllOption, |
| NonIncrementalOption, |
| InvalidateReposOption, |
| WorkaroundOption, |
| }, |
| } |
| c.Workaround = &promptui.Prompt{ |
| Label: "Temporarily workaround the bug by deleting the output folder", |
| IsConfirm: true, |
| } |
| c.Remember = &promptui.Prompt{ |
| Label: rememberLine2, |
| IsConfirm: true, |
| } |
| c.Prefs = *viper.GetViper() |
| return c |
| } |
| |
| // Run runs the aspect build command. |
| func (c *Clean) Run(_ *cobra.Command, _ []string) error { |
| skip := c.Prefs.GetBool(skipPromptKey) |
| if c.isInteractiveMode && !skip { |
| |
| _, chosen, err := c.Behavior.Run() |
| |
| if err != nil { |
| return fmt.Errorf("prompt failed: %w", err) |
| } |
| |
| switch chosen { |
| |
| case ReclaimOption: |
| // Allow user to opt-out of our fancy "clean" command and just behave like bazel |
| fmt.Fprint(c.Streams.Stdout, rememberLine1) |
| if _, err := c.Remember.Run(); err == nil { |
| c.Prefs.Set(skipPromptKey, "true") |
| if err := c.Prefs.WriteConfig(); err != nil { |
| return fmt.Errorf("failed to update config file: %w", err) |
| } |
| } |
| case ReclaimAllOption: |
| fmt.Fprint(c.Streams.Stdout, "Sorry, this is not implemented yet: discover all bazel workspaces on the machine\n") |
| return nil |
| case NonIncrementalOption: |
| fmt.Fprint(c.Streams.Stdout, outputBaseHint) |
| return nil |
| case InvalidateReposOption: |
| fmt.Fprint(c.Streams.Stdout, syncHint) |
| return nil |
| case WorkaroundOption: |
| fmt.Fprint(c.Streams.Stdout, fileIssueHint) |
| _, err := c.Workaround.Run() |
| if err != nil { |
| return fmt.Errorf("prompt failed: %w", err) |
| } |
| } |
| } |
| |
| cmd := []string{"clean"} |
| if c.Expunge { |
| cmd = append(cmd, "--expunge") |
| } |
| if c.ExpungeAsync { |
| cmd = append(cmd, "--expunge_async") |
| } |
| if exitCode, err := c.bzl.Spawn(cmd); exitCode != 0 { |
| err = &aspecterrors.ExitError{ |
| Err: err, |
| ExitCode: exitCode, |
| } |
| return err |
| } |
| |
| return nil |
| } |