Skip to content

Commit b437a73

Browse files
kadaangcurtis
andauthored
Fix #1868: Fix flake handling (#1870)
## Summary `devbox update` does not work with git flakes. This adds support for git flake to `devbox update` and also ensures that the nix packages are rebuilt when an update is requested. Without this, git flakes will not be refreshed from git. ## How was it tested? 1. Configured a devbox.json with devbox packages and flakes 2. Ran devbox shell without a .devbox folder and without packages being installed in nix 3. Saw packages get installed 4. Ran devbox shell with a .devbox folder and with packages being installed in nix 5. Saw packages verified, but not updated or installed 6. Ran devbox update 7. Saw packages get rebuild with `nix build --refresh` --------- Signed-off-by: Greg Curtis <[email protected]> Co-authored-by: Greg Curtis <[email protected]>
1 parent f914c2c commit b437a73

File tree

5 files changed

+80
-43
lines changed

5 files changed

+80
-43
lines changed

internal/devbox/packages.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ func (d *Devbox) ensureStateIsUpToDate(ctx context.Context, mode installMode) er
260260
}
261261

262262
if mode == install || mode == update || mode == ensure {
263-
if err := d.installPackages(ctx); err != nil {
263+
if err := d.installPackages(ctx, mode); err != nil {
264264
return err
265265
}
266266
}
@@ -384,15 +384,15 @@ func resetProfileDirForFlakes(profileDir string) (err error) {
384384
return errors.WithStack(os.Remove(profileDir))
385385
}
386386

387-
func (d *Devbox) installPackages(ctx context.Context) error {
387+
func (d *Devbox) installPackages(ctx context.Context, mode installMode) error {
388388
// Create plugin directories first because packages might need them
389389
for _, pluginConfig := range d.Config().IncludedPluginConfigs() {
390390
if err := d.PluginManager().CreateFilesForConfig(pluginConfig); err != nil {
391391
return err
392392
}
393393
}
394394

395-
if err := d.installNixPackagesToStore(ctx); err != nil {
395+
if err := d.installNixPackagesToStore(ctx, mode); err != nil {
396396
return err
397397
}
398398

@@ -420,8 +420,8 @@ func (d *Devbox) InstallRunXPackages(ctx context.Context) error {
420420
// This is done by running `nix build` on the flake. We do this so that the
421421
// packages will be available in the nix store when computing the devbox environment
422422
// and installing in the nix profile (even if offline).
423-
func (d *Devbox) installNixPackagesToStore(ctx context.Context) error {
424-
packages, err := d.packagesToInstallInStore(ctx)
423+
func (d *Devbox) installNixPackagesToStore(ctx context.Context, mode installMode) error {
424+
packages, err := d.packagesToInstallInStore(ctx, mode)
425425
if err != nil {
426426
return err
427427
}
@@ -439,12 +439,17 @@ func (d *Devbox) installNixPackagesToStore(ctx context.Context) error {
439439
return err
440440
}
441441

442+
// --no-link to avoid generating the result objects
443+
flags := []string{"--no-link"}
444+
if mode == update {
445+
flags = append(flags, "--refresh")
446+
}
447+
442448
for _, installable := range installables {
443449
args := &nix.BuildArgs{
444450
AllowInsecure: pkg.HasAllowInsecure(),
445-
// --no-link to avoid generating the result objects
446-
Flags: []string{"--no-link"},
447-
Writer: d.stderr,
451+
Flags: flags,
452+
Writer: d.stderr,
448453
}
449454
err = nix.Build(ctx, args, installable)
450455
if err != nil {
@@ -460,7 +465,7 @@ func (d *Devbox) installNixPackagesToStore(ctx context.Context) error {
460465
return err
461466
}
462467

463-
func (d *Devbox) packagesToInstallInStore(ctx context.Context) ([]*devpkg.Package, error) {
468+
func (d *Devbox) packagesToInstallInStore(ctx context.Context, mode installMode) ([]*devpkg.Package, error) {
464469
// First, get and prepare all the packages that must be installed in this project
465470
// and remove non-nix packages from the list
466471
packages := lo.Filter(d.InstallablePackages(), devpkg.IsNix)
@@ -476,6 +481,10 @@ func (d *Devbox) packagesToInstallInStore(ctx context.Context) ([]*devpkg.Packag
476481
return nil, err
477482
}
478483
for _, installable := range installables {
484+
if mode == update {
485+
packagesToInstall = append(packagesToInstall, pkg)
486+
continue
487+
}
479488
storePaths, err := nix.StorePathsFromInstallable(ctx, installable, pkg.HasAllowInsecure())
480489
if err != nil {
481490
return nil, packageInstallErrorHandler(err, pkg, installable)

internal/devbox/update.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ func (d *Devbox) updateDevboxPackage(pkg *devpkg.Package) error {
102102
if err != nil {
103103
return err
104104
}
105+
if resolved == nil {
106+
return nil
107+
}
105108

106109
return d.mergeResolvedPackageToLockfile(pkg, resolved, d.lockfile)
107110
}

internal/devpkg/package.go

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ func newPackage(raw string, isInstallable func() bool, locker lock.Locker) *Pack
142142
// ("nixpkgs" is a devbox package and a flake). When that happens, we
143143
// assume a Devbox package.
144144
parsed, err := flake.ParseInstallable(raw)
145-
if err != nil || isAmbiguous(raw, parsed) {
145+
if err != nil || pkgtype.IsAmbiguous(raw, parsed) {
146146
pkg.IsDevboxPackage = true
147147
pkg.resolve = sync.OnceValue(func() error { return resolve(pkg) })
148148
return pkg
@@ -156,38 +156,6 @@ func newPackage(raw string, isInstallable func() bool, locker lock.Locker) *Pack
156156
return pkg
157157
}
158158

159-
// isAmbiguous returns true if a package string could be a Devbox package or
160-
// a flake installable. For example, "nixpkgs" is both a Devbox package and a
161-
// flake.
162-
func isAmbiguous(raw string, parsed flake.Installable) bool {
163-
// Devbox package strings never have a #attr_path in them.
164-
if parsed.AttrPath != "" {
165-
return false
166-
}
167-
168-
// Indirect installables must have a "flake:" scheme to disambiguate
169-
// them from legacy (unversioned) devbox package strings.
170-
if parsed.Ref.Type == flake.TypeIndirect {
171-
return !strings.HasPrefix(raw, "flake:")
172-
}
173-
174-
// Path installables must have a "path:" scheme, start with "/" or start
175-
// with "./" to disambiguate them from devbox package strings.
176-
if parsed.Ref.Type == flake.TypePath {
177-
if raw[0] == '.' || raw[0] == '/' {
178-
return false
179-
}
180-
if strings.HasPrefix(raw, "path:") {
181-
return false
182-
}
183-
return true
184-
}
185-
186-
// All other flakeref types must have a scheme, so we know those can't
187-
// be devbox package strings.
188-
return false
189-
}
190-
191159
// resolve is the implementation of Package.resolve, where it is wrapped in a
192160
// sync.OnceValue function. It should not be called directly.
193161
func resolve(pkg *Package) error {
@@ -242,7 +210,7 @@ func (p *Package) FlakeInputName() string {
242210
}
243211
}
244212
default:
245-
result = p.installable.String() + "-" + p.Hash()
213+
result = p.installable.Ref.String() + "-" + p.Hash()
246214
}
247215

248216
// replace all non-alphanumeric with dashes

internal/devpkg/pkgtype/flake.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package pkgtype
2+
3+
import (
4+
"strings"
5+
6+
"go.jetpack.io/devbox/nix/flake"
7+
)
8+
9+
func IsFlake(s string) bool {
10+
if IsRunX(s) {
11+
return false
12+
}
13+
parsed, err := flake.ParseInstallable(s)
14+
if err != nil {
15+
return false
16+
}
17+
if IsAmbiguous(s, parsed) {
18+
return false
19+
}
20+
return true
21+
}
22+
23+
// IsAmbiguous returns true if a package string could be a Devbox package or
24+
// a flake installable. For example, "nixpkgs" is both a Devbox package and a
25+
// flake.
26+
func IsAmbiguous(raw string, parsed flake.Installable) bool {
27+
// Devbox package strings never have a #attr_path in them.
28+
if parsed.AttrPath != "" {
29+
return false
30+
}
31+
32+
// Indirect installables must have a "flake:" scheme to disambiguate
33+
// them from legacy (unversioned) devbox package strings.
34+
if parsed.Ref.Type == flake.TypeIndirect {
35+
return !strings.HasPrefix(raw, "flake:")
36+
}
37+
38+
// Path installables must have a "path:" scheme, start with "/" or start
39+
// with "./" to disambiguate them from devbox package strings.
40+
if parsed.Ref.Type == flake.TypePath {
41+
if raw[0] == '.' || raw[0] == '/' {
42+
return false
43+
}
44+
if strings.HasPrefix(raw, "path:") {
45+
return false
46+
}
47+
return true
48+
}
49+
50+
// All other flakeref types must have a scheme, so we know those can't
51+
// be devbox package strings.
52+
return false
53+
}

internal/lock/resolve.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ import (
2828
// a newer hash than the lock file but same version. In that case we don't want
2929
// to update because it would be slow and wasteful.
3030
func (f *File) FetchResolvedPackage(pkg string) (*Package, error) {
31+
if pkgtype.IsFlake(pkg) {
32+
return nil, nil
33+
}
34+
3135
name, version, _ := searcher.ParseVersionedPackage(pkg)
3236
if version == "" {
3337
return nil, usererr.New("No version specified for %q.", name)

0 commit comments

Comments
 (0)