-
Notifications
You must be signed in to change notification settings - Fork 184
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
[Outputs] Add store-paths of package outputs to devbox.lock #1814
Conversation
Current dependencies on/for this PR: This stack of pull requests is managed by Graphite. |
bdba596
to
7718ef5
Compare
internal/devbox/packages.go
Outdated
if err := d.lockfile.Add(addedPackageNames...); err != nil { | ||
return err | ||
} |
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.
Main issue with this is that this saves the lock file right away. This will leave it in a bad state if something fails later on. It's also not 100% correct (see newFlakePlan
calls Add
)
A few alternatives:
- Instead of doing lockfile.Add just do
lockfile.UpdateSystemsFormat()
in the 3 places you need it (add/remove/update). This would update the internal boolean. This is simplest. We can keep this function around whenever we want to update lock file formats. - In lockfile.Save() only update the format is stuff is dirty (i.e if reloading the lockfile doesn't match what is in memory). (though it may be a bit complex to tell if dirty). This is cleanest solution because it doesn't require a new bool we may forget to add.
- Remove
Save
fromAdd
(this is probably OK, but it feels risky). I don't love this one.
FWIW, currently newFlakePlan
calls lockfile.Add
on includes every time the state needs to be recomputed, so I think any project with includes will automatically update.
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.
ugh, I thought I had checked for existing callers, but I think I missed this one...good catch
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.
Added some comments with alternative approach. There's a minor bug because of previous use of Add
I think the cleanest solution that would not require boolean:
- If lockfile contains store paths it is legacy.
- If lockfile is dirty (i.e. loaded version doesn't match file) we modernize. (small perf hit for re-reading file, but we should rarely write to lock file and when we do, it's a slower operation anyway)
But if you run into issues implementing this, I think using a boolean is fine.
internal/lock/package.go
Outdated
if o == nil || other == nil { | ||
return o == other | ||
} | ||
return o.Name == other.Name && o.Path == other.Path && o.Default == other.Default |
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 normal equality in golang. You can do *o == *other
internal/lock/package.go
Outdated
// It is the cache key in the Binary Cache Store (cache.nixos.org) | ||
// It is of the form /nix/store/<hash>-<name>-<version> | ||
// <name> may be different from the canonicalName so we store the full store path. | ||
Outputs []*Output `json:"outputs,omitempty"` |
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.
Why pointers? If you make them structs you don't need the Equals
function below you can just compare
a == b
internal/lock/package.go
Outdated
isEqual := true | ||
if len(i.Outputs) != len(other.Outputs) { | ||
return false | ||
} | ||
for i, o := range i.Outputs { | ||
if !o.Equals(other.Outputs[i]) { | ||
return false | ||
} | ||
} | ||
return isEqual | ||
} |
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.
slices.Compare == 0
(assuming you don't use pointers, see comment above)
52da4cf
to
c0f982d
Compare
This doesn't work in the following scenario:
yay! this works :) |
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 using "out" for legacy data is okay. I'm pretty sure the original store_path
JSON field mapped to .outPath
attribute of the package.
internal/lock/package.go
Outdated
@@ -43,9 +64,50 @@ func (p *Package) IsAllowInsecure() bool { | |||
return p.AllowInsecure | |||
} | |||
|
|||
// Useful for debugging when we print the struct | |||
func (i *SystemInfo) String() string { | |||
return fmt.Sprintf("SystemInfo{Outputs:%v, StorePath:%s}", i.Outputs, i.StorePath) |
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.
return fmt.Sprintf("SystemInfo{Outputs:%v, StorePath:%s}", i.Outputs, i.StorePath) | |
return fmt.Sprintf("%+v", *i) |
func (i *SystemInfo) Equals(other *SystemInfo) bool { | ||
if i == nil || other == nil { | ||
return i == other | ||
} | ||
return *i == *other | ||
|
||
return slices.Equal(i.Outputs, other.Outputs) |
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.
Does this need to take i.StorePath
into account?
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 this is OK? at least looking at the callsite https://github.com/jetpack-io/devbox/blob/main/internal/devbox/update.go#L142-L146
Only outputs matter moving forward and storePath is just kept for legacy reasons (to avoid updating everyone's lockfile)
func (f *File) Save() error { | ||
isDirty, err := f.isDirty() |
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.
sweet! forgot we had this.
func (i *SystemInfo) Equals(other *SystemInfo) bool { | ||
if i == nil || other == nil { | ||
return i == other | ||
} | ||
return *i == *other | ||
|
||
return slices.Equal(i.Outputs, other.Outputs) |
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 this is OK? at least looking at the callsite https://github.com/jetpack-io/devbox/blob/main/internal/devbox/update.go#L142-L146
Only outputs matter moving forward and storePath is just kept for legacy reasons (to avoid updating everyone's lockfile)
2ab4625
to
e1cbd9d
Compare
Summary
This PR updates the
devbox.lock
file to incorporate the expanded response from/v2/resolve
api. This/v2/response
incorporates store-paths for each output of the nix package.To do so, we update
lock.SystemInfo
to expect[]Output
instead of the previousStorePath string
. ThisSystemInfo
struct needs to be backwards-compatible to read existingdevbox.lock
files that have theStorePath
string. So, the read-path inlock.GetFile
has some fallback code to handle this scenario, where theStorePath
is added asOutput{ Name: "out", path: <path>, Default: true}
.The main challenge with writing this PR was that we don't want to update
devbox.lock
unless there's been an explicit user action of the formdevbox update
ordevbox add
ordevbox remove
. Unfortunately, thelock.File
gets internally updated by various code paths using itsfile.Resolve()
function, and so it was (previously) hard for the lockfileto track when an add/remove/update action happened.
To work around this, I have more explicitly invoked
lockfile.Add
andlockfile.Remove
in the "add package" and "remove package" code paths. I think this is safe to do, but let me know if you feel a better path exists. This lets us track whether to write the lockfile with the legacy format oflock.SystemInfo
(i.e. Store Path) or the modern format (i.e. []Output).How was it tested?
devbox update
in devbox-repo doesn't have changes so the lock file wasn't changed.rm -rf .devbox && devbox shell
. The lockfile wasn't changed.devbox add prometheus
added the package in the modern []Output format of SystemInfo, and also updated the other packages to use the modern SystemInfo format.