@@ -13,11 +13,11 @@ import (
1313 "runtime/trace"
1414 "strings"
1515
16- "github.com/fatih/color"
1716 "github.com/pkg/errors"
1817 "github.com/samber/lo"
1918 "golang.org/x/exp/slices"
2019
20+ "go.jetpack.io/devbox/internal/boxcli/usererr"
2121 "go.jetpack.io/devbox/internal/debug"
2222 "go.jetpack.io/devbox/internal/lock"
2323 "go.jetpack.io/devbox/internal/nix"
@@ -29,54 +29,53 @@ import (
2929// packages.go has functions for adding, removing and getting info about nix packages
3030
3131// Add adds the `pkgs` to the config (i.e. devbox.json) and nix profile for this devbox project
32- func (d * Devbox ) Add (ctx context.Context , pkgs ... string ) error {
32+ func (d * Devbox ) Add (ctx context.Context , pkgsNames ... string ) error {
3333 ctx , task := trace .NewTask (ctx , "devboxAdd" )
3434 defer task .End ()
3535
36- pkgs = lo .Uniq (pkgs )
36+ pkgs := nix . InputsFromStrings ( lo .Uniq (pkgsNames ), d . lockfile )
3737
38- original := d .cfg .Packages
38+ versionedPackages := []* nix.Input {}
39+ // Add to Packages of the config only if it's not already there. We do this
40+ // before addin @latest to ensure we don't accidentally add a package that
41+ // is already in the config.
42+ for _ , pkg := range pkgs {
43+ versioned := pkg .Versioned ()
44+ versionedPackages = append (
45+ versionedPackages ,
46+ nix .InputFromString (versioned , d .lockfile ),
47+ )
48+ // Only add if the package doesn't exist versioned or unversioned.
49+ if ! slices .Contains (d .cfg .Packages , pkg .Raw ) && ! slices .Contains (d .cfg .Packages , versioned ) {
50+ d .cfg .Packages = append (d .cfg .Packages , versioned )
51+ }
52+ }
53+ pkgs = versionedPackages
3954
4055 // Check packages are valid before adding.
4156 for _ , pkg := range pkgs {
42- ok , err := nix . PkgExists ( pkg , d . lockfile )
57+ ok , err := pkg . ValidateExists ( )
4358 if err != nil {
4459 return err
4560 }
4661 if ! ok {
47- return errors .WithMessage (nix .ErrPackageNotFound , pkg )
48- }
49- }
50-
51- // Add to Packages of the config only if it's not already there
52- for _ , pkg := range pkgs {
53- if slices .Contains (d .cfg .Packages , pkg ) {
54- continue
62+ return errors .WithMessage (nix .ErrPackageNotFound , pkg .Raw )
5563 }
56- d .cfg .Packages = append (d .cfg .Packages , pkg )
57- }
58- if err := d .saveCfg (); err != nil {
59- return err
6064 }
6165
6266 d .pluginManager .ApplyOptions (plugin .WithAddMode ())
6367 if err := d .ensurePackagesAreInstalled (ctx , install ); err != nil {
64- // if installation fails, revert devbox.json
65- // This is not perfect because there may be more than 1 package being
66- // installed and we don't know which one failed. But it's better than
67- // blindly add all packages.
68- color .New (color .FgRed ).Fprintf (
69- d .writer ,
70- "There was an error installing nix packages: %v. " +
71- "Packages were not added to devbox.json\n " ,
72- strings .Join (pkgs , ", " ),
68+ return usererr .WithUserMessage (
69+ err ,
70+ "There was an error installing nix packages" ,
7371 )
74- d .cfg .Packages = original
75- _ = d .saveCfg () // ignore error to ensure we return the original error
72+ }
73+
74+ if err := d .saveCfg (); err != nil {
7675 return err
7776 }
7877
79- for _ , input := range nix . InputsFromStrings ( pkgs , d . lockfile ) {
78+ for _ , input := range pkgs {
8079 if err := plugin .PrintReadme (
8180 input ,
8281 d .projectDir ,
@@ -87,48 +86,58 @@ func (d *Devbox) Add(ctx context.Context, pkgs ...string) error {
8786 }
8887 }
8988
90- if err := d .lockfile .Add (pkgs ... ); err != nil {
89+ if err := d .lockfile .Add (
90+ lo .Map (pkgs , func (pkg * nix.Input , _ int ) string { return pkg .Raw })... ,
91+ ); err != nil {
9192 return err
9293 }
9394
9495 return wrapnix .CreateWrappers (ctx , d )
9596}
9697
97- // Remove removes the `pkgs` from the config (i.e. devbox.json) and nix profile for this devbox project
98+ // Remove removes the `pkgs` from the config (i.e. devbox.json) and nix profile
99+ // for this devbox project
98100func (d * Devbox ) Remove (ctx context.Context , pkgs ... string ) error {
99101 ctx , task := trace .NewTask (ctx , "devboxRemove" )
100102 defer task .End ()
101103
102- pkgs = lo .Uniq (pkgs )
104+ packagesToUninstall := []string {}
105+ missingPkgs := []string {}
106+ for _ , pkg := range lo .Uniq (pkgs ) {
107+ found , _ := d .findPackageByName (pkg )
108+ if found != "" {
109+ packagesToUninstall = append (packagesToUninstall , found )
110+ d .cfg .Packages = lo .Without (d .cfg .Packages , found )
111+ } else {
112+ missingPkgs = append (missingPkgs , pkg )
113+ }
114+ }
103115
104- // First, save which packages are being uninstalled. Do this before we modify d.cfg.RawPackages below.
105- uninstalledPackages := lo .Intersect (d .cfg .Packages , pkgs )
106- remainingPkgs , missingPkgs := lo .Difference (d .cfg .Packages , pkgs )
107116 if len (missingPkgs ) > 0 {
108117 ux .Fwarning (
109118 d .writer ,
110119 "the following packages were not found in your devbox.json: %s\n " ,
111120 strings .Join (missingPkgs , ", " ),
112121 )
113122 }
114- d . cfg . Packages = remainingPkgs
115- if err := d . saveCfg ( ); err != nil {
123+
124+ if err := plugin . Remove ( d . projectDir , packagesToUninstall ); err != nil {
116125 return err
117126 }
118127
119- if err := plugin . Remove ( d . projectDir , uninstalledPackages ); err != nil {
128+ if err := d . removePackagesFromProfile ( ctx , packagesToUninstall ); err != nil {
120129 return err
121130 }
122131
123- if err := d .removePackagesFromProfile (ctx , uninstalledPackages ); err != nil {
132+ if err := d .ensurePackagesAreInstalled (ctx , uninstall ); err != nil {
124133 return err
125134 }
126135
127- if err := d .ensurePackagesAreInstalled ( ctx , uninstall ); err != nil {
136+ if err := d .lockfile . Remove ( packagesToUninstall ... ); err != nil {
128137 return err
129138 }
130139
131- if err := d .lockfile . Remove ( uninstalledPackages ... ); err != nil {
140+ if err := d .saveCfg ( ); err != nil {
132141 return err
133142 }
134143
0 commit comments