Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions internal/devbox/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,12 +358,12 @@ func (d *Devbox) ensureStateIsUpToDate(ctx context.Context, mode installMode) er
)
}

return d.updateLockfile(recomputeState)
return d.updateLockfile(mode, recomputeState)
}

// updateLockfile will ensure devbox.lock is up to date with the current state of the project.update
// If recomputeState is true, then we will also update the state.json file.
func (d *Devbox) updateLockfile(recomputeState bool) error {
func (d *Devbox) updateLockfile(mode installMode, recomputeState bool) error {
// Ensure we clean out packages that are no longer needed.
d.lockfile.Tidy()

Expand All @@ -374,9 +374,11 @@ func (d *Devbox) updateLockfile(recomputeState bool) error {
}
}

// Update plugin versions in lockfile.
// Update plugin versions in lockfile. In ensure mode (shell, run, install)
// only fill in missing versions; changing an existing version is left to
// commands that are expected to modify the lockfile (add, rm, update).
for _, pluginConfig := range d.Config().IncludedPluginConfigs() {
if err := d.PluginManager().UpdateLockfileVersion(pluginConfig); err != nil {
if err := d.PluginManager().UpdateLockfileVersion(pluginConfig, mode != ensure); err != nil {
return err
}
}
Expand Down
9 changes: 8 additions & 1 deletion internal/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,21 @@ func (m *Manager) CreateFilesForConfig(cfg *Config) error {
return nil
}

func (m *Manager) UpdateLockfileVersion(cfg *Config) error {
// UpdateLockfileVersion records the plugin's version in the lockfile. An
// existing version is only replaced when overwrite is true, so that commands
// which aren't meant to modify devbox.lock (shell, run, install) don't rewrite
// it just because the running Devbox embeds a newer or older built-in plugin.
func (m *Manager) UpdateLockfileVersion(cfg *Config, overwrite bool) error {
pkg := cfg.Source
locked := m.lockfile.Packages[pkg.LockfileKey()]
// plugins that are not triggered by packages don't have a lockfile entry
// this may change if we decide to store all plugins in the lockfile
if locked == nil {
return nil
}
if locked.PluginVersion != "" && !overwrite {
return nil
}
locked.PluginVersion = cfg.Version
return m.lockfile.Save()
}
Expand Down
47 changes: 47 additions & 0 deletions internal/plugin/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.jetify.com/devbox/internal/lock"
"go.jetify.com/devbox/nix/flake"
)

Expand Down Expand Up @@ -126,3 +127,49 @@ func TestBuildConfigTemplatePlaceholders(t *testing.T) {
"Virtenv should be <projectDir>/.devbox/virtenv/<plugin.name>",
)
}

// lockProjectForTest is a minimal lock.File project rooted at dir.
type lockProjectForTest struct{ dir string }

func (p lockProjectForTest) ConfigHash() (string, error) { return "", nil }
func (p lockProjectForTest) Stdenv() flake.Ref { return flake.Ref{} }
func (p lockProjectForTest) ProjectDir() string { return p.dir }
func (p lockProjectForTest) AllPackageNamesIncludingRemovedTriggerPackages() []string {
return nil
}

// TestUpdateLockfileVersion verifies that an existing plugin_version is only
// overwritten when requested, so that running a different Devbox version (which
// embeds different built-in plugin versions) doesn't rewrite devbox.lock.
func TestUpdateLockfileVersion(t *testing.T) {
tests := []struct {
name string
locked string
overwrite bool
want string
}{
{name: "fills missing version", locked: "", overwrite: false, want: "0.0.2"},
{name: "keeps existing version", locked: "0.0.1", overwrite: false, want: "0.0.1"},
{name: "overwrites existing version", locked: "0.0.1", overwrite: true, want: "0.0.2"},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
lockfile, err := lock.GetFile(lockProjectForTest{dir: t.TempDir()})
require.NoError(t, err)
lockfile.Packages["nodejs@22"] = &lock.Package{
Resolved: "github:NixOS/nixpkgs/abc#nodejs_22",
PluginVersion: test.locked,
}

cfg := &Config{
PluginOnlyData: PluginOnlyData{
Source: fakeIncludable{name: "nodejs@22"},
Version: "0.0.2",
},
}
m := NewManager(WithLockfile(lockfile))
require.NoError(t, m.UpdateLockfileVersion(cfg, test.overwrite))
assert.Equal(t, test.want, lockfile.Packages["nodejs@22"].PluginVersion)
})
}
}
Loading