Skip to content

Commit 44e7ba2

Browse files
committed
[remove nixpkgs] part 2: generate flake.nix using builtins.fetchClosure
1 parent b90a2aa commit 44e7ba2

File tree

11 files changed

+234
-25
lines changed

11 files changed

+234
-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: 28 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

@@ -129,6 +129,33 @@ func (l *File) Resolve(pkg string) (*Package, error) {
129129
return l.Packages[pkg], nil
130130
}
131131

132+
func (l *File) SystemInfo(pkgVersionedName string) (*SystemInfo, error) {
133+
if !featureflag.RemoveNixpkgs.Enabled() {
134+
return nil, nil
135+
}
136+
137+
pkg, ok := l.Packages[pkgVersionedName]
138+
if !ok {
139+
return nil, errors.Errorf("package %s not found in lockfile", pkgVersionedName)
140+
}
141+
142+
if sysInfo, ok := pkg.Systems[l.system]; ok {
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, ok := pkg.Systems[l.system]; ok {
154+
return sysInfo, nil
155+
}
156+
return nil, errors.Errorf("Unable to get system %s info for package %s", l.system, pkgVersionedName)
157+
}
158+
132159
func (l *File) ForceResolve(pkg string) (*Package, error) {
133160
delete(l.Packages, pkg)
134161
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: 18 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,27 @@ 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+
// For Savil to debug "remove nixpkgs" feature. The search api lacks x86-darwin info.
124+
// So, I need to fake that I am x86-linux and inspect the output in generated devbox.lock
125+
// and flake.nix files.
126+
override := os.Getenv("__DEVBOX_NIX_SYSTEM")
127+
if override != "" {
128+
return override, nil
129+
}
130+
114131
if cachedSystem == "" {
115132
cmd := exec.Command(
116133
"nix", "eval", "--impure", "--raw", "--expr", "builtins.currentSystem",

internal/shellgen/flake_input.go

Lines changed: 10 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,18 @@ 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+
// This is temporary, so that we can support the existing flake.nix.tmpl
87+
// as well as the new flake_remove_nixpkgs.nix.tmpl.
88+
return !featureflag.RemoveNixpkgs.Enabled() || item.SystemInfo() == nil
89+
})
90+
91+
order := []string{}
92+
for _, input := range allInputs {
8593
AttributePath, err := input.FullPackageAttributePath()
8694
if err != nil {
8795
return nil, err

internal/shellgen/flake_package.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package shellgen
2+
3+
import (
4+
"path/filepath"
5+
"strings"
6+
7+
"go.jetpack.io/devbox/internal/boxcli/featureflag"
8+
"go.jetpack.io/devbox/internal/nix"
9+
)
10+
11+
type flakePackage struct {
12+
Name string
13+
FromStore string
14+
FromPath string
15+
}
16+
17+
func newFlakePackage(pkg *nix.Package) (*flakePackage, error) {
18+
// nixosCacheURL is where we fetch package binaries from
19+
const nixosCacheURL = "https://cache.nixos.org"
20+
21+
// flakePackages only support versioned packages that have a system info
22+
sysInfo := pkg.SystemInfo()
23+
if sysInfo == nil {
24+
return nil, nil
25+
}
26+
27+
attributePath, err := pkg.PackageAttributePath()
28+
if err != nil {
29+
return nil, err
30+
}
31+
32+
storeDir := strings.Join([]string{sysInfo.FromHash, sysInfo.StoreName, sysInfo.StoreVersion}, "-")
33+
return &flakePackage{
34+
Name: attributePath,
35+
FromStore: nixosCacheURL,
36+
FromPath: filepath.Join("/nix/store", storeDir),
37+
// TODO add ToPath:
38+
}, nil
39+
}
40+
41+
func flakePackages(devbox devboxer) ([]*flakePackage, error) {
42+
if !featureflag.RemoveNixpkgs.Enabled() {
43+
return nil, nil
44+
}
45+
46+
userInputs := devbox.PackagesAsInputs()
47+
pluginInputs, err := devbox.PluginManager().PluginInputs(userInputs)
48+
if err != nil {
49+
return nil, err
50+
}
51+
// As per flakeInputs function comments, we prioritize plugin packages
52+
// so the php plugin works.
53+
pkgs := append(pluginInputs, userInputs...)
54+
55+
result := []*flakePackage{}
56+
for _, pkg := range pkgs {
57+
flakePkg, err := newFlakePackage(pkg)
58+
if err != nil {
59+
return nil, err
60+
}
61+
if flakePkg != nil {
62+
result = append(result, flakePkg)
63+
}
64+
}
65+
return result, nil
66+
}

internal/shellgen/flake_plan.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ package shellgen
33
import (
44
"context"
55
"runtime/trace"
6+
7+
"go.jetpack.io/devbox/internal/nix"
68
)
79

810
// flakePlan contains the data to populate the top level flake.nix file
911
// that builds the devbox environment
1012
type flakePlan struct {
1113
NixpkgsInfo *NixpkgsInfo
1214
FlakeInputs []*flakeInput
15+
Packages []*flakePackage
16+
System string
1317
}
1418

1519
func newFlakePlan(ctx context.Context, devbox devboxer) (*flakePlan, error) {
@@ -35,9 +39,13 @@ func newFlakePlan(ctx context.Context, devbox devboxer) (*flakePlan, error) {
3539
}
3640
}
3741

38-
shellPlan := &flakePlan{}
3942
var err error
40-
shellPlan.FlakeInputs, err = flakeInputs(ctx, devbox)
43+
flakeInputs, err := flakeInputs(ctx, devbox)
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
flakePackages, err := flakePackages(devbox)
4149
if err != nil {
4250
return nil, err
4351
}
@@ -46,14 +54,22 @@ func newFlakePlan(ctx context.Context, devbox devboxer) (*flakePlan, error) {
4654

4755
// This is an optimization. Try to reuse the nixpkgs info from the flake
4856
// inputs to avoid introducing a new one.
49-
for _, input := range shellPlan.FlakeInputs {
57+
for _, input := range flakeInputs {
5058
if input.IsNixpkgs() {
5159
nixpkgsInfo = getNixpkgsInfo(input.HashFromNixPkgsURL())
5260
break
5361
}
5462
}
5563

56-
shellPlan.NixpkgsInfo = nixpkgsInfo
64+
system, err := nix.System()
65+
if err != nil {
66+
return nil, err
67+
}
5768

58-
return shellPlan, nil
69+
return &flakePlan{
70+
FlakeInputs: flakeInputs,
71+
NixpkgsInfo: nixpkgsInfo,
72+
Packages: flakePackages,
73+
System: system,
74+
}, nil
5975
}

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)