aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/manual/rl-next/nix-profile-names.md7
-rw-r--r--src/libcmd/cmd-profiles.cc26
-rw-r--r--src/libcmd/cmd-profiles.hh3
-rw-r--r--src/nix/profile-list.md9
-rw-r--r--src/nix/profile-remove.md11
-rw-r--r--src/nix/profile-upgrade.md11
-rw-r--r--src/nix/profile.cc156
-rw-r--r--tests/functional/nix-profile.sh20
8 files changed, 179 insertions, 64 deletions
diff --git a/doc/manual/rl-next/nix-profile-names.md b/doc/manual/rl-next/nix-profile-names.md
new file mode 100644
index 000000000..0db17b21e
--- /dev/null
+++ b/doc/manual/rl-next/nix-profile-names.md
@@ -0,0 +1,7 @@
+---
+synopsis: "`nix profile` now allows referring to elements by human-readable name"
+prs: 8678
+cls: 978
+---
+
+[`nix profile`](@docroot@/command-ref/new-cli/nix3-profile.md) now uses names to refer to installed packages when running [`list`](@docroot@/command-ref/new-cli/nix3-profile-list.md), [`remove`](@docroot@/command-ref/new-cli/nix3-profile-remove.md) or [`upgrade`](@docroot@/command-ref/new-cli/nix3-profile-upgrade.md) as opposed to indices. Indices are deprecated and will be removed in a future version.
diff --git a/src/libcmd/cmd-profiles.cc b/src/libcmd/cmd-profiles.cc
index 4d8ff7438..5fa7f902c 100644
--- a/src/libcmd/cmd-profiles.cc
+++ b/src/libcmd/cmd-profiles.cc
@@ -1,6 +1,9 @@
+#include <set>
+
#include "cmd-profiles.hh"
#include "built-path.hh"
#include "builtins/buildenv.hh"
+#include "logging.hh"
#include "names.hh"
#include "store-api.hh"
@@ -106,6 +109,8 @@ ProfileManifest::ProfileManifest(EvalState & state, const Path & profile)
if (pathExists(manifestPath)) {
auto json = nlohmann::json::parse(readFile(manifestPath));
+ // Keep track of already found names so we can prevent duplicates.
+ std::set<std::string> foundNames;
auto version = json.value("version", 0);
std::string sUrl;
@@ -139,6 +144,26 @@ ProfileManifest::ProfileManifest(EvalState & state, const Path & profile)
e["attrPath"],
e["outputs"].get<ExtendedOutputsSpec>()};
}
+
+ std::string nameCandidate(element.identifier());
+
+ if (e.contains("name")) {
+ nameCandidate = e["name"];
+ } else if (element.source) {
+ auto const url = parseURL(element.source->to_string());
+ auto const name = getNameFromURL(url);
+ if (name) {
+ nameCandidate = *name;
+ }
+ }
+
+ auto finalName = nameCandidate;
+ for (unsigned appendedIndex = 1; foundNames.contains(finalName); ++appendedIndex) {
+ finalName = nameCandidate + std::to_string(appendedIndex);
+ }
+ element.name = finalName;
+ foundNames.insert(element.name);
+
elements.emplace_back(std::move(element));
}
} else if (pathExists(profile + "/manifest.nix")) {
@@ -151,6 +176,7 @@ ProfileManifest::ProfileManifest(EvalState & state, const Path & profile)
for (auto & drvInfo : drvInfos) {
ProfileElement element;
element.storePaths = {drvInfo.queryOutPath()};
+ element.name = element.identifier();
elements.emplace_back(std::move(element));
}
}
diff --git a/src/libcmd/cmd-profiles.hh b/src/libcmd/cmd-profiles.hh
index d03f9b1c2..7f2c8a76f 100644
--- a/src/libcmd/cmd-profiles.hh
+++ b/src/libcmd/cmd-profiles.hh
@@ -6,6 +6,8 @@
#include "flake/flakeref.hh"
#include "get-drvs.hh"
#include "types.hh"
+#include "url.hh"
+#include "url-name.hh"
#include <string>
#include <set>
@@ -33,6 +35,7 @@ constexpr int DEFAULT_PRIORITY = 5;
struct ProfileElement
{
StorePathSet storePaths;
+ std::string name;
std::optional<ProfileElementSource> source;
bool active = true;
int priority = DEFAULT_PRIORITY;
diff --git a/src/nix/profile-list.md b/src/nix/profile-list.md
index 5d7fcc0ec..d807a69c6 100644
--- a/src/nix/profile-list.md
+++ b/src/nix/profile-list.md
@@ -6,6 +6,7 @@ R""(
```console
# nix profile list
+ Name: gdb
Index: 0
Flake attribute: legacyPackages.x86_64-linux.gdb
Original flake URL: flake:nixpkgs
@@ -13,6 +14,7 @@ R""(
Store paths: /nix/store/indzcw5wvlhx6vwk7k4iq29q15chvr3d-gdb-11.1
Index: 1
+ Name: blender-bin
Flake attribute: packages.x86_64-linux.default
Original flake URL: flake:blender-bin
Locked flake URL: github:edolstra/nix-warez/91f2ffee657bf834e4475865ae336e2379282d34?dir=blender
@@ -26,7 +28,7 @@ R""(
# nix build github:edolstra/nix-warez/91f2ffee657bf834e4475865ae336e2379282d34?dir=blender#packages.x86_64-linux.default
```
- will build the package with index 1 shown above.
+ will build the package with name blender-bin shown above.
# Description
@@ -34,10 +36,13 @@ This command shows what packages are currently installed in a
profile. For each installed package, it shows the following
information:
-* `Index`: An integer that can be used to unambiguously identify the
+* `Name`: A unique name used to unambiguously identify the
package in invocations of `nix profile remove` and `nix profile
upgrade`.
+* `Index`: An integer that can be used to unambiguously identify the package in invocations of `nix profile remove` and `nix profile upgrade`.
+ (*Deprecated, will be removed in a future version in favor of `Name`.*)
+
* `Flake attribute`: The flake output attribute path that provides the
package (e.g. `packages.x86_64-linux.hello`).
diff --git a/src/nix/profile-remove.md b/src/nix/profile-remove.md
index ba85441d8..c76f4b09c 100644
--- a/src/nix/profile-remove.md
+++ b/src/nix/profile-remove.md
@@ -2,10 +2,17 @@ R""(
# Examples
-* Remove a package by position:
+* Remove a package by name:
```console
- # nix profile remove 3
+ # nix profile remove hello
+ ```
+
+* Remove a package by index
+ *(deprecated, will be removed in a future version)*:
+
+ ```console
+ $ nix profile remove 3
```
* Remove a package by attribute path:
diff --git a/src/nix/profile-upgrade.md b/src/nix/profile-upgrade.md
index 39cca428b..b13cb66bb 100644
--- a/src/nix/profile-upgrade.md
+++ b/src/nix/profile-upgrade.md
@@ -9,18 +9,19 @@ R""(
# nix profile upgrade '.*'
```
-* Upgrade a specific package:
+* Upgrade a specific package by name:
+
+ ```console
+ # nix profile upgrade hello
+ ```
```console
# nix profile upgrade packages.x86_64-linux.hello
```
-* Upgrade a specific profile element by number:
+* Upgrade a specific package by index:
```console
- # nix profile list
- 0 flake:nixpkgs#legacyPackages.x86_64-linux.spotify …
-
# nix profile upgrade 0
```
diff --git a/src/nix/profile.cc b/src/nix/profile.cc
index 67f97ca9b..f702c7c06 100644
--- a/src/nix/profile.cc
+++ b/src/nix/profile.cc
@@ -189,13 +189,24 @@ public:
{
std::vector<Matcher> res;
+ auto anyIndexMatchers = false;
+
for (auto & s : _matchers) {
- if (auto n = string2Int<size_t>(s))
+ if (auto n = string2Int<size_t>(s)) {
res.push_back(*n);
- else if (store->isStorePath(s))
+ anyIndexMatchers = true;
+ } else if (store->isStorePath(s)) {
res.push_back(s);
- else
+ } else {
res.push_back(RegexPattern{s,std::regex(s, std::regex::extended | std::regex::icase)});
+ }
+ }
+
+ if (anyIndexMatchers) {
+ warn(
+ "Indices are deprecated and be removed in a future version!\n"
+ " Refer to packages by their `Name` printed by `nix profile list`.\n"
+ );
}
return res;
@@ -206,12 +217,13 @@ public:
for (auto & matcher : matchers) {
if (auto n = std::get_if<size_t>(&matcher)) {
if (*n == pos) return true;
+
} else if (auto path = std::get_if<Path>(&matcher)) {
if (element.storePaths.count(store.parseStorePath(*path))) return true;
} else if (auto regex = std::get_if<RegexPattern>(&matcher)) {
- if (element.source
- && std::regex_match(element.source->attrPath, regex->reg))
+ if (std::regex_match(element.name, regex->reg)) {
return true;
+ }
}
}
@@ -294,62 +306,100 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
Installables installables;
std::vector<size_t> indices;
+ auto matchedCount = 0;
auto upgradedCount = 0;
for (size_t i = 0; i < manifest.elements.size(); ++i) {
auto & element(manifest.elements[i]);
- if (element.source
- && !element.source->originalRef.input.isLocked()
- && matches(*store, element, i, matchers))
- {
- upgradedCount++;
+ if (!matches(*store, element, i, matchers)) {
+ continue;
+ }
- Activity act(*logger, lvlChatty, actUnknown,
- fmt("checking '%s' for updates", element.source->attrPath));
+ matchedCount += 1;
- auto installable = make_ref<InstallableFlake>(
- this,
- getEvalState(),
- FlakeRef(element.source->originalRef),
- "",
- element.source->outputs,
- Strings{element.source->attrPath},
- Strings{},
- lockFlags);
+ if (!element.source) {
+ warn(
+ "Found package '%s', but it was not installed from a flake, so it can't be checked for upgrades",
+ element.identifier()
+ );
+ continue;
+ }
- auto derivedPaths = installable->toDerivedPaths();
- if (derivedPaths.empty()) continue;
- auto * infop = dynamic_cast<ExtraPathInfoFlake *>(&*derivedPaths[0].info);
- // `InstallableFlake` should use `ExtraPathInfoFlake`.
- assert(infop);
- auto & info = *infop;
+ if (element.source->originalRef.input.isLocked()) {
+ warn(
+ "Found package '%s', but it was installed from a locked flake reference so it can't be upgraded",
+ element.identifier()
+ );
+ continue;
+ }
- if (element.source->lockedRef == info.flake.lockedRef) continue;
+ upgradedCount++;
- printInfo("upgrading '%s' from flake '%s' to '%s'",
- element.source->attrPath, element.source->lockedRef, info.flake.lockedRef);
+ Activity act(
+ *logger,
+ lvlChatty,
+ actUnknown,
+ fmt("checking '%s' for updates", element.source->attrPath),
+ Logger::Fields{element.source->attrPath}
+ );
- element.source = ProfileElementSource {
- .originalRef = installable->flakeRef,
- .lockedRef = info.flake.lockedRef,
- .attrPath = info.value.attrPath,
- .outputs = installable->extendedOutputsSpec,
- };
+ auto installable = make_ref<InstallableFlake>(
+ this,
+ getEvalState(),
+ FlakeRef(element.source->originalRef),
+ "",
+ element.source->outputs,
+ Strings{element.source->attrPath},
+ Strings{},
+ lockFlags
+ );
- installables.push_back(installable);
- indices.push_back(i);
+ auto derivedPaths = installable->toDerivedPaths();
+ if (derivedPaths.empty()) {
+ continue;
}
+
+ auto * infop = dynamic_cast<ExtraPathInfoFlake *>(&*derivedPaths[0].info);
+ // `InstallableFlake` should use `ExtraPathInfoFlake`.
+ assert(infop);
+ auto & info = *infop;
+
+ if (element.source->lockedRef == info.flake.lockedRef) {
+ continue;
+ }
+
+ printInfo(
+ "upgrading '%s' from flake '%s' to '%s'",
+ element.source->attrPath,
+ element.source->lockedRef,
+ info.flake.lockedRef
+ );
+
+ element.source = ProfileElementSource {
+ .originalRef = installable->flakeRef,
+ .lockedRef = info.flake.lockedRef,
+ .attrPath = info.value.attrPath,
+ .outputs = installable->extendedOutputsSpec,
+ };
+
+ installables.push_back(installable);
+ indices.push_back(i);
+
}
if (upgradedCount == 0) {
- for (auto & matcher : matchers) {
- if (const size_t * index = std::get_if<size_t>(&matcher)){
- warn("'%d' is not a valid index", *index);
- } else if (const Path * path = std::get_if<Path>(&matcher)){
- warn("'%s' does not match any paths", *path);
- } else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)){
- warn("'%s' does not match any packages", regex->pattern);
+ if (matchedCount == 0) {
+ for (auto & matcher : matchers) {
+ if (const size_t * index = std::get_if<size_t>(&matcher)){
+ warn("'%d' is not a valid index", *index);
+ } else if (const Path * path = std::get_if<Path>(&matcher)){
+ warn("'%s' does not match any paths", *path);
+ } else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)){
+ warn("'%s' does not match any packages", regex->pattern);
+ }
}
+ } else {
+ warn("Found some packages but none of them could be upgraded");
}
warn ("Use 'nix profile list' to see the current profile.");
}
@@ -394,10 +444,18 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro
} else {
for (size_t i = 0; i < manifest.elements.size(); ++i) {
auto & element(manifest.elements[i]);
- if (i) logger->cout("");
- logger->cout("Index: " ANSI_BOLD "%s" ANSI_NORMAL "%s",
- i,
- element.active ? "" : " " ANSI_RED "(inactive)" ANSI_NORMAL);
+ if (i) {
+ logger->cout("");
+ }
+ logger->cout(
+ "Name: " ANSI_BOLD "%s" ANSI_NORMAL "%s",
+ element.name,
+ element.active ? "" : " " ANSI_RED "(inactive)" ANSI_NORMAL
+ );
+ logger->cout(
+ "Index: " ANSI_BOLD "%s" ANSI_NORMAL "%S",
+ i
+ );
if (element.source) {
logger->cout("Flake attribute: %s%s", element.source->attrPath, element.source->outputs.to_string());
logger->cout("Original flake URL: %s", element.source->originalRef.to_string());
diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh
index 7c478a0cd..b19341851 100644
--- a/tests/functional/nix-profile.sh
+++ b/tests/functional/nix-profile.sh
@@ -47,7 +47,7 @@ cp ./config.nix $flake1Dir/
# Test upgrading from nix-env.
nix-env -f ./user-envs.nix -i foo-1.0
-nix profile list | grep -A2 'Index:.*0' | grep 'Store paths:.*foo-1.0'
+nix profile list | grep -A2 'Name:.*foo' | grep 'Store paths:.*foo-1.0'
nix profile install $flake1Dir -L
nix profile list | grep -A4 'Index:.*1' | grep 'Locked flake URL:.*narHash'
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]]
@@ -81,7 +81,7 @@ nix profile rollback
# Test uninstall.
[ -e $TEST_HOME/.nix-profile/bin/foo ]
-nix profile remove 0
+nix profile remove "foo"
(! [ -e $TEST_HOME/.nix-profile/bin/foo ])
nix profile history | grep 'foo: 1.0 -> ∅'
nix profile diff-closures | grep 'Version 3 -> 4'
@@ -93,6 +93,13 @@ nix profile remove 1
nix profile install $(nix-build --no-out-link ./simple.nix)
[[ $(cat $TEST_HOME/.nix-profile/hello) = "Hello World!" ]]
+# Test packages with same name from different sources
+mkdir $TEST_ROOT/simple-too
+cp ./simple.nix ./config.nix simple.builder.sh $TEST_ROOT/simple-too
+nix profile install --file $TEST_ROOT/simple-too/simple.nix ''
+nix profile list | grep -A4 'Name:.*simple' | grep 'Name:.*simple1'
+nix profile remove simple1
+
# Test wipe-history.
nix profile wipe-history
[[ $(nix profile history | grep Version | wc -l) -eq 1 ]]
@@ -104,7 +111,7 @@ nix profile upgrade 0
nix profile history | grep "packages.$system.default: 1.0, 1.0-man -> 3.0, 3.0-man"
# Test new install of CA package.
-nix profile remove 0
+nix profile remove flake1
printf 4.0 > $flake1Dir/version
printf Utrecht > $flake1Dir/who
nix profile install $flake1Dir
@@ -112,19 +119,20 @@ nix profile install $flake1Dir
[[ $(nix path-info --json $(realpath $TEST_HOME/.nix-profile/bin/hello) | jq -r .[].ca) =~ fixed:r:sha256: ]]
# Override the outputs.
-nix profile remove 0 1
+nix profile remove simple flake1
nix profile install "$flake1Dir^*"
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Utrecht" ]]
[ -e $TEST_HOME/.nix-profile/share/man ]
[ -e $TEST_HOME/.nix-profile/include ]
printf Nix > $flake1Dir/who
-nix profile upgrade 0
+nix profile list
+nix profile upgrade flake1
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Nix" ]]
[ -e $TEST_HOME/.nix-profile/share/man ]
[ -e $TEST_HOME/.nix-profile/include ]
-nix profile remove 0
+nix profile remove flake1
nix profile install "$flake1Dir^man"
(! [ -e $TEST_HOME/.nix-profile/bin/hello ])
[ -e $TEST_HOME/.nix-profile/share/man ]