Skip to content

Commit f53929d

Browse files
authored
[remove nixpkgs] Remove CAPath, and only enable for nix >= 2.17 (#1332)
## Summary We want to restrict the feature to nix 2.17+, because it enables pure mode nix when using just `fromPath` in `builtins.fetchClosure`. This greatly simplifies the `devbox.lock` issues with caPath, and will improve the UX. This Remove Nixpkgs feature is most beneficial to new users who have not downloaded the nixpkgs they need. These users are most likely to have the latest nix installed for them, so I think restricting to nix >= 2.17 is an acceptable tradeoff. ## How was it tested? did a sanity test on x86-darwin. ~need to test better~ on a "supported system" where nix >= 2.17: - updated Devbox Cloud to run nix 2.17 - in `examples/development/python/pip`, did `devbox shell`. Since we have system-info for `python` and not `pip`, the generated `flake.nix` looked like this: ``` { description = "A devbox shell"; inputs = { nixpkgs.url = "http://lax.devbox-nixed.internal/nixos/nixpkgs/archive/8670e496ffd093b60e74e7fa53526aa5920d09eb.tar.gz"; nixpkgs-8670e4.url = "http://lax.devbox-nixed.internal/nixos/nixpkgs/archive/8670e496ffd093b60e74e7fa53526aa5920d09eb.tar.gz"; }; outputs = { self, nixpkgs, nixpkgs-8670e4, }: let pkgs = nixpkgs.legacyPackages.x86_64-linux; nixpkgs-8670e4-pkgs = (import nixpkgs-8670e4 { system = "x86_64-linux"; config.allowUnfree = true; config.permittedInsecurePackages = [ ]; }); in { devShells.x86_64-linux.default = pkgs.mkShell { buildInputs = [ (builtins.fetchClosure{ fromStore = "https://cache.nixos.org"; fromPath = "/nix/store/95cxzy2hpizr23343b8bskl4yacf4b3l-python3-3.10.11"; inputAddressed = true; }) nixpkgs-8670e4-pkgs.python310Packages.pip ]; }; }; } ```
1 parent 7988140 commit f53929d

File tree

9 files changed

+52
-200
lines changed

9 files changed

+52
-200
lines changed

internal/devpkg/package.go

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"fmt"
1010
"io"
1111
"net/url"
12-
"os"
1312
"path/filepath"
1413
"regexp"
1514
"strings"
@@ -21,7 +20,7 @@ import (
2120
"go.jetpack.io/devbox/internal/cuecfg"
2221
"go.jetpack.io/devbox/internal/lock"
2322
"go.jetpack.io/devbox/internal/nix"
24-
"go.jetpack.io/devbox/internal/ux"
23+
"go.jetpack.io/devbox/internal/vercheck"
2524
"go.jetpack.io/devbox/plugins"
2625
)
2726

@@ -156,7 +155,7 @@ func (p *Package) Installable() (string, error) {
156155
}
157156

158157
if inCache {
159-
installable, err := p.ContentAddressedPath()
158+
installable, err := p.InputAddressedPath()
160159
if err != nil {
161160
return "", err
162161
}
@@ -466,36 +465,22 @@ func (p *Package) IsInBinaryCache() (bool, error) {
466465

467466
// Check if the user's system's info is present in the lockfile
468467
_, ok := entry.Systems[userSystem]
469-
return ok, nil
470-
}
471-
472-
// InputAddressedPath is the input-addressed path in /nix/store
473-
// It is also the key in the BinaryCache for this package
474-
func (p *Package) InputAddressedPath() (string, error) {
475-
if inCache, err := p.IsInBinaryCache(); err != nil {
476-
return "", err
477-
} else if !inCache {
478-
return "",
479-
errors.Errorf("Package %q cannot be fetched from binary cache store", p.Raw)
468+
if !ok {
469+
return false, nil
480470
}
481471

482-
entry, err := p.lockfile.Resolve(p.Raw)
472+
version, err := nix.Version()
483473
if err != nil {
484-
return "", err
485-
}
486-
487-
userSystem, err := nix.System()
488-
if err != nil {
489-
return "", err
474+
return false, err
490475
}
491476

492-
sysInfo := entry.Systems[userSystem]
493-
return sysInfo.StorePath, nil
477+
// enable for nix >= 2.17
478+
return vercheck.SemverCompare(version, "2.17.0") >= 0, nil
494479
}
495480

496-
// ContentAddressedPath is the content-addressed form of Package.InputAddressedPath
497-
func (p *Package) ContentAddressedPath() (string, error) {
498-
481+
// InputAddressedPath is the input-addressed path in /nix/store
482+
// It is also the key in the BinaryCache for this package
483+
func (p *Package) InputAddressedPath() (string, error) {
499484
if inCache, err := p.IsInBinaryCache(); err != nil {
500485
return "", err
501486
} else if !inCache {
@@ -514,20 +499,7 @@ func (p *Package) ContentAddressedPath() (string, error) {
514499
}
515500

516501
sysInfo := entry.Systems[userSystem]
517-
if sysInfo.CAStorePath != "" {
518-
return sysInfo.CAStorePath, nil
519-
}
520-
521-
ux.Fwarning(
522-
os.Stderr,
523-
fmt.Sprintf("calculating ca_store_path for %s. This may be slow. "+
524-
"Run `devbox update` to speed this up for next time.\n", sysInfo.StorePath),
525-
)
526-
localPath, err := nix.ContentAddressedStorePath(sysInfo.StorePath)
527-
if err != nil {
528-
return "", err
529-
}
530-
return localPath, err
502+
return sysInfo.StorePath, nil
531503
}
532504

533505
func (p *Package) AllowInsecure() bool {

internal/impl/update_test.go

Lines changed: 6 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ func TestUpdateNewCurrentSysInfoIsAdded(t *testing.T) {
4444
Resolved: "resolved-flake-reference",
4545
Systems: map[string]*lock.SystemInfo{
4646
sys: {
47-
StorePath: "store_path1",
48-
CAStorePath: "ca_path1",
47+
StorePath: "store_path1",
4948
},
5049
},
5150
}
@@ -64,7 +63,6 @@ func TestUpdateNewCurrentSysInfoIsAdded(t *testing.T) {
6463
require.Contains(t, lockfile.Packages, raw)
6564
require.Contains(t, lockfile.Packages[raw].Systems, sys)
6665
require.Equal(t, "store_path1", lockfile.Packages[raw].Systems[sys].StorePath)
67-
require.Equal(t, "ca_path1", lockfile.Packages[raw].Systems[sys].CAStorePath)
6866
}
6967

7068
func TestUpdateNewSysInfoIsAdded(t *testing.T) {
@@ -81,8 +79,7 @@ func TestUpdateNewSysInfoIsAdded(t *testing.T) {
8179
Resolved: "resolved-flake-reference",
8280
Systems: map[string]*lock.SystemInfo{
8381
sys1: {
84-
StorePath: "store_path1",
85-
CAStorePath: "ca_path1",
82+
StorePath: "store_path1",
8683
},
8784
sys2: {
8885
StorePath: "store_path2",
@@ -95,8 +92,7 @@ func TestUpdateNewSysInfoIsAdded(t *testing.T) {
9592
Resolved: "resolved-flake-reference",
9693
Systems: map[string]*lock.SystemInfo{
9794
sys1: {
98-
StorePath: "store_path1",
99-
CAStorePath: "ca_path1",
95+
StorePath: "store_path1",
10096
},
10197
// Missing sys2
10298
},
@@ -127,8 +123,7 @@ func TestUpdateOtherSysInfoIsReplaced(t *testing.T) {
127123
Resolved: "resolved-flake-reference",
128124
Systems: map[string]*lock.SystemInfo{
129125
sys1: {
130-
StorePath: "store_path1",
131-
CAStorePath: "ca_path1",
126+
StorePath: "store_path1",
132127
},
133128
sys2: {
134129
StorePath: "store_path2",
@@ -141,12 +136,10 @@ func TestUpdateOtherSysInfoIsReplaced(t *testing.T) {
141136
Resolved: "resolved-flake-reference",
142137
Systems: map[string]*lock.SystemInfo{
143138
sys1: {
144-
StorePath: "store_path1",
145-
CAStorePath: "ca_path1",
139+
StorePath: "store_path1",
146140
},
147141
sys2: {
148-
StorePath: "mismatching_store_path",
149-
CAStorePath: "ca_path2",
142+
StorePath: "mismatching_store_path",
150143
},
151144
},
152145
},
@@ -161,58 +154,6 @@ func TestUpdateOtherSysInfoIsReplaced(t *testing.T) {
161154
require.Contains(t, lockfile.Packages[raw].Systems, sys2)
162155
require.Equal(t, "store_path1", lockfile.Packages[raw].Systems[sys1].StorePath)
163156
require.Equal(t, "store_path2", lockfile.Packages[raw].Systems[sys2].StorePath)
164-
require.Empty(t, lockfile.Packages[raw].Systems[sys2].CAStorePath)
165-
}
166-
167-
func TestUpdateCAPathIsNotReplaced(t *testing.T) {
168-
featureflag.RemoveNixpkgs.EnableForTest(t)
169-
devbox := devboxForTesting(t)
170-
171-
172-
sys1 := currentSystem(t)
173-
sys2 := "system2"
174-
devPkg := &devpkg.Package{
175-
Raw: raw,
176-
}
177-
resolved := &lock.Package{
178-
Resolved: "resolved-flake-reference",
179-
Systems: map[string]*lock.SystemInfo{
180-
sys1: {
181-
StorePath: "store_path1",
182-
CAStorePath: "ca_path1",
183-
},
184-
sys2: {
185-
StorePath: "store_path2",
186-
// No CAPath here because this is not the current system.
187-
},
188-
},
189-
}
190-
lockfile := &lock.File{
191-
Packages: map[string]*lock.Package{
192-
raw: {
193-
Resolved: "resolved-flake-reference",
194-
Systems: map[string]*lock.SystemInfo{
195-
sys1: {
196-
StorePath: "store_path1",
197-
CAStorePath: "ca_path1",
198-
},
199-
sys2: {
200-
StorePath: "store_path2",
201-
CAStorePath: "ca_path2", // we already have CAPath for this system; it should not be replaced
202-
},
203-
},
204-
},
205-
},
206-
}
207-
208-
err := devbox.mergeResolvedPackageToLockfile(context.Background(), devPkg, resolved, lockfile)
209-
require.NoError(t, err, "update failed")
210-
211-
require.Contains(t, lockfile.Packages, raw)
212-
require.Contains(t, lockfile.Packages[raw].Systems, sys1)
213-
require.Contains(t, lockfile.Packages[raw].Systems, sys2)
214-
require.Equal(t, "store_path2", lockfile.Packages[raw].Systems[sys2].StorePath)
215-
require.Equal(t, "ca_path2", lockfile.Packages[raw].Systems[sys2].CAStorePath)
216157
}
217158

218159
func currentSystem(t *testing.T) string {

internal/lock/package.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ type SystemInfo struct {
2525
// It is of the form /nix/store/<hash>-<name>-<version>
2626
// <name> may be different from the canonicalName so we store the full store path.
2727
StorePath string `json:"store_path,omitempty"`
28-
// CAStorePath is the content-addressed path for the nix package in /nix/store
29-
// It is of the form /nix/store/<hash>-<name>-<version>
30-
CAStorePath string `json:"ca_store_path,omitempty"`
3128
}
3229

3330
func (p *Package) GetSource() string {

internal/lock/resolve.go

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,7 @@ func (f *File) FetchResolvedPackage(pkg string) (*Package, error) {
3535

3636
sysInfos := map[string]*SystemInfo{}
3737
if featureflag.RemoveNixpkgs.Enabled() {
38-
sysInfos, err = buildLockSystemInfos(packageVersion)
39-
if err != nil {
40-
return nil, err
41-
}
38+
sysInfos = buildLockSystemInfos(packageVersion)
4239
}
4340
packageInfo, err := selectForSystem(packageVersion)
4441
if err != nil {
@@ -81,12 +78,7 @@ func selectForSystem(pkg *searcher.PackageVersion) (searcher.PackageInfo, error)
8178
return maps.Values(pkg.Systems)[0], nil
8279
}
8380

84-
func buildLockSystemInfos(pkg *searcher.PackageVersion) (map[string]*SystemInfo, error) {
85-
userSystem, err := nix.System()
86-
if err != nil {
87-
return nil, err
88-
}
89-
81+
func buildLockSystemInfos(pkg *searcher.PackageVersion) map[string]*SystemInfo {
9082
sysInfos := map[string]*SystemInfo{}
9183
for sysName, sysInfo := range pkg.Systems {
9284

@@ -97,17 +89,9 @@ func buildLockSystemInfos(pkg *searcher.PackageVersion) (map[string]*SystemInfo,
9789
}
9890

9991
storePath := nix.StorePath(sysInfo.StoreHash, sysInfo.StoreName, sysInfo.StoreVersion)
100-
caStorePath := ""
101-
if sysName == userSystem {
102-
caStorePath, err = nix.ContentAddressedStorePath(storePath)
103-
if err != nil {
104-
return nil, errors.WithMessagef(err, "failed to make content addressed path for %s", storePath)
105-
}
106-
}
10792
sysInfos[sysName] = &SystemInfo{
108-
StorePath: storePath,
109-
CAStorePath: caStorePath,
93+
StorePath: storePath,
11094
}
11195
}
112-
return sysInfos, nil
96+
return sysInfos
11397
}

internal/nix/nix.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,31 @@ func System() (string, error) {
144144
return cachedSystem, nil
145145
}
146146

147+
// version is the cached output of `nix --version`.
148+
var version = ""
149+
150+
// Version returns the version of nix from `nix --version`. Usually in a semver
151+
// like format, but not strictly.
152+
func Version() (string, error) {
153+
154+
if version != "" {
155+
return version, nil
156+
}
157+
158+
cmd := command("--version")
159+
outBytes, err := cmd.Output()
160+
if err != nil {
161+
return "", errors.WithStack(err)
162+
}
163+
out := string(outBytes)
164+
const prefix = "nix (Nix) "
165+
if !strings.HasPrefix(out, prefix) {
166+
return "", errors.Errorf(`Expected "%s" prefix, but output from nix --version was: %s`, prefix, out)
167+
}
168+
version = strings.TrimSpace(strings.TrimPrefix(out, prefix))
169+
return version, nil
170+
}
171+
147172
// Warning: be careful using the bins in default/bin, they won't always match bins
148173
// produced by the flakes.nix. Use devbox.NixBins() instead.
149174
func ProfileBinPath(projectDir string) string {

internal/nix/store.go

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package nix
22

33
import (
4-
"encoding/json"
54
"path/filepath"
65
"strings"
7-
8-
"github.com/pkg/errors"
96
)
107

118
func StorePath(hash, name, version string) string {
@@ -16,29 +13,3 @@ func StorePath(hash, name, version string) string {
1613
storeDir := strings.Join(storeDirParts, "-")
1714
return filepath.Join("/nix/store", storeDir)
1815
}
19-
20-
// ContentAddressedStorePath takes a store path and returns the content-addressed store path.
21-
func ContentAddressedStorePath(storePath string) (string, error) {
22-
cmd := command("store", "make-content-addressed", storePath, "--json")
23-
out, err := cmd.Output()
24-
if err != nil {
25-
return "", errors.WithStack(err)
26-
}
27-
// Example Output:
28-
// > nix store make-content-addressed /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1 --json
29-
// {"rewrites":{"/nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1":"/nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1"}}
30-
31-
type ContentAddressed struct {
32-
Rewrites map[string]string `json:"rewrites"`
33-
}
34-
caOutput := ContentAddressed{}
35-
if err := json.Unmarshal(out, &caOutput); err != nil {
36-
return "", errors.WithStack(err)
37-
}
38-
39-
caStorePath, ok := caOutput.Rewrites[storePath]
40-
if !ok {
41-
return "", errors.Errorf("could not find content-addressed store path for %s", storePath)
42-
}
43-
return caStorePath, nil
44-
}

internal/nix/store_test.go

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1 @@
11
package nix
2-
3-
import (
4-
"fmt"
5-
"strings"
6-
"testing"
7-
8-
"golang.org/x/exp/slices"
9-
)
10-
11-
func TestContentAddressedPath(t *testing.T) {
12-
testCases := []struct {
13-
storePath string
14-
expected []string
15-
}{
16-
{
17-
"/nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1",
18-
[]string{
19-
// Hash from before Nix 2.17.0.
20-
"/nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1",
21-
22-
// Hash after Nix 2.17.0.
23-
"/nix/store/d49wyvsz5nkqa23qp4p0ikr04mw9n4h9-git-2.33.1",
24-
},
25-
},
26-
}
27-
28-
for index, testCase := range testCases {
29-
t.Run(fmt.Sprintf("%d", index), func(t *testing.T) {
30-
out, err := ContentAddressedStorePath(testCase.storePath)
31-
if err != nil {
32-
t.Errorf("got error: %v", err)
33-
}
34-
if !slices.Contains(testCase.expected, out) {
35-
t.Errorf("got %q, want any of:\n%s", out, strings.Join(testCase.expected, "\n"))
36-
}
37-
})
38-
}
39-
}

internal/shellgen/tmpl/flake_remove_nixpkgs.nix.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
(builtins.fetchClosure{
4242
fromStore = "{{ $.BinaryCache }}";
4343
fromPath = "{{ .InputAddressedPath }}";
44-
toPath = "{{ .ContentAddressedPath }}";
44+
inputAddressed = true;
4545
})
4646
{{- end }}
4747
{{- end }}

0 commit comments

Comments
 (0)