package main import ( "bytes" "context" "encoding/json" "errors" "fmt" "os" "os/exec" "strings" ) // managedBy names the tool that installs skills instead of this one, such as // home-manager. skills then only maintains the lock and the Nix store. func managedBy() string { return os.Getenv("SKILLS_MANAGED_BY") } // nixFetch copies a locked source into the Nix store as the current user and // returns its narHash. A later evaluation, even by root without this user's // SSH keys, then finds it there instead of fetching it. func nixFetch(ctx context.Context, url, rev string) (string, error) { ref := "git+" + url + "?rev=" + rev + "&allRefs=1" cmd := exec.CommandContext(ctx, "nix", "--extra-experimental-features", "nix-command flakes", "flake", "prefetch", "--json", ref) var stderr bytes.Buffer cmd.Stderr = &stderr out, err := cmd.Output() if errors.Is(err, exec.ErrNotFound) { return "", fmt.Errorf("SKILLS_MANAGED_BY=%s needs nix, which is not installed", managedBy()) } if err != nil { return "", fmt.Errorf("nix could not fetch %s: %s", url, strings.TrimSpace(stderr.String())) } var p struct { Hash string `json:"hash"` } if err := json.Unmarshal(out, &p); err != nil || p.Hash == "" { return "", fmt.Errorf("unexpected output from nix flake prefetch: %s", out) } return p.Hash, nil }