Skip to content

Commit 3bb13c1

Browse files
authored
[perf] Add flag to fix missing store paths (#2102)
1 parent a1f4f40 commit 3bb13c1

File tree

13 files changed

+145
-15
lines changed

13 files changed

+145
-15
lines changed

internal/boxcli/install.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,17 @@ import (
1111

1212
"go.jetpack.io/devbox/internal/devbox"
1313
"go.jetpack.io/devbox/internal/devbox/devopt"
14+
"go.jetpack.io/devbox/internal/devpkg"
15+
"go.jetpack.io/devbox/internal/ux"
1416
)
1517

18+
type installCmdFlags struct {
19+
runCmdFlags
20+
tidyLockfile bool
21+
}
22+
1623
func installCmd() *cobra.Command {
17-
flags := runCmdFlags{}
24+
flags := installCmdFlags{}
1825
command := &cobra.Command{
1926
Use: "install",
2027
Short: "Install all packages mentioned in devbox.json",
@@ -26,11 +33,16 @@ func installCmd() *cobra.Command {
2633
}
2734

2835
flags.config.register(command)
36+
command.Flags().BoolVar(
37+
&flags.tidyLockfile, "tidy-lockfile", false,
38+
"Fix missing store paths in the devbox.lock file.",
39+
// Could potentially do more in the future.
40+
)
2941

3042
return command
3143
}
3244

33-
func installCmdFunc(cmd *cobra.Command, flags runCmdFlags) error {
45+
func installCmdFunc(cmd *cobra.Command, flags installCmdFlags) error {
3446
// Check the directory exists.
3547
box, err := devbox.Open(&devopt.Opts{
3648
Dir: flags.config.path,
@@ -40,9 +52,18 @@ func installCmdFunc(cmd *cobra.Command, flags runCmdFlags) error {
4052
if err != nil {
4153
return errors.WithStack(err)
4254
}
43-
if err = box.Install(cmd.Context()); err != nil {
55+
ctx := cmd.Context()
56+
if flags.tidyLockfile {
57+
ctx = ux.HideMessage(ctx, devpkg.MissingStorePathsWarning)
58+
}
59+
if err = box.Install(ctx); err != nil {
4460
return errors.WithStack(err)
4561
}
62+
if flags.tidyLockfile {
63+
if err = box.FixMissingStorePaths(ctx); err != nil {
64+
return errors.WithStack(err)
65+
}
66+
}
4667
fmt.Fprintln(cmd.ErrOrStderr(), "Finished installing packages.")
4768
return nil
4869
}

internal/boxcli/pull.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ func pullCmdFunc(cmd *cobra.Command, url string, flags *pullCmdFlags) error {
106106

107107
return installCmdFunc(
108108
cmd,
109-
runCmdFlags{config: configFlags{pathFlag: pathFlag{path: flags.config.path}}},
109+
installCmdFlags{
110+
runCmdFlags: runCmdFlags{config: configFlags{pathFlag: pathFlag{path: flags.config.path}}},
111+
},
110112
)
111113
}
112114

internal/devbox/devbox.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,7 @@ func (d *Devbox) configureProcessCompose(ctx context.Context, processComposeOpts
856856
// some additional processing. The computeEnv environment won't necessarily
857857
// represent the final "devbox run" or "devbox shell" environments.
858858
func (d *Devbox) computeEnv(ctx context.Context, usePrintDevEnvCache bool) (map[string]string, error) {
859+
defer debug.FunctionTimer().End()
859860
defer trace.StartRegion(ctx, "devboxComputeEnv").End()
860861

861862
// Append variables from current env if --pure is not passed

internal/devbox/nixprofile.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
//
1717
// It also removes any packages from the nix profile that are no longer in the buildInputs.
1818
func (d *Devbox) syncNixProfileFromFlake(ctx context.Context) error {
19+
defer debug.FunctionTimer().End()
1920
// Get the computed Devbox environment from the generated flake
2021
env, err := d.computeEnv(ctx, false /*usePrintDevEnvCache*/)
2122
if err != nil {

internal/devbox/packages.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ func (d *Devbox) updateLockfile(recomputeState bool) error {
335335
// - the generated flake
336336
// - the nix-profile
337337
func (d *Devbox) recomputeState(ctx context.Context) error {
338+
defer debug.FunctionTimer().End()
338339
if err := shellgen.GenerateForPrintEnv(ctx, d); err != nil {
339340
return err
340341
}
@@ -643,3 +644,49 @@ func (d *Devbox) moveAllowInsecureFromLockfile(writer io.Writer, lockfile *lock.
643644

644645
return nil
645646
}
647+
648+
func (d *Devbox) FixMissingStorePaths(ctx context.Context) error {
649+
packages := d.InstallablePackages()
650+
for _, pkg := range packages {
651+
if pkg.IsRunX() {
652+
continue
653+
}
654+
existingStorePaths, err := pkg.GetResolvedStorePaths()
655+
if err != nil {
656+
return err
657+
}
658+
659+
if len(existingStorePaths) > 0 {
660+
continue
661+
}
662+
663+
installables, err := pkg.Installables()
664+
if err != nil {
665+
return err
666+
}
667+
668+
outputs := []lock.Output{}
669+
for _, installable := range installables {
670+
storePaths, err := nix.StorePathsFromInstallable(ctx, installable, pkg.HasAllowInsecure())
671+
if err != nil {
672+
return err
673+
}
674+
if len(storePaths) == 0 {
675+
return fmt.Errorf("no store paths found for package %s", pkg.Raw)
676+
}
677+
for _, storePath := range storePaths {
678+
parts := nix.NewStorePathParts(storePath)
679+
outputs = append(outputs, lock.Output{
680+
Path: storePath,
681+
Name: parts.Output,
682+
// Ugh, not sure this is true, but it's more true than not.
683+
Default: true,
684+
})
685+
}
686+
}
687+
if err = d.lockfile.SetOutputsForPackage(pkg.Raw, outputs); err != nil {
688+
return err
689+
}
690+
}
691+
return d.lockfile.Save()
692+
}

internal/devbox/update.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ func (d *Devbox) Update(ctx context.Context, opts devopt.UpdateOpts) error {
7575
// It will return an error if .devbox/gen/flake is missing
7676
// TODO: Remove this if it's not needed.
7777
_ = nix.FlakeUpdate(shellgen.FlakePath(d))
78+
79+
// fix any missing store paths.
80+
if err = d.FixMissingStorePaths(ctx); err != nil {
81+
return errors.WithStack(err)
82+
}
83+
7884
return plugin.Update()
7985
}
8086

internal/devpkg/package.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -736,19 +736,19 @@ func (p *Package) GetResolvedStorePaths() ([]string, error) {
736736
return storePaths, nil
737737
}
738738

739+
const MissingStorePathsWarning = "Outputs for %s are not in lockfile. To fix this issue and improve performance, please run " +
740+
"`devbox install --tidy-lockfile`\n"
741+
739742
func (p *Package) GetStorePaths(ctx context.Context, w io.Writer) ([]string, error) {
740743
storePathsForPackage, err := p.GetResolvedStorePaths()
741744
if err != nil || len(storePathsForPackage) > 0 {
742745
return storePathsForPackage, err
743746
}
744747

745-
// No fast path, we need to query nix.
746-
// TODO we should give people the option to add paths to lockfile.
747-
ux.Fwarning(
748-
w,
749-
"Outputs for %s are not in lockfile. Fetching store paths from nix, this may take a while\n",
750-
p.Raw,
751-
)
748+
if p.IsDevboxPackage {
749+
// No fast path, we need to query nix.
750+
ux.FHidableWarning(ctx, w, MissingStorePathsWarning, p.Raw)
751+
}
752752

753753
installables, err := p.Installables()
754754
if err != nil {

internal/lock/lockfile.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/samber/lo"
1515
"go.jetpack.io/devbox/internal/cachehash"
1616
"go.jetpack.io/devbox/internal/devpkg/pkgtype"
17+
"go.jetpack.io/devbox/internal/nix"
1718
"go.jetpack.io/devbox/internal/searcher"
1819
"go.jetpack.io/pkg/runx/impl/types"
1920

@@ -199,6 +200,21 @@ func (f *File) IsUpToDateAndInstalled(isFish bool) (bool, error) {
199200
})
200201
}
201202

203+
func (f *File) SetOutputsForPackage(pkg string, outputs []Output) error {
204+
p, err := f.Resolve(pkg)
205+
if err != nil {
206+
return err
207+
}
208+
if p.Systems == nil {
209+
p.Systems = map[string]*SystemInfo{}
210+
}
211+
if p.Systems[nix.System()] == nil {
212+
p.Systems[nix.System()] = &SystemInfo{}
213+
}
214+
p.Systems[nix.System()].Outputs = outputs
215+
return f.Save()
216+
}
217+
202218
func (f *File) isDirty() (bool, error) {
203219
currentHash, err := cachehash.JSON(f)
204220
if err != nil {

internal/nix/nixprofile/profile.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/pkg/errors"
1414
"github.com/samber/lo"
15+
"go.jetpack.io/devbox/internal/debug"
1516
"go.jetpack.io/devbox/internal/devpkg"
1617
"go.jetpack.io/devbox/internal/lock"
1718
"go.jetpack.io/devbox/internal/nix"
@@ -23,6 +24,7 @@ func ProfileListItems(
2324
writer io.Writer,
2425
profileDir string,
2526
) ([]*NixProfileListItem, error) {
27+
defer debug.FunctionTimer().End()
2628
output, err := nix.ProfileList(writer, profileDir, true /*useJSON*/)
2729
if err != nil {
2830
// fallback to legacy profile list

internal/nix/profiles.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type ProfileInstallArgs struct {
3838
}
3939

4040
func ProfileInstall(ctx context.Context, args *ProfileInstallArgs) error {
41+
defer debug.FunctionTimer().End()
4142
if !IsInsecureAllowed() && PackageIsInsecure(args.Installable) {
4243
knownVulnerabilities := PackageKnownVulnerabilities(args.Installable)
4344
errString := fmt.Sprintf("Package %s is insecure. \n\n", args.Installable)
@@ -78,6 +79,7 @@ func ProfileInstall(ctx context.Context, args *ProfileInstallArgs) error {
7879
// ProfileRemove removes packages from a profile.
7980
// WARNING, don't use indexes, they are not supported by nix 2.20+
8081
func ProfileRemove(profilePath string, packageNames ...string) error {
82+
defer debug.FunctionTimer().End()
8183
cmd := command(
8284
append([]string{
8385
"profile", "remove",

0 commit comments

Comments
 (0)