diff --git a/internal/devbox/packages.go b/internal/devbox/packages.go index 0c2240bda0d..c9681b42c7d 100644 --- a/internal/devbox/packages.go +++ b/internal/devbox/packages.go @@ -430,80 +430,89 @@ func (d *Devbox) InstallRunXPackages(ctx context.Context) error { // and installing in the nix profile (even if offline). func (d *Devbox) installNixPackagesToStore(ctx context.Context, mode installMode) error { packages, err := d.packagesToInstallInStore(ctx, mode) - if err != nil { + if err != nil || len(packages) == 0 { return err } - stepNum := 0 - total := len(packages) - for _, pkg := range packages { - stepNum += 1 + // --no-link to avoid generating the result objects + flags := []string{"--no-link"} + if mode == update { + flags = append(flags, "--refresh") + } - stepMsg := fmt.Sprintf("[%d/%d] %s", stepNum, total, pkg) - fmt.Fprintf(d.stderr, stepMsg+"\n") + args := &nix.BuildArgs{ + Flags: flags, + Writer: d.stderr, + } + args.ExtraSubstituter, _ = d.providers.NixCache.URI(ctx) + // TODO (Landau): handle errors that are not auth.ErrNotLoggedIn + // Only lookup credentials if we have a cache to use + if args.ExtraSubstituter != "" { + creds, err := d.providers.NixCache.Credentials(ctx) + if err == nil { + args.Env = creds.Env() + } - installables, err := pkg.Installables() + u, err := user.Current() if err != nil { - return err + err = redact.Errorf("lookup current user: %v", err) + debug.Log("error configuring cache: %v", err) } + err = d.providers.NixCache.Configure(ctx, u.Username) + if err != nil { + debug.Log("error configuring cache: %v", err) - // --no-link to avoid generating the result objects - flags := []string{"--no-link"} - if mode == update { - flags = append(flags, "--refresh") + var daemonErr *nix.DaemonError + if errors.As(err, &daemonErr) { + // Error here to give the user a chance to restart the daemon. + return usererr.New("Devbox configured Nix to use %q as a cache. Please restart the Nix daemon and re-run Devbox.", args.ExtraSubstituter) + } + // Other errors indicate we couldn't update nix.conf, so just warn and continue + // by building from source if necessary. + ux.Fwarning(d.stderr, "Devbox was unable to configure Nix to use your organization's private cache. Some packages might be built from source.") } + } - args := &nix.BuildArgs{ - AllowInsecure: pkg.HasAllowInsecure(), - Flags: flags, - Writer: d.stderr, + packageNames := lo.Map( + packages, + func(p *devpkg.Package, _ int) string { return p.Raw }, + ) + ux.Finfo( + d.stderr, + "Installing the following packages to the nix store: %s\n", + strings.Join(packageNames, ", "), + ) + + installables := map[bool][]string{false: {}, true: {}} + for _, pkg := range packages { + pkgInstallables, err := pkg.Installables() + if err != nil { + return err } - args.ExtraSubstituter, _ = d.providers.NixCache.URI(ctx) - // TODO (Landau): handle errors that are not auth.ErrNotLoggedIn - // Only lookup credentials if we have a cache to use - if args.ExtraSubstituter != "" { - creds, err := d.providers.NixCache.Credentials(ctx) - if err == nil { - args.Env = creds.Env() - } + installables[pkg.HasAllowInsecure()] = append( + installables[pkg.HasAllowInsecure()], + pkgInstallables..., + ) + } - u, err := user.Current() - if err != nil { - err = redact.Errorf("lookup current user: %v", err) - debug.Log("error configuring cache: %v", err) - } - err = d.providers.NixCache.Configure(ctx, u.Username) - if err != nil { - debug.Log("error configuring cache: %v", err) - - var daemonErr *nix.DaemonError - if errors.As(err, &daemonErr) { - // Error here to give the user a chance to restart the daemon. - return usererr.New("Devbox configured Nix to use %q as a cache. Please restart the Nix daemon and re-run Devbox.", args.ExtraSubstituter) - } - // Other errors indicate we couldn't update nix.conf, so just warn and continue - // by building from source if necessary. - ux.Fwarning(d.stderr, "Devbox was unable to configure Nix to use your organization's private cache. Some packages might be built from source.") - } + for allowInsecure, installables := range installables { + if len(installables) == 0 { + continue } - for _, installable := range installables { - eventStart := time.Now() - err = nix.Build(ctx, args, installable) - if err != nil { - fmt.Fprintf(d.stderr, "%s: ", stepMsg) - color.New(color.FgRed).Fprintf(d.stderr, "Fail\n") - return packageInstallErrorHandler(err, pkg, installable) - } - telemetry.Event(telemetry.EventNixBuildSuccess, telemetry.Metadata{ - EventStart: eventStart, - Packages: []string{pkg.Raw}, - }) + eventStart := time.Now() + args.AllowInsecure = allowInsecure + err = nix.Build(ctx, args, installables...) + if err != nil { + color.New(color.FgRed).Fprintf(d.stderr, "Fail\n") + return err } - - fmt.Fprintf(d.stderr, "%s: ", stepMsg) - color.New(color.FgGreen).Fprintf(d.stderr, "Success\n") + telemetry.Event(telemetry.EventNixBuildSuccess, telemetry.Metadata{ + EventStart: eventStart, + Packages: packageNames, + }) } - return err + + return nil } func (d *Devbox) packagesToInstallInStore(ctx context.Context, mode installMode) ([]*devpkg.Package, error) { @@ -540,7 +549,7 @@ func (d *Devbox) packagesToInstallInStore(ctx context.Context, mode installMode) } } - return packagesToInstall, nil + return lo.Uniq(packagesToInstall), nil } // packageInstallErrorHandler checks for two kinds of errors to print custom messages for so that Devbox users