Skip to content

Bring back: derive nix profile changes (#1742) #1747

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions .github/workflows/cli-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
- name: Build devbox
run: go build -o dist/devbox ./cmd/devbox
- name: Upload devbox artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v3
with:
name: devbox-${{ runner.os }}-${{ runner.arch }}
path: ./dist/devbox
Expand Down Expand Up @@ -113,7 +113,7 @@ jobs:
run: |
chmod +x ./devbox
sudo mv ./devbox /usr/local/bin/

- run: devbox run lint

test:
Expand All @@ -129,9 +129,8 @@ jobs:
run-project-tests: ["project-tests", "project-tests-off"]
# Run tests on:
# 1. the oldest supported nix version (which is 2.9.0? But determinate-systems installer has 2.12.0)
# 2. nix version 2.17.0 which introduces a new code path that minimizes nixpkgs downloads.
# 3. latest nix version
nix-version: ["2.12.0", "2.17.0", "2.19.2"]
# 2. latest nix version
nix-version: ["2.12.0", "2.19.2"]
exclude:
- is-main: "not-main"
os: "${{ inputs.run-mac-tests && 'dummy' || 'macos-latest' }}"
Expand Down Expand Up @@ -200,7 +199,7 @@ jobs:
go test -v -timeout $DEVBOX_GOLANG_TEST_TIMEOUT ./...

auto-nix-install: # ensure Devbox installs nix and works properly after installation.
needs: build-devbox
needs: build-devbox
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
Expand Down
86 changes: 86 additions & 0 deletions internal/devbox/nixprofile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package devbox

import (
"context"
"fmt"
"strings"

"github.com/samber/lo"
"go.jetpack.io/devbox/internal/nix"
"go.jetpack.io/devbox/internal/nix/nixprofile"
"go.jetpack.io/devbox/internal/ux"
)

// syncNixProfileFromFlake ensures the nix profile has the packages from the buildInputs
// from the devshell of the generated flake.
//
// It also removes any packages from the nix profile that are no longer in the buildInputs.
func (d *Devbox) syncNixProfileFromFlake(ctx context.Context) error {
// Get the computed Devbox environment from the generated flake
env, err := d.computeEnv(ctx, false /*usePrintDevEnvCache*/)
if err != nil {
return err
}

// Get the store-paths of the packages we want installed in the nix profile
wantStorePaths := []string{}
if env["buildInputs"] != "" {
// env["buildInputs"] can be empty string if there are no packages in the project
// if buildInputs is empty, then we don't want wantStorePaths to be an array with a single "" entry
wantStorePaths = strings.Split(env["buildInputs"], " ")
}

profilePath, err := d.profilePath()
if err != nil {
return err
}

// Get the store-paths of the packages currently installed in the nix profile
items, err := nixprofile.ProfileListItems(d.stderr, profilePath)
if err != nil {
return fmt.Errorf("nix profile list: %v", err)
}
gotStorePaths := make([]string, 0, len(items))
for _, item := range items {
gotStorePaths = append(gotStorePaths, item.StorePaths()...)
}

// Diff the store paths and install/remove packages as needed
remove, add := lo.Difference(gotStorePaths, wantStorePaths)
if len(remove) > 0 {
packagesToRemove := make([]string, 0, len(remove))
for _, p := range remove {
storePath := nix.NewStorePathParts(p)
packagesToRemove = append(packagesToRemove, fmt.Sprintf("%s@%s", storePath.Name, storePath.Version))
}
if len(packagesToRemove) == 1 {
ux.Finfo(d.stderr, "Removing %s\n", strings.Join(packagesToRemove, ", "))
} else {
ux.Finfo(d.stderr, "Removing packages: %s\n", strings.Join(packagesToRemove, ", "))
}

if err := nix.ProfileRemove(profilePath, remove...); err != nil {
return err
}
}
if len(add) > 0 {
// We need to install the packages in the nix profile one-by-one because
// we do checks for insecure packages.
// TODO: move the insecure package check here, and do `nix profile install installables...`
// in one command for speed.
for _, addPath := range add {
if err = nix.ProfileInstall(ctx, &nix.ProfileInstallArgs{
Installable: addPath,
// Install in offline mode for speed. We know we should have all the files
// locally in /nix/store since we have run `nix print-dev-env` prior to this.
// Also avoids some "substituter not found for store-path" errors.
Offline: true,
ProfilePath: profilePath,
Writer: d.stderr,
}); err != nil {
return fmt.Errorf("error installing package in nix profile %s: %w", addPath, err)
}
}
}
return nil
}
Loading