Skip to content

Commit 3112b8e

Browse files
authored
[flakes] explain better nixpkgs prefetch during devbox add (#624)
## Summary Previously, we'd always have an explicit numbered step for `nixpkgs` when doing `devbox add`. This would look odd like: ``` > devbox add emacs [1/2] nixpkgs registry ... [1/2] nixpkgs registry: Success [2/2] emacs ... [2/2] emacs: Success ``` I couldn't find a way to inspect local nix store to determine if we need to prefetch nixpkgs or not. So, we continue to always do `nix flakes prefetch nixpkgs/<commit>`. But to make the UX more understandable, I pull it out into its own function with its own print statements. IMO this is an improvement but still not awesome. We can continue iterating on it. ## How was it tested? First run (packages not previously present): <img width="1031" alt="Screenshot 2023-02-10 at 3 19 52 PM" src="https://user-images.githubusercontent.com/676452/218221476-5c2250f5-0648-411e-8b6d-01a605d0ee3f.png"> Second run: <img width="984" alt="Screenshot 2023-02-10 at 3 20 30 PM" src="https://user-images.githubusercontent.com/676452/218221528-d7a1a87e-81f0-427a-9dc3-f638b13f3127.png">
1 parent a474c16 commit 3112b8e

File tree

1 file changed

+59
-49
lines changed

1 file changed

+59
-49
lines changed

internal/impl/packages.go

Lines changed: 59 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ func (d *Devbox) addPackagesToProfile(mode installMode) error {
5050
return nil
5151
}
5252

53+
if err := d.ensureNixpkgsPrefetched(); err != nil {
54+
return err
55+
}
56+
5357
pkgs, err := d.pendingPackagesForInstallation()
5458
if err != nil {
5559
return err
@@ -61,75 +65,40 @@ func (d *Devbox) addPackagesToProfile(mode installMode) error {
6165

6266
var msg string
6367
if len(pkgs) == 1 {
64-
msg = fmt.Sprintf("Installing the following package: %s.\n", pkgs[0])
68+
msg = fmt.Sprintf("Installing package: %s.", pkgs[0])
6569
} else {
66-
msg = fmt.Sprintf("Installing the following %d packages: %s.\n", len(pkgs), strings.Join(pkgs, ", "))
70+
msg = fmt.Sprintf("Installing %d packages: %s.", len(pkgs), strings.Join(pkgs, ", "))
6771
}
68-
color.New(color.FgGreen).Fprintf(d.writer, msg)
72+
fmt.Fprintf(d.writer, "\n%s\n\n", msg)
6973

7074
profileDir, err := d.profilePath()
7175
if err != nil {
7276
return err
7377
}
7478

75-
// Append an empty string to prefetch the nixpkgs as a distinct step
76-
// TODO savil. Its a bit odd to always show nixpkgs as being installed during `devbox add`.
77-
// Can we inspect the nix store to check if its been pre-fetched already?
78-
packages := append([]string{""}, pkgs...)
79-
80-
total := len(packages)
81-
for idx, pkg := range packages {
79+
total := len(pkgs)
80+
for idx, pkg := range pkgs {
8281
stepNum := idx + 1
8382

84-
var stepMsg string
85-
if pkg == "" {
86-
stepMsg = fmt.Sprintf("[%d/%d] nixpkgs registry", stepNum, total)
87-
} else {
88-
stepMsg = fmt.Sprintf("[%d/%d] %s", stepNum, total, pkg)
89-
}
83+
stepMsg := fmt.Sprintf("[%d/%d] %s", stepNum, total, pkg)
9084
fmt.Printf("%s\n", stepMsg)
9185

92-
var cmd *exec.Cmd
93-
if pkg == "" {
94-
cmd = exec.Command(
95-
"nix", "flake", "prefetch",
96-
"--extra-experimental-features", "nix-command flakes",
97-
nix.FlakeNixpkgs(d.cfg.Nixpkgs.Commit),
98-
)
99-
cmd.Stdout = d.writer
100-
} else {
101-
cmd = exec.Command(
102-
"nix", "profile", "install",
103-
"--profile", profileDir,
104-
"--extra-experimental-features", "nix-command flakes",
105-
nix.FlakeNixpkgs(d.cfg.Nixpkgs.Commit)+"#"+pkg,
106-
)
107-
cmd.Stdout = &nixPackageInstallWriter{d.writer}
108-
}
86+
cmd := exec.Command(
87+
"nix", "profile", "install",
88+
"--profile", profileDir,
89+
"--extra-experimental-features", "nix-command flakes",
90+
nix.FlakeNixpkgs(d.cfg.Nixpkgs.Commit)+"#"+pkg,
91+
)
92+
cmd.Stdout = &nixPackageInstallWriter{d.writer}
10993

11094
cmd.Env = nix.DefaultEnv()
11195
cmd.Stderr = cmd.Stdout
11296
err = cmd.Run()
113-
11497
if err != nil {
115-
116-
// ExitErrors can give us more information so handle that specially.
117-
var errorMsg string
118-
var exitErr *exec.ExitError
119-
if errors.As(err, &exitErr) {
120-
errorMsg = fmt.Sprintf(
121-
"Error running command %s. Exit status is %d. Command stderr: %s",
122-
cmd, exitErr.ExitCode(), string(exitErr.Stderr),
123-
)
124-
} else {
125-
errorMsg = fmt.Sprintf("Error running command %s. Error: %v", cmd, err)
126-
}
127-
fmt.Fprint(d.writer, errorMsg)
128-
12998
fmt.Fprintf(d.writer, "%s: ", stepMsg)
13099
color.New(color.FgRed).Fprintf(d.writer, "Fail\n")
131100

132-
return errors.New(errorMsg)
101+
return errors.New(commandErrorMessage(cmd, err))
133102
}
134103

135104
fmt.Fprintf(d.writer, "%s: ", stepMsg)
@@ -255,3 +224,44 @@ func resetProfileDirForFlakes(profileDir string) (err error) {
255224

256225
return errors.WithStack(os.Remove(profileDir))
257226
}
227+
228+
// ensureNixpkgsPrefetched runs the prefetch step to download the flake of the registry
229+
func (d *Devbox) ensureNixpkgsPrefetched() error {
230+
fmt.Fprintf(d.writer, "Ensuring nixpkgs registry is downloaded.\n")
231+
cmd := exec.Command(
232+
"nix", "flake", "prefetch",
233+
"--extra-experimental-features", "nix-command flakes",
234+
nix.FlakeNixpkgs(d.cfg.Nixpkgs.Commit),
235+
)
236+
cmd.Env = nix.DefaultEnv()
237+
cmd.Stdout = d.writer
238+
cmd.Stderr = cmd.Stdout
239+
if err := cmd.Run(); err != nil {
240+
fmt.Fprintf(d.writer, "Ensuring nixpkgs registry is downloaded: ")
241+
color.New(color.FgRed).Fprintf(d.writer, "Fail\n")
242+
return errors.New(commandErrorMessage(cmd, err))
243+
}
244+
fmt.Fprintf(d.writer, "Ensuring nixpkgs registry is downloaded: ")
245+
color.New(color.FgGreen).Fprintf(d.writer, "Success\n")
246+
return nil
247+
}
248+
249+
// Consider moving to cobra middleware where this could be generalized. There is
250+
// a complication in that its current form is useful because of the exec.Cmd. This
251+
// would be missing in the middleware, unless we pass it along by wrapping the error in
252+
// another struct.
253+
func commandErrorMessage(cmd *exec.Cmd, err error) string {
254+
var errorMsg string
255+
256+
// ExitErrors can give us more information so handle that specially.
257+
var exitErr *exec.ExitError
258+
if errors.As(err, &exitErr) {
259+
errorMsg = fmt.Sprintf(
260+
"Error running command %s. Exit status is %d. Command stderr: %s",
261+
cmd, exitErr.ExitCode(), string(exitErr.Stderr),
262+
)
263+
} else {
264+
errorMsg = fmt.Sprintf("Error running command %s. Error: %v", cmd, err)
265+
}
266+
return errorMsg
267+
}

0 commit comments

Comments
 (0)