Skip to content

Commit

Permalink
[flakes] save commit-hash to file, and inspect prior to prefetching n…
Browse files Browse the repository at this point in the history
…ixpkgs (#681)

## Summary

This PR saves the nixpkgs commit hash to a file at
`~/.cache/devbox/nixpkgs.txt`.
We then inspect this file for the nixpkgs commit hash of the devbox
project,
and if it is present then we skip the `nix flake prefetch
nixpkgs/<commit>` call.


Alternate approach:
I tried waiting 100 milliseconds to see if the `nix flake prefetch`
command is still
working as a proxy for deciding if the nixpkgs is being downloaded or
not. However,
this does NOT work because the nix progress indicator fails to render if
we mess with
the output from `nix flake prefetch`. See:
#680

## How was it tested?

1. edited devbox.json to have a new nixpkgs commit hash, and started
`devbox shell` . Observed the flake prefetch output as before.
2. re-ran `devbox shell` and this time there was no output about nixpkgs
being downloaded. As expected.
  • Loading branch information
savil committed Feb 27, 2023
1 parent c4de4dc commit 999dcf6
Showing 1 changed file with 79 additions and 1 deletion.
80 changes: 79 additions & 1 deletion internal/impl/packages.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package impl

import (
"encoding/json"
"fmt"
"io/fs"
"os"
"os/exec"
"path/filepath"
Expand All @@ -13,6 +15,7 @@ import (
"go.jetpack.io/devbox/internal/debug"
"go.jetpack.io/devbox/internal/fileutil"
"go.jetpack.io/devbox/internal/nix"
"go.jetpack.io/devbox/internal/xdg"
)

// packages.go has functions for adding, removing and getting info about nix packages
Expand Down Expand Up @@ -227,6 +230,21 @@ func resetProfileDirForFlakes(profileDir string) (err error) {

// ensureNixpkgsPrefetched runs the prefetch step to download the flake of the registry
func (d *Devbox) ensureNixpkgsPrefetched() error {
// Look up the cached map of commitHash:nixStoreLocation
commitToLocation, err := d.nixpkgsCommitFileContents()
if err != nil {
return err
}

// Check if this nixpkgs.Commit is located in the local /nix/store
location, isPresent := commitToLocation[d.cfg.Nixpkgs.Commit]
if isPresent {
if fi, err := os.Stat(location); err == nil && fi.IsDir() {
// The nixpkgs for this commit hash is present, so we don't need to prefetch
return nil
}
}

fmt.Fprintf(d.writer, "Ensuring nixpkgs registry is downloaded.\n")
cmd := exec.Command(
"nix", "flake", "prefetch",
Expand All @@ -243,5 +261,65 @@ func (d *Devbox) ensureNixpkgsPrefetched() error {
}
fmt.Fprintf(d.writer, "Ensuring nixpkgs registry is downloaded: ")
color.New(color.FgGreen).Fprintf(d.writer, "Success\n")
return nil

return d.saveToNixpkgsCommitFile(commitToLocation)
}

func (d *Devbox) nixpkgsCommitFileContents() (map[string]string, error) {
path := nixpkgsCommitFilePath()
if !fileutil.Exists(path) {
return map[string]string{}, nil
}

contents, err := os.ReadFile(path)
if err != nil {
return nil, errors.WithStack(err)
}

commitToLocation := map[string]string{}
if err := json.Unmarshal(contents, &commitToLocation); err != nil {
return nil, errors.WithStack(err)
}
return commitToLocation, nil
}

func (d *Devbox) saveToNixpkgsCommitFile(commitToLocation map[string]string) error {
// Make a query to get the /nix/store path for this commit hash.
cmd := exec.Command("nix", "flake", "prefetch", "--json",
nix.FlakeNixpkgs(d.cfg.Nixpkgs.Commit),
)
cmd.Args = append(cmd.Args, nix.ExperimentalFlags()...)
out, err := cmd.Output()
if err != nil {
return errors.WithStack(err)
}

// read the json response
var prefetchData struct {
StorePath string `json:"storePath"`
}
if err := json.Unmarshal(out, &prefetchData); err != nil {
return errors.WithStack(err)
}

// Ensure the nixpkgs commit file path exists so we can write an update to it
path := nixpkgsCommitFilePath()
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil && !errors.Is(err, fs.ErrExist) {
return errors.WithStack(err)
}

// write to the map, jsonify it, and write that json to the nixpkgsCommit file
commitToLocation[d.cfg.Nixpkgs.Commit] = prefetchData.StorePath
serialized, err := json.Marshal(commitToLocation)
if err != nil {
return errors.WithStack(err)
}

err = os.WriteFile(path, serialized, 0644)
return errors.WithStack(err)
}

func nixpkgsCommitFilePath() string {
cacheDir := xdg.CacheSubpath("devbox")
return filepath.Join(cacheDir, "nixpkgs.json")
}

0 comments on commit 999dcf6

Please sign in to comment.