-
Notifications
You must be signed in to change notification settings - Fork 218
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
[planners] Separate shell plan from build plan #227
Conversation
dad4793
to
1bc8439
Compare
…228) ## Summary Until we can land #227, I want to revert the changes in #222 so that shell plans can continue working, and we can release some other (unrelated) fixes. In this PR, I revert (manually) the `devbox.go` changes in #222, while leaving the test changes to minimize merge conflicts with #227. cc @loreto @LucilleH ## How was it tested? sanity check ``` > cd testdata/nodejs/nodejs-18 > devbox shell > which node # get nix store path > exit ``` - [x] will find an example with init-hooks and test that. Example 1: nginx ``` > devbox shell Installing nix packages. This may take a while...done. Starting a devbox shell... ##### WARNING: nginx planner is experimental ##### You may need to add "include ./.devbox/gen/shell-helper-nginx.conf;" to your shell-nginx.conf file to ensure the server can start in the nix shell. Use "shell-nginx" to start the server ``` Example 2: python/pip-example ``` devbox/testdata/python/pip-example > devbox shell Installing nix packages. This may take a while...done. Starting a devbox shell... Creating/Using virtual environment in /Users/savil/code/jetpack/devbox/testdata/python/pip-example/.venv (devbox) devbox/testdata/python/pip-example > which python3 /Users/savil/code/jetpack/devbox/testdata/python/pip-example/.venv/bin/python3 ```
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.
Nice!
Overall, I really like the separation of ShellPlan from BuildPlan. I think this will add clarity.
The parts that have me confused are:
Shell plan is only applied when user defined packages includes all of the packages the shell plan requires
Build plan is only applied when user defined packages includes all of the dev packages the build plan requires
Build plan runtimePackages are no longer merged with user defined packages
What is the overall story around how users will understand this behavior?
It feels a bit of a stretch still that users are expected to first add the package via devbox add
for devbox's planners to kick in. cc @Lagoja @loreto curious about your thoughts.
|
||
plan, err := box.BuildPlan() | ||
shellPlan := box.ShellPlan() |
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.
check shellPlan.Invalid()
?
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.
There's no invalid shell plan, as devbox shell
should always succeeds?
devbox.go
Outdated
return d.convertToPlan() | ||
func (d *Devbox) ShellPlan() *plansdk.ShellPlan { | ||
shellPlan := planner.GetShellPlan(d.srcDir, d.cfg.Packages) | ||
shellPlan.DevPackages = d.cfg.Packages |
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.
Two thoughts when reading this. It's possible the answers await me below as I read on.
- Should this override or append to
DevPackages
? - Also, which not do this inside
planner.GetShellPlan
?
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.
Since shell plans are only selected when users have all of the packages a shell plan expect, user defined packages will always include devPackages
from shell plan.
I was thinking planner.GetShellPlan
only strictly return the shell plan from planners.
if err != nil { | ||
return nil, err | ||
} | ||
return plansdk.MergeUserPlan(userPlan, buildPlan) | ||
return plansdk.MergeUserBuildPlan(userPlan, buildPlan) |
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.
same thought as above. Check for buildPlan.Invalid()
here?
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.
Right now we have Buildable()
and Invalid()
. I think maybe we can consolidate the 2. I'll refactor a bit 👍
|
||
currentDir, err := os.Getwd() | ||
require.New(t).NoError(err) | ||
|
||
baseDir := filepath.Dir(testPath) | ||
t.Run(baseDir, func(t *testing.T) { | ||
assert := assert.New(t) | ||
goldenFile := filepath.Join(baseDir, "plan.json") | ||
hasGoldenFile := fileExists(goldenFile) | ||
shellPlanFile := filepath.Join(baseDir, "shell_plan.json") |
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.
change the name of the test case above to t.Run(baseDir+"_testShell, ...
?
devbox_test.go
Outdated
require.New(t).NoError(err) | ||
|
||
baseDir := filepath.Dir(testPath) | ||
t.Run(baseDir, func(t *testing.T) { |
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.
change name to t.Run(baseDir+"_testBuild" + testPath, ...
?
pkgslice/slice.go
Outdated
@@ -30,3 +30,17 @@ func Exclude(s []string, elems []string) []string { | |||
} | |||
return filtered | |||
} | |||
|
|||
// returns true when s includes all elements from elems. |
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 okay, but thought I'd mention lo.Contains
as alternative way
https://github.com/samber/lo#contains
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 saw you importing this package lo
in #230. If we need it in multiple places, I can switch to use that
plan := &plansdk.Plan{ | ||
return &plansdk.ShellPlan{ | ||
Definitions: p.definitions(srcDir, v), | ||
} |
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.
is a user able to specify the definitions they want to use, if the default-planner one is not correct for them?
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.
No, not right now. Though, this is not a regression because we have that problem before.
pkgslice/slice.go
Outdated
@@ -30,3 +30,17 @@ func Exclude(s []string, elems []string) []string { | |||
} | |||
return filtered | |||
} | |||
|
|||
// returns true when s includes all elements from elems. | |||
func Contains(s []string, elems []string) bool { |
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.
for my sanity:
could we call s
as superset
and elems
as subset
?
planner/plansdk/plansdk.go
Outdated
packagesPlan, err := MergePlans(userPlan, automatedPlan) | ||
packagesPlan := &BuildPlan{ | ||
DevPackages: append([]string{}, userPlan.DevPackages...), | ||
RuntimePackages: []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.
should we also add NixOverlays
here since both userPlan and automatedPlan might specify 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.
AppendWithSlice
should handle that case properly
My suggestion as a path forward to land this quickly (which I do want for the shellPlanner versus buildPlanner separation) is to add back to the ShellPlanners the Then we can independently iron out the product story around (possibly) dropping the auto-add of packages from the planners. As is, I fear we'll break functionality for quite a few users. |
Since shell plans are only selected when users have all of the packages a shell plan expect, user defined packages will always include devPackages from shell plan @savil |
a1126e9
to
62cb346
Compare
3a50d46
to
2614d98
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.
nice! Lets ship it!
Shell plan is applied when user defined packages include all of the packages the shell plan requires include one of the packages from the shell plan.
This could get tricky in some situations. For a C++ project, the user may add gcc
package.
But this would then activate the Rust shell planner (which includes two packages, a rust pkg and "gcc"). We could maybe tighten this up in a follow up.
} | ||
|
||
rustPkgDev := fmt.Sprintf("rust-bin.stable.%s.default", rustVersion) | ||
plan.DevPackages = []string{rustPkgDev, "gcc"} |
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.
A style nit is that its easier to track what fields are filled in the ShellPlan struct if we construct it at the end, as in:
manifest, err := ...
rustVersion, err := ...
rustPkgDev := fmt.Sprintf(....
return &plansdk.ShellPlan {
DevPackages: []string{rustPkgDev, "gcc"}
NixOverlays: ...
}
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.
Need to return the nix overlay on error cases, which is why this is formatted this way. Thoughts?
planner/planner.go
Outdated
// if merge fails, we return no errors for now. | ||
if len(userPkgs) == 0 { | ||
// Since user have no packages added yet, we merge all applicable shell planners | ||
// And make proper suggestion accordingly. |
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.
where is this suggestion made?
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.
In devbox.go
, ShellPlan()
Rust shell planner will only be applied when it is relevant (cargo.toml file exist), so I think this is ok? |
36bdb66
to
8c4c7e3
Compare
8c4c7e3
to
ba4a3b7
Compare
Summary
This PR separate shell plans from build plans. This PR looks big but really it only does the following:
For shell
shellPlan
andbuildPlan
packages
include all of the packages the shell plan requiresinclude one of the packages from the shell plan.devbox.json
devbox add
For build
packages
includes all of the dev packages the build plan requiresruntimePackages
are no longer merged with user definedpackages
How was it tested?
go test ./...
devbox shell
devbox build