Skip to content

Commit

Permalink
nix3-profile: allow using human-readable names to select packages
Browse files Browse the repository at this point in the history
These names are parsed from the URL provided for that package

Based off of commit 257b768

Upstream-PR: NixOS/nix#8678
Co-authored-by: Felix Uhl <[email protected]>
Change-Id: I76d5f9cfb11d3d2915b3dd1db21d7bb49e91f4fb
  • Loading branch information
Qyriad and iFreilicht committed May 2, 2024
1 parent f719de7 commit 8b0b735
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 64 deletions.
7 changes: 7 additions & 0 deletions doc/manual/rl-next/nix-profile-names.md
Original file line number Diff line number Diff line change
@@ -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.
26 changes: 26 additions & 0 deletions src/libcmd/cmd-profiles.cc
Original file line number Diff line number Diff line change
@@ -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"

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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")) {
Expand All @@ -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));
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/libcmd/cmd-profiles.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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;
Expand Down
9 changes: 7 additions & 2 deletions src/nix/profile-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ R""(

```console
# nix profile list
Name: gdb
Index: 0
Flake attribute: legacyPackages.x86_64-linux.gdb
Original flake URL: flake:nixpkgs
Locked flake URL: github:NixOS/nixpkgs/7b38b03d76ab71bdc8dc325e3f6338d984cc35ca
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
Expand All @@ -26,18 +28,21 @@ 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

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`).

Expand Down
11 changes: 9 additions & 2 deletions src/nix/profile-remove.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
11 changes: 6 additions & 5 deletions src/nix/profile-upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down
156 changes: 107 additions & 49 deletions src/nix/profile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,24 @@ class MixProfileElementMatchers : virtual Args
{
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;
Expand All @@ -206,12 +217,13 @@ class MixProfileElementMatchers : virtual Args
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;
}
}
}

Expand Down Expand Up @@ -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.");
}
Expand Down Expand Up @@ -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());
Expand Down
Loading

0 comments on commit 8b0b735

Please sign in to comment.