Skip to content

Commit dcce2b1

Browse files
authored
[lockfile] add lockfile.Tidy and call from ensurePackagesAreInstalled (#1109)
## Summary Problem: if I change devbox.json from [email protected] to [email protected], and do devbox install, I still see the [email protected] in the devbox.lock (in addition to the new [email protected]) This PR ensures that when we update installed packages, we also update the lockfile to have the set of packages that match those of the devbox.json config. implementation note: I needed to rename lockfile.Packages to a different name since devboxProject is embedded in the lockfile.File struct and we have a name conflict. Fixes #1095 ## How was it tested? - [x] will add a testscript unit test that matches the steps below: ``` # initial devbox.lock file (devbox) savil@Savil-Srivastavas-MacBook-Pro ~/c/j/devbox (savil/update-lockfile-v2)> cat devbox.lock { "lockfile_version": "1", "packages": { "[email protected]": { "last_modified": "2023-03-31T22:52:29Z", "resolved": "github:NixOS/nixpkgs/242246ee1e58f54d2322227fc5eef53b4a616a31#actionlint", "version": "1.6.23" }, "[email protected]": { "last_modified": "2023-05-01T16:53:22Z", "resolved": "github:NixOS/nixpkgs/8670e496ffd093b60e74e7fa53526aa5920d09eb#go", "version": "1.20.3" }, "[email protected]": { "last_modified": "2023-05-01T16:53:22Z", "resolved": "github:NixOS/nixpkgs/8670e496ffd093b60e74e7fa53526aa5920d09eb#golangci-lint", "version": "1.52.2" } } } # edit the [email protected] to be [email protected]⏎ (devbox) savil@Savil-Srivastavas-MacBook-Pro ~/c/j/devbox (savil/update-lockfile-v2)> vim devbox.json (devbox) savil@Savil-Srivastavas-MacBook-Pro ~/c/j/devbox (savil/update-lockfile-v2)> devbox install Ensuring packages are installed. Finished installing packages. (devbox) savil@Savil-Srivastavas-MacBook-Pro ~/c/j/devbox (savil/update-lockfile-v2)> cat devbox.lock { "lockfile_version": "1", "packages": { "[email protected]": { "last_modified": "2023-03-31T22:52:29Z", "resolved": "github:NixOS/nixpkgs/242246ee1e58f54d2322227fc5eef53b4a616a31#actionlint", "version": "1.6.23" }, "[email protected]": { "last_modified": "2023-05-01T16:53:22Z", "resolved": "github:NixOS/nixpkgs/8670e496ffd093b60e74e7fa53526aa5920d09eb#go_1_19", "version": "1.19.8" }, "[email protected]": { "last_modified": "2023-05-01T16:53:22Z", "resolved": "github:NixOS/nixpkgs/8670e496ffd093b60e74e7fa53526aa5920d09eb#golangci-lint", "version": "1.52.2" } } }⏎ # Notice that the older [email protected] is now gone ```
1 parent 7223bef commit dcce2b1

File tree

7 files changed

+81
-13
lines changed

7 files changed

+81
-13
lines changed

internal/impl/packages.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ func (d *Devbox) ensurePackagesAreInstalled(ctx context.Context, mode installMod
199199
return err
200200
}
201201

202+
// Ensure we clean out packages that are no longer needed.
203+
d.lockfile.Tidy()
204+
202205
if err = d.lockfile.Save(); err != nil {
203206
return err
204207
}

internal/lock/interfaces.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package lock
66
type devboxProject interface {
77
ConfigHash() (string, error)
88
NixPkgsCommitHash() string
9+
Packages() []string
910
ProjectDir() string
1011
}
1112

@@ -14,7 +15,7 @@ type resolver interface {
1415
}
1516

1617
type Locker interface {
17-
devboxProject
1818
resolver
1919
LegacyNixpkgsPath(string) string
20+
ProjectDir() string
2021
}

internal/lock/lockfile.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"path/filepath"
1111
"strings"
1212

13+
"github.com/samber/lo"
1314
"go.jetpack.io/devbox/internal/boxcli/featureflag"
1415
"go.jetpack.io/devbox/internal/cuecfg"
1516
)
@@ -99,7 +100,7 @@ func (l *File) Save() error {
99100
return nil
100101
}
101102

102-
return cuecfg.WriteFile(lockFilePath(l), l)
103+
return cuecfg.WriteFile(lockFilePath(l.devboxProject), l)
103104
}
104105

105106
func (l *File) LegacyNixpkgsPath(pkg string) string {
@@ -121,12 +122,18 @@ func IsLegacyPackage(pkg string) bool {
121122
return !IsVersionedPackage(pkg) &&
122123
!strings.Contains(pkg, ":") &&
123124
// We don't support absolute paths without "path:" prefix, but adding here
124-
// just inc ase we ever do.
125+
// just in case we ever do.
125126
// Landau note: I don't think we should support it, it's hard to read and a
126127
// bit ambiguous.
127128
!strings.HasPrefix(pkg, "/")
128129
}
129130

131+
// Tidy ensures that the lockfile has the set of packages corresponding to the devbox.json config.
132+
// It gets rid of older packages that are no longer needed.
133+
func (l *File) Tidy() {
134+
l.Packages = lo.PickByKeys(l.Packages, l.devboxProject.Packages())
135+
}
136+
130137
func lockFilePath(project devboxProject) string {
131138
return filepath.Join(project.ProjectDir(), "devbox.lock")
132139
}

internal/nix/input_test.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,6 @@ type lockfile struct {
106106
projectDir string
107107
}
108108

109-
func (lockfile) ConfigHash() (string, error) {
110-
return "", nil
111-
}
112-
113-
func (lockfile) NixPkgsCommitHash() string {
114-
return ""
115-
}
116-
117109
func (l *lockfile) ProjectDir() string {
118110
return l.projectDir
119111
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Testscript to ensure lockfile is updated to remove the older version of a package
2+
3+
# start with a devbox.json having [email protected]
4+
cp devbox_original.json devbox.json
5+
exec devbox install
6+
devboxlock.packages.contains devbox.lock [email protected]
7+
8+
# change devbox.json to instead have [email protected]
9+
cp devbox_changed.json devbox.json
10+
exec devbox install
11+
devboxlock.packages.contains devbox.lock [email protected]
12+
! devboxlock.packages.contains devbox.lock [email protected]
13+
14+
15+
-- devbox_original.json --
16+
{
17+
"packages": [
18+
19+
]
20+
}
21+
22+
-- devbox_changed.json --
23+
{
24+
"packages": [
25+
26+
]
27+
}
28+

testscripts/testrunner/assert.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"go.jetpack.io/devbox/internal/devconfig"
1212
"go.jetpack.io/devbox/internal/envir"
13+
"go.jetpack.io/devbox/internal/lock"
1314
)
1415

1516
// Usage: env.path.len <number>
@@ -36,7 +37,7 @@ func assertPathLength(script *testscript.TestScript, neg bool, args []string) {
3637

3738
func assertDevboxJSONPackagesContains(script *testscript.TestScript, neg bool, args []string) {
3839
if len(args) != 2 {
39-
script.Fatalf("usage: json.list.contains list.json value")
40+
script.Fatalf("usage: devboxjson.packages.contains devbox.json value")
4041
}
4142

4243
data := script.ReadFile(args[0])
@@ -59,6 +60,28 @@ func assertDevboxJSONPackagesContains(script *testscript.TestScript, neg bool, a
5960
}
6061
}
6162

63+
func assertDevboxLockPackagesContains(script *testscript.TestScript, neg bool, args []string) {
64+
if len(args) != 2 {
65+
script.Fatalf("usage: devboxlock.packages.contains devbox.lock value")
66+
}
67+
68+
data := script.ReadFile(args[0])
69+
lockfile := lock.File{}
70+
err := json.Unmarshal([]byte(data), &lockfile)
71+
script.Check(err)
72+
73+
expected := args[1]
74+
if _, ok := lockfile.Packages[expected]; ok {
75+
if neg {
76+
script.Fatalf("value '%s' found in %s", expected, args[0])
77+
}
78+
} else {
79+
if !neg {
80+
script.Fatalf("value '%s' not found in '%s'", expected, args[0])
81+
}
82+
}
83+
}
84+
6285
// Usage: json.superset superset.json subset.json
6386
// Checks that the JSON in superset.json contains all the keys and values
6487
// present in subset.json.

testscripts/testrunner/testrunner.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,29 @@ func globDirs(pattern string) []string {
6767
return directories
6868
}
6969

70+
// copyFileCmd enables copying files within the WORKDIR
71+
func copyFileCmd(script *testscript.TestScript, neg bool, args []string) {
72+
if len(args) < 2 {
73+
script.Fatalf("usage: cp <from-file> <to-file>")
74+
}
75+
if neg {
76+
script.Fatalf("neg does not make sense for this command")
77+
}
78+
err := script.Exec("cp", script.MkAbs(args[0]), script.MkAbs(args[1]))
79+
script.Check(err)
80+
}
81+
7082
func getTestscriptParams(t *testing.T, dir string) testscript.Params {
7183
return testscript.Params{
7284
Dir: dir,
7385
RequireExplicitExec: true,
7486
TestWork: false, // Set to true if you're trying to debug a test.
7587
Setup: func(env *testscript.Env) error { return setupTestEnv(t, env) },
7688
Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){
77-
"env.path.len": assertPathLength,
89+
"cp": copyFileCmd,
7890
"devboxjson.packages.contains": assertDevboxJSONPackagesContains,
91+
"devboxlock.packages.contains": assertDevboxLockPackagesContains,
92+
"env.path.len": assertPathLength,
7993
"json.superset": assertJSONSuperset,
8094
"path.order": assertPathOrder,
8195
"source.path": sourcePath,

0 commit comments

Comments
 (0)