Skip to content

Commit

Permalink
Merge pull request NixOS#8678 from iFreilicht/profile-names-instead-o…
Browse files Browse the repository at this point in the history
…f-index

`nix profile`: Allow referring to elements by human-readable name
  • Loading branch information
edolstra authored Dec 21, 2023
2 parents 8e64cd5 + 942d635 commit 17c202b
Show file tree
Hide file tree
Showing 12 changed files with 281 additions and 70 deletions.
6 changes: 6 additions & 0 deletions doc/manual/rl-next/nix-profile-names.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
synopsis: "`nix profile` now allows referring to elements by human-readable name"
prs: 8678
---

[`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.
2 changes: 1 addition & 1 deletion src/libexpr/flake/flakeref.cc
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef(

static std::regex flakeRegex(
"((" + flakeIdRegexS + ")(?:/(?:" + refAndOrRevRegex + "))?)"
+ "(?:#(" + queryRegex + "))?",
+ "(?:#(" + fragmentRegex + "))?",
std::regex::ECMAScript);

if (std::regex_match(url, match, flakeRegex)) {
Expand Down
48 changes: 48 additions & 0 deletions src/libutil/url-name.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "url-name.hh"
#include <regex>
#include <iostream>

namespace nix {

static const std::string attributeNamePattern("[a-z0-9_-]+");
static const std::regex lastAttributeRegex("(?:" + attributeNamePattern + "\\.)*(?!default)(" + attributeNamePattern +")(\\^.*)?");
static const std::string pathSegmentPattern("[a-zA-Z0-9_-]+");
static const std::regex lastPathSegmentRegex(".*/(" + pathSegmentPattern +")");
static const std::regex secondPathSegmentRegex("(?:" + pathSegmentPattern + ")/(" + pathSegmentPattern +")(?:/.*)?");
static const std::regex gitProviderRegex("github|gitlab|sourcehut");
static const std::regex gitSchemeRegex("git($|\\+.*)");
static const std::regex defaultOutputRegex(".*\\.default($|\\^.*)");

std::optional<std::string> getNameFromURL(const ParsedURL & url)
{
std::smatch match;

/* If there is a dir= argument, use its value */
if (url.query.count("dir") > 0)
return url.query.at("dir");

/* If the fragment isn't a "default" and contains two attribute elements, use the last one */
if (std::regex_match(url.fragment, match, lastAttributeRegex))
return match.str(1);

/* If this is a github/gitlab/sourcehut flake, use the repo name */
if (std::regex_match(url.scheme, gitProviderRegex) && std::regex_match(url.path, match, secondPathSegmentRegex))
return match.str(1);

/* If it is a regular git flake, use the directory name */
if (std::regex_match(url.scheme, gitSchemeRegex) && std::regex_match(url.path, match, lastPathSegmentRegex))
return match.str(1);

/* If everything failed but there is a non-default fragment, use it in full */
if (!url.fragment.empty() && !std::regex_match(url.fragment, defaultOutputRegex))
return url.fragment;

/* If there is no fragment, take the last element of the path */
if (std::regex_match(url.path, match, lastPathSegmentRegex))
return match.str(1);

/* If even that didn't work, the URL does not contain enough info to determine a useful name */
return {};
}

}
20 changes: 20 additions & 0 deletions src/libutil/url-name.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "url.hh"
#include "url-parts.hh"
#include "util.hh"
#include "split.hh"

namespace nix {

/**
* Try to extract a reasonably unique and meaningful, human-readable
* name of a flake output from a parsed URL.
* When nullopt is returned, the callsite should use information available
* to it outside of the URL to determine a useful name.
* This is a heuristic approach intended for user interfaces.
* @return nullopt if the extracted name is not useful to identify a
* flake output, for example because it is empty or "default".
* Otherwise returns the extracted name.
*/
std::optional<std::string> getNameFromURL(const ParsedURL & url);

}
1 change: 1 addition & 0 deletions src/libutil/url-parts.hh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const static std::string userRegex = "(?:(?:" + unreservedRegex + "|" + pctEncod
const static std::string authorityRegex = "(?:" + userRegex + "@)?" + hostRegex + "(?::[0-9]+)?";
const static std::string pcharRegex = "(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|[:@])";
const static std::string queryRegex = "(?:" + pcharRegex + "|[/? \"])*";
const static std::string fragmentRegex = "(?:" + pcharRegex + "|[/? \"^])*";
const static std::string segmentRegex = "(?:" + pcharRegex + "*)";
const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)";
const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)";
Expand Down
2 changes: 1 addition & 1 deletion src/libutil/url.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ ParsedURL parseURL(const std::string & url)
"((" + schemeNameRegex + "):"
+ "(?:(?://(" + authorityRegex + ")(" + absPathRegex + "))|(/?" + pathRegex + ")))"
+ "(?:\\?(" + queryRegex + "))?"
+ "(?:#(" + queryRegex + "))?",
+ "(?:#(" + fragmentRegex + "))?",
std::regex::ECMAScript);

std::smatch match;
Expand Down
10 changes: 8 additions & 2 deletions src/nix/profile-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ 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

Name: blender-bin
Index: 1
Flake attribute: packages.x86_64-linux.default
Original flake URL: flake:blender-bin
Expand All @@ -26,18 +28,22 @@ 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 `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
9 changes: 5 additions & 4 deletions src/nix/profile-remove.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +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 attribute path:
* Remove a package by index
*(deprecated, will be removed in a future version)*:

```console
# nix profile remove packages.x86_64-linux.hello
# nix profile remove 3
```

* Remove all packages:
Expand Down
10 changes: 4 additions & 6 deletions src/nix/profile-upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,16 @@ R""(
# nix profile upgrade '.*'
```

* Upgrade a specific package:
* Upgrade a specific package by name:

```console
# nix profile upgrade packages.x86_64-linux.hello
# nix profile upgrade hello
```

* Upgrade a specific profile element by number:
* Upgrade a specific package by index
*(deprecated, will be removed in a future version)*:

```console
# nix profile list
0 flake:nixpkgs#legacyPackages.x86_64-linux.spotify …

# nix profile upgrade 0
```

Expand Down
Loading

0 comments on commit 17c202b

Please sign in to comment.