-
Notifications
You must be signed in to change notification settings - Fork 264
[CLI] add --config flag #225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Current dependencies on/for this PR:
This comment was auto-generated by Graphite. |
8eb948d
to
2366111
Compare
I'll fix the linter errors (minor), but this is ready for review. |
boxcli/build.go
Outdated
func BuildCmd() *cobra.Command { | ||
flags := &docker.BuildFlags{} | ||
flags := newBuildFlags() | ||
|
||
command := &cobra.Command{ | ||
Use: "build [<dir>]", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update Use
to not have [<dir>]
– same for any other commands where this is changing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, yeah, I overlooked that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
boxcli/init.go
Outdated
@@ -9,19 +9,29 @@ import ( | |||
"go.jetpack.io/devbox" | |||
) | |||
|
|||
type initCmdFlags struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So ... init
is the one command I was still imagining would work as devbox init my/project
instead of devbox init -c my/project
Thoughts on whether we should keep init working as before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel strongly, I can change it back ....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you feel about keeping it for consistency?
boxcli/args.go
Outdated
"github.com/pkg/errors" | ||
) | ||
|
||
// Functions that help parse arguments | ||
|
||
// If args empty, defaults to the current directory | ||
// Otherwise grabs the path from the first argument | ||
func pathArg(args []string) string { | ||
func pathArg(args []string, flags *configFlags) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1
If you want to keep the logic simpler, I think it'd be ok to error if both flag and path as an argument are specified at the same time:
- path-as-arg: warning
- flag: no warning
- both: error
2
I think you somehow need to distinguish between the flag being explicitly set and not set. At least I was assuming that:
devbox add foo
looks fordevbox.json
in a parent directory if it's not in the current directory.devbox add foo -c /my/project
looks for adevbox.json
in/my/project
only (and not in any of it's parent directories). By extension that meansdevbox add foo -c "."
only looks in the current directory and not in the parents.devbox add foo -c /my/project/my-file.json
treatsmy-file.json
as the config file.
That said, open to a different approach. How were you thinking it should work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'd be ok to error if both flag and path as an argument are specified at the same time:
👍🏾
That said, open to a different approach. How were you thinking it should work?
I'd been assuming we'd match the current behavior i.e. devbox add foo -c /my/project
will treat /my/project
as the starting point of the search and traverse up to the parent directories.
Happy to change that logic to your proposal. It does feel more clear to not do the magic search if the user is specifying the config-path directly.
How do you feel about doing that as a followup PR? I fear a bigger refactoring is needed because the search-parent-dirs logic is inside devbox.go
. If its simple, I'll fold the change into this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If its simple, I'll fold the change into this PR.
narrator voice it was simple, and folded into this PR.
boxcli/config.go
Outdated
|
||
func registerConfigFlags(cmd *cobra.Command, flags *configFlags) { | ||
cmd.Flags().StringVarP( | ||
&flags.path, "config", "c", currentDir, "path to directory containing a devbox.json config file", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you need to distinguish between the flag being set and not set, you might want the default value to actually be the empty string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cobra has an api something like if cmd.Flags().Changed(flagName) {
I suppose it depends on where I need to distinguish this.
Two more thoughts:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@loreto Thanks for the review! I'll fix up early tomorrow.
Two more thoughts:
The warning message feels too long – it should be more succinct. Something like WARNING: devbox shell is deprecated, use devbox shell --config instead
sounds good, easy change.
While I'm happy enough with --config, I'm not sure I love -c when seeing it in the examples. I'm not entirely sure people will be able to immediately guess that in devbox shell -c nodejs-18 the -c stands for config. Thoughts?
Not sure I follow the concern here. We do still have the --config
flag. The -c
is just a shorthand for that flag for users who get familiar with devbox and don't wanna type as much. Could you elaborate a bit more?
boxcli/args.go
Outdated
"github.com/pkg/errors" | ||
) | ||
|
||
// Functions that help parse arguments | ||
|
||
// If args empty, defaults to the current directory | ||
// Otherwise grabs the path from the first argument | ||
func pathArg(args []string) string { | ||
func pathArg(args []string, flags *configFlags) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'd be ok to error if both flag and path as an argument are specified at the same time:
👍🏾
That said, open to a different approach. How were you thinking it should work?
I'd been assuming we'd match the current behavior i.e. devbox add foo -c /my/project
will treat /my/project
as the starting point of the search and traverse up to the parent directories.
Happy to change that logic to your proposal. It does feel more clear to not do the magic search if the user is specifying the config-path directly.
How do you feel about doing that as a followup PR? I fear a bigger refactoring is needed because the search-parent-dirs logic is inside devbox.go
. If its simple, I'll fold the change into this PR.
boxcli/build.go
Outdated
func BuildCmd() *cobra.Command { | ||
flags := &docker.BuildFlags{} | ||
flags := newBuildFlags() | ||
|
||
command := &cobra.Command{ | ||
Use: "build [<dir>]", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, yeah, I overlooked that
boxcli/config.go
Outdated
|
||
func registerConfigFlags(cmd *cobra.Command, flags *configFlags) { | ||
cmd.Flags().StringVarP( | ||
&flags.path, "config", "c", currentDir, "path to directory containing a devbox.json config file", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cobra has an api something like if cmd.Flags().Changed(flagName) {
I suppose it depends on where I need to distinguish this.
boxcli/init.go
Outdated
@@ -9,19 +9,29 @@ import ( | |||
"go.jetpack.io/devbox" | |||
) | |||
|
|||
type initCmdFlags struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel strongly, I can change it back ....
1237909
to
a29ba81
Compare
a29ba81
to
8b8505b
Compare
@gcurtis @mikeland86 adding y'all as reviewers since others may be away for a bit. |
8b8505b
to
13c67a1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Can we just add some tests for the config searching behavior?
Another minor, optional change is to pass around the flag structs as values if they're not being modified.
boxcli/rm.go
Outdated
return command | ||
} | ||
|
||
func runRemoveCmd(cmd *cobra.Command, args []string) error { | ||
box, err := devbox.Open(".", os.Stdout) | ||
func runRemoveCmd(_ *cobra.Command, args []string, flags *removeCmdFlags) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just remove the *cobra.Command
parameter entirely?
boxcli/config.go
Outdated
func registerConfigFlags(cmd *cobra.Command, flags *configFlags) { | ||
cmd.Flags().StringVarP( | ||
&flags.path, "config", "c", "", "path to directory containing a devbox.json config file", | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Optional) making this a method instead of a package function might make it easier to find.
func registerConfigFlags(cmd *cobra.Command, flags *configFlags) { | |
cmd.Flags().StringVarP( | |
&flags.path, "config", "c", "", "path to directory containing a devbox.json config file", | |
) | |
} | |
func (c *configFlags) register(cmd *cobra.Command) { | |
cmd.Flags().StringVarP( | |
&c.path, "config", "c", "", "path to directory containing a devbox.json config file", | |
) | |
} |
boxcli/build.go
Outdated
func newBuildFlags() *buildCmdFlags { | ||
return &buildCmdFlags{ | ||
docker: &docker.BuildFlags{}, | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If buildCmdFlags
is used as a value then this can go away.
func newBuildFlags() *buildCmdFlags { | |
return &buildCmdFlags{ | |
docker: &docker.BuildFlags{}, | |
} | |
} |
devbox.go
Outdated
} | ||
|
||
// findConfigDir |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops?
devbox.go
Outdated
switch mode := fi.Mode(); { | ||
case mode.IsDir(): | ||
if !plansdk.FileExists(filepath.Join(absDir, configFilename)) { | ||
return "", missingDevboxJSONError(dir, false /*didCheckParents*/) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a personal style thing, but sometimes I find that using a struct directly can help keep things readable. For example, if you have:
type missingConfigError struct {
dir string
checkedParents bool
}
// Implement error
then this line becomes:
return "", &missingConfigError{dir: dir, checkedParents: false}
Up to you though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, it won't quite be:
return "", &missingConfigError{dir: dir, checkedParents: false}
because we need to run some logic in missingDevboxJSONError
to make the error message read nicely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 if the struct implements the Error
interface with the same logic in missingDevboxJSONError
, would that work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I was thinking. Just make the Error
method format the message however we want.
devbox.go
Outdated
// we return a directory from this function | ||
return filepath.Dir(absDir), nil | ||
default: | ||
return "", errors.Errorf("unhandled mode %v", mode) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we just assume that everything that isn't a directory is a file? If for some reason it is unsupported, then we'll get a read error later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure :)
devbox.go
Outdated
} | ||
|
||
// findConfigDir | ||
func findConfigDir(dir string) (string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this go in config.go
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
its added inside devbox.go to keep the CLI minimal. Moving this logic inside here enables devbox
as a package to encapsulate all the logic and functionality (enabling reuse outside this CLI scenario).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with keeping it in devbox
. I was wondering if it should go in config.go
within the same package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just add some tests for the config searching behavior?
will do!
devbox.go
Outdated
switch mode := fi.Mode(); { | ||
case mode.IsDir(): | ||
if !plansdk.FileExists(filepath.Join(absDir, configFilename)) { | ||
return "", missingDevboxJSONError(dir, false /*didCheckParents*/) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, it won't quite be:
return "", &missingConfigError{dir: dir, checkedParents: false}
because we need to run some logic in missingDevboxJSONError
to make the error message read nicely.
devbox.go
Outdated
// we return a directory from this function | ||
return filepath.Dir(absDir), nil | ||
default: | ||
return "", errors.Errorf("unhandled mode %v", mode) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure :)
@savil there are a couple of options for testing
With either approach I think // or: findConfig(root fs.FS, dir string)
func findConfig(root, subdir string) (string, error) {
root, _ := filepath.Abs(root)
for subdir != "." {
rooted := filepath.Join(root, cur)
debug.Log("finding %s in dir: %s\n", configFilename, rooted)
if plansdk.FileExists(filepath.Join(rooted, configFilename)) {
return rooted, nil
}
subdir = filepath.Dir(subdir)
}
}
// CLI
wd, _ := os.Getwd()
findConfig("/", wd)
// Tests
tmpDir := t.TempDir()
os.MkdirAll(filepath.Join(tmpDir, "a/b/c"))
os.WriteFile(filepath.Join(tmpDir, "a", "devbox.json"), []byte(`{}`), 0666)
findConfig(tmpDir, "a/b/c") That will let you control where the search stops ( |
- Update "use" and descriptions of commands - Error if both --config and comand arg are used for config-path - Change logic to do parent-directory search only when config path is not specified in flag or command-args - Shorten deprecation warning message - Lint fixes: replace unused cmd argument variables with underscore variables - Init command uses args: special case handling for init-cmd to use args. Ensure no warning printed.
- pass flag structs by value - make register a method on configFlags - improve shell help message
13c67a1
to
2b5831f
Compare
2b5831f
to
5c2655c
Compare
Summary
Motivation
We want to introduce a
--config
flag and deprecate the use of config as an argument.devbox shell --config path/to/devbox/json nodejs-18_x
, or even more simplydevbox shell nodejs-18_x
.my_sub_directory/devbox.json
and startdevbox shell my_sub_directory/devbox.json
from the root of the repository, then doingdevbox add
ordevbox rm
inside this shell will fail.--config
flag to override the env-var.How was it tested?
preliminary steps
nodejs-18_x
totestdata/nodejs/nodejs-18
devbox.json
at the root of this repo. This is to ensure I verify that the CLI still complains if no devbox.json is found.