Skip to content

Commit 3d91344

Browse files
authored
[lock] Refactor local lock (#1314)
## Summary Follow up to #1291. This simplified the lockfile interface and mostly removes "local" lock from public view. It addresses this comment: #1291 (comment) ## How was it tested? `devbox update` on a package that needs system info downloaded.
1 parent 714dbac commit 3d91344

File tree

7 files changed

+78
-60
lines changed

7 files changed

+78
-60
lines changed

devbox.lock

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
"lockfile_version": "1",
33
"packages": {
44
5-
"last_modified": "2023-05-25T03:54:59Z",
6-
"resolved": "github:NixOS/nixpkgs/8d4d822bc0efa9de6eddc79cb0d82897a9baa750#go",
7-
"version": "1.20.4"
5+
"last_modified": "2023-06-29T16:20:38Z",
6+
"resolved": "github:NixOS/nixpkgs/3c614fbc76fc152f3e1bc4b2263da6d90adf80fb#go",
7+
"source": "devbox-search",
8+
"version": "1.20.5"
89
},
910
1011
"last_modified": "2023-05-01T16:53:22Z",

internal/impl/devbox.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -893,11 +893,7 @@ func (d *Devbox) nixEnv(ctx context.Context) (map[string]string, error) {
893893
usePrintDevEnvCache := false
894894

895895
// If lockfile is up-to-date, we can use the print-dev-env cache.
896-
lockFile, err := lock.Local(d)
897-
if err != nil {
898-
return nil, err
899-
}
900-
upToDate, err := lockFile.IsUpToDate()
896+
upToDate, err := d.lockfile.IsUpToDateAndInstalled()
901897
if err != nil {
902898
return nil, err
903899
}

internal/impl/packages.go

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222

2323
"go.jetpack.io/devbox/internal/boxcli/usererr"
2424
"go.jetpack.io/devbox/internal/debug"
25-
"go.jetpack.io/devbox/internal/lock"
2625
"go.jetpack.io/devbox/internal/nix"
2726
"go.jetpack.io/devbox/internal/plugin"
2827
"go.jetpack.io/devbox/internal/ux"
@@ -188,18 +187,9 @@ const (
188187
func (d *Devbox) ensurePackagesAreInstalled(ctx context.Context, mode installMode) error {
189188
defer trace.StartRegion(ctx, "ensurePackages").End()
190189

191-
localLock, err := lock.Local(d)
192-
if err != nil {
193-
return err
194-
}
195-
196-
upToDate, err := localLock.IsUpToDate()
197-
if err != nil {
190+
if upToDate, err := d.lockfile.IsUpToDateAndInstalled(); err != nil || upToDate {
198191
return err
199192
}
200-
if upToDate {
201-
return nil
202-
}
203193

204194
if mode == ensure {
205195
fmt.Fprintln(d.writer, "Ensuring packages are installed.")
@@ -218,18 +208,14 @@ func (d *Devbox) ensurePackagesAreInstalled(ctx context.Context, mode installMod
218208
}
219209

220210
// Force print-dev-env cache to be recomputed.
221-
if _, err = d.computeNixEnv(ctx, false /*use cache*/); err != nil {
211+
if _, err := d.computeNixEnv(ctx, false /*use cache*/); err != nil {
222212
return err
223213
}
224214

225215
// Ensure we clean out packages that are no longer needed.
226216
d.lockfile.Tidy()
227217

228-
if err = d.lockfile.Save(); err != nil {
229-
return err
230-
}
231-
232-
return localLock.Update()
218+
return d.lockfile.Save()
233219
}
234220

235221
func (d *Devbox) profilePath() (string, error) {

internal/impl/update.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,6 @@ func (d *Devbox) Update(ctx context.Context, pkgs ...string) error {
5454
}
5555
}
5656

57-
if err := d.lockfile.Save(); err != nil {
58-
return err
59-
}
60-
6157
if err := d.ensurePackagesAreInstalled(ctx, ensure); err != nil {
6258
return err
6359
}

internal/lock/local.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,24 @@ func (l *localLockFile) equals(other *localLockFile) bool {
3434
l.DevboxVersion == other.DevboxVersion
3535
}
3636

37-
func (l *localLockFile) IsUpToDate() (bool, error) {
38-
newLock, err := forProject(l.project)
37+
func isLocalUpToDate(project devboxProject) (bool, error) {
38+
filesystemLock, err := readLocal(project)
39+
if err != nil {
40+
return false, err
41+
}
42+
newLock, err := forProject(project)
3943
if err != nil {
4044
return false, err
4145
}
4246

43-
return l.equals(newLock), nil
47+
return filesystemLock.equals(newLock), nil
4448
}
4549

46-
func (l *localLockFile) Update() error {
50+
func updateLocal(project devboxProject) error {
51+
l, err := readLocal(project)
52+
if err != nil {
53+
return err
54+
}
4755
newLock, err := forProject(l.project)
4856
if err != nil {
4957
return err
@@ -53,7 +61,7 @@ func (l *localLockFile) Update() error {
5361
return cuecfg.WriteFile(localLockFilePath(l.project), l)
5462
}
5563

56-
func Local(project devboxProject) (*localLockFile, error) {
64+
func readLocal(project devboxProject) (*localLockFile, error) {
5765
lockFile := &localLockFile{project: project}
5866
err := cuecfg.ParseFile(localLockFilePath(project), lockFile)
5967
if errors.Is(err, fs.ErrNotExist) {

internal/lock/lockfile.go

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,76 +45,79 @@ func GetFile(project devboxProject) (*File, error) {
4545
return lockFile, nil
4646
}
4747

48-
func (l *File) Add(pkgs ...string) error {
48+
func (f *File) Add(pkgs ...string) error {
4949
for _, p := range pkgs {
50-
if _, err := l.Resolve(p); err != nil {
50+
if _, err := f.Resolve(p); err != nil {
5151
return err
5252
}
5353
}
54-
return l.Save()
54+
return f.Save()
5555
}
5656

57-
func (l *File) Remove(pkgs ...string) error {
57+
func (f *File) Remove(pkgs ...string) error {
5858
for _, p := range pkgs {
59-
delete(l.Packages, p)
59+
delete(f.Packages, p)
6060
}
61-
return l.Save()
61+
return f.Save()
6262
}
6363

6464
// Resolve updates the in memory copy for performance but does not write to disk
6565
// This avoids writing values that may need to be removed in case of error.
66-
func (l *File) Resolve(pkg string) (*Package, error) {
67-
entry, hasEntry := l.Packages[pkg]
66+
func (f *File) Resolve(pkg string) (*Package, error) {
67+
entry, hasEntry := f.Packages[pkg]
6868

6969
if !hasEntry || entry.Resolved == "" {
7070
locked := &Package{}
7171
var err error
7272
if _, _, versioned := searcher.ParseVersionedPackage(pkg); versioned {
73-
locked, err = l.FetchResolvedPackage(pkg)
73+
locked, err = f.FetchResolvedPackage(pkg)
7474
if err != nil {
7575
return nil, err
7676
}
7777
} else if IsLegacyPackage(pkg) {
7878
// These are legacy packages without a version. Resolve to nixpkgs with
7979
// whatever hash is in the devbox.json
8080
locked = &Package{
81-
Resolved: l.LegacyNixpkgsPath(pkg),
81+
Resolved: f.LegacyNixpkgsPath(pkg),
8282
Source: nixpkgSource,
8383
}
8484
}
85-
l.Packages[pkg] = locked
85+
f.Packages[pkg] = locked
8686
}
8787

88-
return l.Packages[pkg], nil
88+
return f.Packages[pkg], nil
8989
}
9090

91-
func (l *File) ForceResolve(pkg string) (*Package, error) {
92-
delete(l.Packages, pkg)
93-
return l.Resolve(pkg)
91+
func (f *File) ForceResolve(pkg string) (*Package, error) {
92+
delete(f.Packages, pkg)
93+
return f.Resolve(pkg)
9494
}
9595

96-
func (l *File) Save() error {
97-
return cuecfg.WriteFile(lockFilePath(l.devboxProject), l)
96+
func (f *File) Save() error {
97+
if err := cuecfg.WriteFile(lockFilePath(f.devboxProject), f); err != nil {
98+
return err
99+
}
100+
return updateLocal(f.devboxProject)
98101
}
99102

100-
func (l *File) LegacyNixpkgsPath(pkg string) string {
103+
func (f *File) LegacyNixpkgsPath(pkg string) string {
101104
return fmt.Sprintf(
102105
"github:NixOS/nixpkgs/%s#%s",
103-
l.NixPkgsCommitHash(),
106+
f.NixPkgsCommitHash(),
104107
pkg,
105108
)
106109
}
107110

108-
func (l *File) Get(pkg string) *Package {
109-
entry, hasEntry := l.Packages[pkg]
111+
func (f *File) Get(pkg string) *Package {
112+
entry, hasEntry := f.Packages[pkg]
110113
if !hasEntry || entry.Resolved == "" {
111114
return nil
112115
}
113116
return entry
114117
}
115118

116-
func (l *File) HasAllowInsecurePackages() bool {
117-
for _, pkg := range l.Packages {
119+
func (f *File) HasAllowInsecurePackages() bool {
120+
for _, pkg := range f.Packages {
118121
if pkg.AllowInsecure {
119122
return true
120123
}
@@ -137,8 +140,36 @@ func IsLegacyPackage(pkg string) bool {
137140

138141
// Tidy ensures that the lockfile has the set of packages corresponding to the devbox.json config.
139142
// It gets rid of older packages that are no longer needed.
140-
func (l *File) Tidy() {
141-
l.Packages = lo.PickByKeys(l.Packages, l.devboxProject.Packages())
143+
func (f *File) Tidy() {
144+
f.Packages = lo.PickByKeys(f.Packages, f.devboxProject.Packages())
145+
}
146+
147+
// IsUpToDateAndInstalled returns true if the lockfile is up to date and the
148+
// local hashes match, which generally indicates all packages are correctly
149+
// installed and print-dev-env has been computed and cached.
150+
func (f *File) IsUpToDateAndInstalled() (bool, error) {
151+
if dirty, err := f.isDirty(); err != nil {
152+
return false, err
153+
} else if dirty {
154+
return false, nil
155+
}
156+
return isLocalUpToDate(f.devboxProject)
157+
}
158+
159+
func (f *File) isDirty() (bool, error) {
160+
currentHash, err := cuecfg.Hash(f)
161+
if err != nil {
162+
return false, err
163+
}
164+
fileSystemLockFile, err := GetFile(f.devboxProject)
165+
if err != nil {
166+
return false, err
167+
}
168+
filesystemHash, err := cuecfg.Hash(fileSystemLockFile)
169+
if err != nil {
170+
return false, err
171+
}
172+
return currentHash != filesystemHash, nil
142173
}
143174

144175
func lockFilePath(project devboxProject) string {

internal/lock/resolve.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
// not changed. This can happen when doing `devbox update` and search has
2323
// a newer hash than the lock file but same version. In that case we don't want
2424
// to update because it would be slow and wasteful.
25-
func (l *File) FetchResolvedPackage(pkg string) (*Package, error) {
25+
func (f *File) FetchResolvedPackage(pkg string) (*Package, error) {
2626
name, version, _ := searcher.ParseVersionedPackage(pkg)
2727
if version == "" {
2828
return nil, usererr.New("No version specified for %q.", name)

0 commit comments

Comments
 (0)