Skip to content
Merged
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
129 changes: 69 additions & 60 deletions internal/devbox/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down