Skip to content

Commit 6f26857

Browse files
committed
[remove nixpkgs] part 2: generate flake.nix using builtins.fetchClosure
1 parent ff16efb commit 6f26857

File tree

11 files changed

+225
-25
lines changed

11 files changed

+225
-25
lines changed

internal/lock/interfaces.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ type Locker interface {
1818
resolver
1919
LegacyNixpkgsPath(string) string
2020
ProjectDir() string
21+
SystemInfo(pkg string) (*SystemInfo, error)
2122
}

internal/lock/lockfile.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
package lock
55

66
import (
7-
"errors"
87
"fmt"
98
"io/fs"
109
"path/filepath"
1110
"strings"
1211

12+
"github.com/pkg/errors"
1313
"github.com/samber/lo"
1414
"go.jetpack.io/devbox/internal/boxcli/featureflag"
1515
"go.jetpack.io/devbox/internal/cuecfg"
@@ -128,6 +128,35 @@ func (l *File) Resolve(pkg string) (*Package, error) {
128128
return l.Packages[pkg], nil
129129
}
130130

131+
func (l *File) SystemInfo(pkgVersionedName string) (*SystemInfo, error) {
132+
if !featureflag.RemoveNixpkgs.Enabled() {
133+
return nil, nil
134+
}
135+
136+
pkg, okidoki := l.Packages[pkgVersionedName]
137+
if !okidoki {
138+
return nil, errors.Errorf("package %s not found in lockfile", pkgVersionedName)
139+
}
140+
141+
sysInfo, okidoki := pkg.Systems[l.system]
142+
if okidoki {
143+
// Found it! We are done.
144+
return sysInfo, nil
145+
}
146+
147+
// If the systemInfo is missing, then resolve the package to get it from the search api.
148+
var err error
149+
pkg, err = l.Resolve(pkgVersionedName)
150+
if err != nil {
151+
return nil, err
152+
}
153+
if sysInfo, okidoki = pkg.Systems[l.system]; !okidoki {
154+
return nil, errors.Errorf("Unable to get system %s info for package %s", l.system, pkgVersionedName)
155+
}
156+
157+
return sysInfo, nil
158+
}
159+
131160
func (l *File) ForceResolve(pkg string) (*Package, error) {
132161
delete(l.Packages, pkg)
133162
return l.Resolve(pkg)

internal/nix/input.go

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import (
1414
"strings"
1515

1616
"github.com/samber/lo"
17-
1817
"go.jetpack.io/devbox/internal/boxcli/usererr"
1918
"go.jetpack.io/devbox/internal/cuecfg"
19+
"go.jetpack.io/devbox/internal/debug"
2020
"go.jetpack.io/devbox/internal/lock"
2121
)
2222

@@ -42,6 +42,9 @@ type Package struct {
4242
Raw string
4343

4444
normalizedPackageAttributePathCache string // memoized value from normalizedPackageAttributePath()
45+
46+
// sysInfo contains system-specific information about this package in the nix store
47+
sysInfo *lock.SystemInfo
4548
}
4649

4750
// PackageFromStrings constructs Package from the list of package names provided.
@@ -73,7 +76,26 @@ func PackageFromString(raw string, locker lock.Locker) *Package {
7376
}
7477
pkgURL, _ = url.Parse(normalizedURL)
7578
}
76-
return &Package{*pkgURL, locker, raw, ""}
79+
80+
pkg := &Package{
81+
URL: *pkgURL,
82+
lockfile: locker,
83+
Raw: raw,
84+
normalizedPackageAttributePathCache: "",
85+
sysInfo: nil,
86+
}
87+
88+
if pkg.isVersioned() {
89+
sysInfo, err := locker.SystemInfo(raw)
90+
if err != nil {
91+
// ignoring for coding convenience. TODO savil. handle error
92+
debug.Log("ERROR: failed to get locker.SystemInfo for pkg %s. Error is %s", raw, err)
93+
} else if sysInfo != nil {
94+
pkg.sysInfo = sysInfo
95+
}
96+
}
97+
98+
return pkg
7799
}
78100

79101
// PackageFromProfileItem constructs a package using the the unlocked reference
@@ -407,6 +429,10 @@ func (p *Package) hashFromNixPkgsURL() string {
407429
return HashFromNixPkgsURL(p.URLForFlakeInput())
408430
}
409431

432+
func (p *Package) SystemInfo() *lock.SystemInfo {
433+
return p.sysInfo
434+
}
435+
410436
// IsGithubNixpkgsURL returns true if the package is a flake of the form:
411437
// github:NixOS/nixpkgs/...
412438
//

internal/nix/input_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ func (l *lockfile) Resolve(pkg string) (*lock.Package, error) {
131131
}
132132
}
133133

134+
func (l *lockfile) SystemInfo(pkg string) (*lock.SystemInfo, error) {
135+
return nil, nil
136+
}
137+
134138
func testInputFromString(s, projectDir string) *testInput {
135139
return lo.ToPtr(testInput{Package: *PackageFromString(s, &lockfile{projectDir})})
136140
}

internal/nix/nix.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import (
1111
"os/exec"
1212
"path/filepath"
1313
"runtime/trace"
14+
"strings"
1415

1516
"github.com/pkg/errors"
17+
"go.jetpack.io/devbox/internal/boxcli/featureflag"
1618

1719
"go.jetpack.io/devbox/internal/debug"
1820
)
@@ -64,6 +66,9 @@ func (*Nix) PrintDevEnv(ctx context.Context, args *PrintDevEnvArgs) (*PrintDevEn
6466
args.FlakesFilePath,
6567
)
6668
cmd.Args = append(cmd.Args, ExperimentalFlags()...)
69+
if featureflag.RemoveNixpkgs.Enabled() {
70+
cmd.Args = append(cmd.Args, "--impure")
71+
}
6772
cmd.Args = append(cmd.Args, "--json")
6873
debug.Log("Running print-dev-env cmd: %s\n", cmd)
6974
data, err = cmd.Output()
@@ -102,15 +107,24 @@ func FlakeNixpkgs(commit string) string {
102107
}
103108

104109
func ExperimentalFlags() []string {
110+
options := []string{"nix-command", "flakes"}
111+
if featureflag.RemoveNixpkgs.Enabled() {
112+
options = append(options, "fetch-closure")
113+
}
105114
return []string{
106115
"--extra-experimental-features", "ca-derivations",
107-
"--option", "experimental-features", "nix-command flakes",
116+
"--option", "experimental-features", strings.Join(options, " "),
108117
}
109118
}
110119

111120
var cachedSystem string
112121

113122
func System() (string, error) {
123+
override := os.Getenv("__DEVBOX_NIX_SYSTEM")
124+
if override != "" {
125+
return override, nil
126+
}
127+
114128
if cachedSystem == "" {
115129
cmd := exec.Command(
116130
"nix", "eval", "--impure", "--raw", "--expr", "builtins.currentSystem",

internal/shellgen/flake_input.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"strings"
77

88
"github.com/samber/lo"
9+
"go.jetpack.io/devbox/internal/boxcli/featureflag"
910
"go.jetpack.io/devbox/internal/goutil"
1011
"go.jetpack.io/devbox/internal/nix"
1112
)
@@ -77,11 +78,16 @@ func flakeInputs(ctx context.Context, devbox devboxer) ([]*flakeInput, error) {
7778
return nil, err
7879
}
7980

80-
order := []string{}
8181
// We prioritize plugin packages so that the php plugin works. Not sure
8282
// if this is behavior we want for user plugins. We may need to add an optional
8383
// priority field to the config.
84-
for _, input := range append(pluginInputs, userInputs...) {
84+
allInputs := append(pluginInputs, userInputs...)
85+
allInputs = lo.Filter(allInputs, func(item *nix.Package, _ int) bool {
86+
return !featureflag.RemoveNixpkgs.Enabled() || item.SystemInfo() == nil
87+
})
88+
89+
order := []string{}
90+
for _, input := range allInputs {
8591
AttributePath, err := input.FullPackageAttributePath()
8692
if err != nil {
8793
return nil, err

internal/shellgen/flake_package.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package shellgen
2+
3+
import (
4+
"path/filepath"
5+
"strings"
6+
7+
"go.jetpack.io/devbox/internal/nix"
8+
)
9+
10+
type flakePackage struct {
11+
Name string
12+
FromStore string
13+
FromPath string
14+
}
15+
16+
func newFlakePackage(pkg *nix.Package) (*flakePackage, error) {
17+
// nixosCacheURL is where we fetch package binaries from
18+
const nixosCacheURL = "https://cache.nixos.org"
19+
20+
// flakePackages only support versioned packages that have a system info
21+
sysInfo := pkg.SystemInfo()
22+
if sysInfo == nil {
23+
return nil, nil
24+
}
25+
26+
attributePath, err := pkg.PackageAttributePath()
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
storeDir := strings.Join([]string{sysInfo.FromHash, sysInfo.StoreName, sysInfo.StoreVersion}, "-")
32+
return &flakePackage{
33+
Name: attributePath,
34+
FromStore: nixosCacheURL,
35+
FromPath: filepath.Join("/nix/store", storeDir),
36+
// TODO add ToPath:
37+
}, nil
38+
}

internal/shellgen/flake_plan.go

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@ package shellgen
33
import (
44
"context"
55
"runtime/trace"
6+
7+
"go.jetpack.io/devbox/internal/boxcli/featureflag"
8+
"go.jetpack.io/devbox/internal/nix"
69
)
710

811
// flakePlan contains the data to populate the top level flake.nix file
912
// that builds the devbox environment
1013
type flakePlan struct {
1114
NixpkgsInfo *NixpkgsInfo
1215
FlakeInputs []*flakeInput
16+
Packages []*flakePackage
17+
System string
1318
}
1419

1520
func newFlakePlan(ctx context.Context, devbox devboxer) (*flakePlan, error) {
@@ -35,9 +40,13 @@ func newFlakePlan(ctx context.Context, devbox devboxer) (*flakePlan, error) {
3540
}
3641
}
3742

38-
shellPlan := &flakePlan{}
3943
var err error
40-
shellPlan.FlakeInputs, err = flakeInputs(ctx, devbox)
44+
flakeInputs, err := flakeInputs(ctx, devbox)
45+
if err != nil {
46+
return nil, err
47+
}
48+
49+
flakePackages, err := flakePackages(devbox)
4150
if err != nil {
4251
return nil, err
4352
}
@@ -46,14 +55,47 @@ func newFlakePlan(ctx context.Context, devbox devboxer) (*flakePlan, error) {
4655

4756
// This is an optimization. Try to reuse the nixpkgs info from the flake
4857
// inputs to avoid introducing a new one.
49-
for _, input := range shellPlan.FlakeInputs {
58+
for _, input := range flakeInputs {
5059
if input.IsNixpkgs() {
5160
nixpkgsInfo = getNixpkgsInfo(input.HashFromNixPkgsURL())
5261
break
5362
}
5463
}
5564

56-
shellPlan.NixpkgsInfo = nixpkgsInfo
65+
system, err := nix.System()
66+
if err != nil {
67+
return nil, err
68+
}
69+
70+
return &flakePlan{
71+
FlakeInputs: flakeInputs,
72+
NixpkgsInfo: nixpkgsInfo,
73+
Packages: flakePackages,
74+
System: system,
75+
}, nil
76+
}
5777

58-
return shellPlan, nil
78+
func flakePackages(devbox devboxer) ([]*flakePackage, error) {
79+
if !featureflag.RemoveNixpkgs.Enabled() {
80+
return nil, nil
81+
}
82+
83+
userInputs := devbox.PackagesAsInputs()
84+
pluginInputs, err := devbox.PluginManager().PluginInputs(userInputs)
85+
if err != nil {
86+
return nil, err
87+
}
88+
// As per flakeInputs function comments, we prioritize plugin packages
89+
// so the php plugin works.
90+
pkgs := append(pluginInputs, userInputs...)
91+
92+
result := []*flakePackage{}
93+
for _, pkg := range pkgs {
94+
flakePkg, err := newFlakePackage(pkg)
95+
if err != nil {
96+
return nil, err
97+
}
98+
result = append(result, flakePkg)
99+
}
100+
return result, nil
59101
}

internal/shellgen/generate.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,14 @@ import (
1717
"text/template"
1818

1919
"github.com/pkg/errors"
20+
"go.jetpack.io/devbox/internal/boxcli/featureflag"
2021
"go.jetpack.io/devbox/internal/cuecfg"
2122
"go.jetpack.io/devbox/internal/debug"
2223
)
2324

2425
//go:embed tmpl/*
2526
var tmplFS embed.FS
2627

27-
var shellFiles = []string{"shell.nix"}
28-
2928
// GenerateForPrintEnv will create all the files necessary for processing
3029
// devbox.PrintEnv, which is the core function from which devbox shell/run/direnv
3130
// functionality is derived.
@@ -39,15 +38,14 @@ func GenerateForPrintEnv(ctx context.Context, devbox devboxer) error {
3938

4039
outPath := genPath(devbox)
4140

42-
for _, file := range shellFiles {
43-
err := writeFromTemplate(outPath, plan, file)
44-
if err != nil {
45-
return errors.WithStack(err)
46-
}
41+
// Preserving shell.nix to avoid breaking old-style .envrc users
42+
err = writeFromTemplate(outPath, plan, "shell.nix", "shell.nix")
43+
if err != nil {
44+
return errors.WithStack(err)
4745
}
4846

4947
// Gitignore file is added to the .devbox directory
50-
err = writeFromTemplate(filepath.Join(devbox.ProjectDir(), ".devbox"), plan, ".gitignore")
48+
err = writeFromTemplate(filepath.Join(devbox.ProjectDir(), ".devbox"), plan, ".gitignore", ".gitignore")
5149
if err != nil {
5250
return errors.WithStack(err)
5351
}
@@ -69,7 +67,7 @@ var (
6967
tmplOldBuf = bytes.NewBuffer(make([]byte, 0, 4096))
7068
)
7169

72-
func writeFromTemplate(path string, plan any, tmplName string) error {
70+
func writeFromTemplate(path string, plan any, tmplName string, generatedName string) error {
7371
tmplKey := tmplName + ".tmpl"
7472
tmpl := tmplCache[tmplKey]
7573
if tmpl == nil {
@@ -93,7 +91,7 @@ func writeFromTemplate(path string, plan any, tmplName string) error {
9391
// changed. Blindly overwriting the file could invalidate Nix's cache
9492
// every time, slowing down evaluation considerably.
9593
var (
96-
outPath = filepath.Join(path, tmplName)
94+
outPath = filepath.Join(path, generatedName)
9795
flag = os.O_RDWR | os.O_CREATE
9896
perm = fs.FileMode(0644)
9997
)
@@ -150,7 +148,11 @@ var templateFuncs = template.FuncMap{
150148

151149
func makeFlakeFile(d devboxer, outPath string, plan *flakePlan) error {
152150
flakeDir := FlakePath(d)
153-
err := writeFromTemplate(flakeDir, plan, "flake.nix")
151+
templateName := "flake.nix"
152+
if featureflag.RemoveNixpkgs.Enabled() {
153+
templateName = "flake_remove_nixpkgs.nix"
154+
}
155+
err := writeFromTemplate(flakeDir, plan, templateName, "flake.nix")
154156
if err != nil {
155157
return errors.WithStack(err)
156158
}

0 commit comments

Comments
 (0)