mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-25 15:26:17 +02:00
Enable using human-readable name in nix profile
This commit is contained in:
parent
150b5aba50
commit
257b768436
9 changed files with 274 additions and 74 deletions
|
@ -64,3 +64,5 @@
|
||||||
```
|
```
|
||||||
|
|
||||||
This makes it match `nix derivation show`, which also maps store paths to information.
|
This makes it match `nix derivation show`, which also maps store paths to information.
|
||||||
|
|
||||||
|
- [`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.
|
||||||
|
|
64
src/libutil/tests/url-name.cc
Normal file
64
src/libutil/tests/url-name.cc
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#include "url-name.hh"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* ----------- tests for url-name.hh --------------------------------------------------*/
|
||||||
|
|
||||||
|
TEST(getNameFromURL, getsNameFromURL) {
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("path:/home/user/project")), "project");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("path:~/repos/nixpkgs#packages.x86_64-linux.hello")), "hello");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("path:.#nonStandardAttr.mylaptop")), "nonStandardAttr.mylaptop");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("path:./repos/myflake#nonStandardAttr.mylaptop")), "nonStandardAttr.mylaptop");
|
||||||
|
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("github:NixOS/nixpkgs#packages.x86_64-linux.hello")), "hello");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("github:NixOS/nixpkgs#hello")), "hello");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("github:NixOS/nix#packages.x86_64-linux.default")), "nix");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("github:NixOS/nix#")), "nix");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("github:NixOS/nix")), "nix");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("github:cachix/devenv/main#packages.x86_64-linux.default")), "devenv");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("github:edolstra/nix-warez?rev=1234&dir=blender&ref=master")), "blender");
|
||||||
|
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("gitlab:NixOS/nixpkgs#packages.x86_64-linux.hello")), "hello");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("gitlab:NixOS/nixpkgs#hello")), "hello");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("gitlab:NixOS/nix#packages.x86_64-linux.default")), "nix");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("gitlab:NixOS/nix#")), "nix");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("gitlab:NixOS/nix")), "nix");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("gitlab:cachix/devenv/main#packages.x86_64-linux.default")), "devenv");
|
||||||
|
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("sourcehut:NixOS/nixpkgs#packages.x86_64-linux.hello")), "hello");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("sourcehut:NixOS/nixpkgs#hello")), "hello");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("sourcehut:NixOS/nix#packages.x86_64-linux.default")), "nix");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("sourcehut:NixOS/nix#")), "nix");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("sourcehut:NixOS/nix")), "nix");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("sourcehut:cachix/devenv/main#packages.x86_64-linux.default")), "devenv");
|
||||||
|
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("git://github.com/edolstra/dwarffs")), "dwarffs");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("git://github.com/edolstra/nix-warez?dir=blender")), "blender");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("git+file:///home/user/project")), "project");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("git+file:///home/user/project?ref=fa1e2d23a22")), "project");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("git+ssh://git@github.com/someuser/my-repo#")), "my-repo");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("git+git://github.com/someuser/my-repo?rev=v1.2.3")), "my-repo");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("git+ssh:///home/user/project?dir=subproject&rev=v2.4")), "subproject");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("git+http://not-even-real#packages.x86_64-linux.hello")), "hello");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("git+https://not-even-real#packages.aarch64-darwin.hello")), "hello");
|
||||||
|
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("tarball+http://github.com/NixOS/nix/archive/refs/tags/2.18.1#packages.x86_64-linux.jq")), "jq");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("tarball+https://github.com/NixOS/nix/archive/refs/tags/2.18.1#packages.x86_64-linux.hg")), "hg");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("tarball+file:///home/user/Downloads/nixpkgs-2.18.1#packages.aarch64-darwin.ripgrep")), "ripgrep");
|
||||||
|
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("https://github.com/NixOS/nix/archive/refs/tags/2.18.1.tar.gz#packages.x86_64-linux.pv")), "pv");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("http://github.com/NixOS/nix/archive/refs/tags/2.18.1.tar.gz#packages.x86_64-linux.pv")), "pv");
|
||||||
|
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("file:///home/user/project?ref=fa1e2d23a22")), "project");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("file+file:///home/user/project?ref=fa1e2d23a22")), "project");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("file+http://not-even-real#packages.x86_64-linux.hello")), "hello");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("file+http://gitfantasy.com/org/user/notaflake")), "notaflake");
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("file+https://not-even-real#packages.aarch64-darwin.hello")), "hello");
|
||||||
|
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("https://www.github.com/")), std::nullopt);
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("path:.")), std::nullopt);
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("file:.#")), std::nullopt);
|
||||||
|
ASSERT_EQ(getNameFromURL(parseURL("path:.#packages.x86_64-linux.default")), std::nullopt);
|
||||||
|
}
|
||||||
|
}
|
46
src/libutil/url-name.cc
Normal file
46
src/libutil/url-name.cc
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#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($|\\+.*)");
|
||||||
|
|
||||||
|
std::optional<std::string> getNameFromURL(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() && !hasSuffix(url.fragment, "default"))
|
||||||
|
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
src/libutil/url-name.hh
Normal file
20
src/libutil/url-name.hh
Normal file
|
@ -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(ParsedURL url);
|
||||||
|
|
||||||
|
}
|
|
@ -6,12 +6,14 @@ R""(
|
||||||
|
|
||||||
```console
|
```console
|
||||||
# nix profile list
|
# nix profile list
|
||||||
|
Name: gdb
|
||||||
Index: 0
|
Index: 0
|
||||||
Flake attribute: legacyPackages.x86_64-linux.gdb
|
Flake attribute: legacyPackages.x86_64-linux.gdb
|
||||||
Original flake URL: flake:nixpkgs
|
Original flake URL: flake:nixpkgs
|
||||||
Locked flake URL: github:NixOS/nixpkgs/7b38b03d76ab71bdc8dc325e3f6338d984cc35ca
|
Locked flake URL: github:NixOS/nixpkgs/7b38b03d76ab71bdc8dc325e3f6338d984cc35ca
|
||||||
Store paths: /nix/store/indzcw5wvlhx6vwk7k4iq29q15chvr3d-gdb-11.1
|
Store paths: /nix/store/indzcw5wvlhx6vwk7k4iq29q15chvr3d-gdb-11.1
|
||||||
|
|
||||||
|
Name: blender-bin
|
||||||
Index: 1
|
Index: 1
|
||||||
Flake attribute: packages.x86_64-linux.default
|
Flake attribute: packages.x86_64-linux.default
|
||||||
Original flake URL: flake:blender-bin
|
Original flake URL: flake:blender-bin
|
||||||
|
@ -26,7 +28,7 @@ R""(
|
||||||
# nix build github:edolstra/nix-warez/91f2ffee657bf834e4475865ae336e2379282d34?dir=blender#packages.x86_64-linux.default
|
# 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
|
# Description
|
||||||
|
|
||||||
|
@ -34,10 +36,14 @@ This command shows what packages are currently installed in a
|
||||||
profile. For each installed package, it shows the following
|
profile. For each installed package, it shows the following
|
||||||
information:
|
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
|
package in invocations of `nix profile remove` and `nix profile
|
||||||
upgrade`.
|
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
|
* `Flake attribute`: The flake output attribute path that provides the
|
||||||
package (e.g. `packages.x86_64-linux.hello`).
|
package (e.g. `packages.x86_64-linux.hello`).
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,19 @@ R""(
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
* Remove a package by position:
|
* Remove a package by name:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix profile remove hello
|
||||||
|
```
|
||||||
|
|
||||||
|
* Remove a package by index
|
||||||
|
*(deprecated, will be removed in a future version)*:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
# nix profile remove 3
|
# nix profile remove 3
|
||||||
```
|
```
|
||||||
|
|
||||||
* Remove a package by attribute path:
|
|
||||||
|
|
||||||
```console
|
|
||||||
# nix profile remove packages.x86_64-linux.hello
|
|
||||||
```
|
|
||||||
|
|
||||||
* Remove all packages:
|
* Remove all packages:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
|
|
|
@ -9,18 +9,16 @@ R""(
|
||||||
# nix profile upgrade '.*'
|
# nix profile upgrade '.*'
|
||||||
```
|
```
|
||||||
|
|
||||||
* Upgrade a specific package:
|
* Upgrade a specific package by name:
|
||||||
|
|
||||||
```console
|
```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
|
```console
|
||||||
# nix profile list
|
|
||||||
0 flake:nixpkgs#legacyPackages.x86_64-linux.spotify …
|
|
||||||
|
|
||||||
# nix profile upgrade 0
|
# nix profile upgrade 0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#include "../nix-env/user-env.hh"
|
#include "../nix-env/user-env.hh"
|
||||||
#include "profiles.hh"
|
#include "profiles.hh"
|
||||||
#include "names.hh"
|
#include "names.hh"
|
||||||
|
#include "url.hh"
|
||||||
|
#include "url-name.hh"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
@ -43,6 +45,7 @@ const int defaultPriority = 5;
|
||||||
struct ProfileElement
|
struct ProfileElement
|
||||||
{
|
{
|
||||||
StorePathSet storePaths;
|
StorePathSet storePaths;
|
||||||
|
std::string name;
|
||||||
std::optional<ProfileElementSource> source;
|
std::optional<ProfileElementSource> source;
|
||||||
bool active = true;
|
bool active = true;
|
||||||
int priority = defaultPriority;
|
int priority = defaultPriority;
|
||||||
|
@ -116,6 +119,8 @@ struct ProfileManifest
|
||||||
|
|
||||||
if (pathExists(manifestPath)) {
|
if (pathExists(manifestPath)) {
|
||||||
auto json = nlohmann::json::parse(readFile(manifestPath));
|
auto json = nlohmann::json::parse(readFile(manifestPath));
|
||||||
|
/* Keep track of alreay found names to allow preventing duplicates */
|
||||||
|
std::set<std::string> foundNames;
|
||||||
|
|
||||||
auto version = json.value("version", 0);
|
auto version = json.value("version", 0);
|
||||||
std::string sUrl;
|
std::string sUrl;
|
||||||
|
@ -149,6 +154,25 @@ struct ProfileManifest
|
||||||
e["outputs"].get<ExtendedOutputsSpec>()
|
e["outputs"].get<ExtendedOutputsSpec>()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string nameCandidate = element.identifier();
|
||||||
|
if (e.contains("name")) {
|
||||||
|
nameCandidate = e["name"];
|
||||||
|
}
|
||||||
|
else if (element.source) {
|
||||||
|
auto url = parseURL(element.source->to_string());
|
||||||
|
auto name = getNameFromURL(url);
|
||||||
|
if (name)
|
||||||
|
nameCandidate = *name;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto finalName = nameCandidate;
|
||||||
|
for (int i = 1; foundNames.contains(finalName); ++i) {
|
||||||
|
finalName = nameCandidate + std::to_string(i);
|
||||||
|
}
|
||||||
|
element.name = finalName;
|
||||||
|
foundNames.insert(element.name);
|
||||||
|
|
||||||
elements.emplace_back(std::move(element));
|
elements.emplace_back(std::move(element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,6 +187,7 @@ struct ProfileManifest
|
||||||
for (auto & drvInfo : drvInfos) {
|
for (auto & drvInfo : drvInfos) {
|
||||||
ProfileElement element;
|
ProfileElement element;
|
||||||
element.storePaths = {drvInfo.queryOutPath()};
|
element.storePaths = {drvInfo.queryOutPath()};
|
||||||
|
element.name = element.identifier();
|
||||||
elements.emplace_back(std::move(element));
|
elements.emplace_back(std::move(element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -451,15 +476,25 @@ public:
|
||||||
{
|
{
|
||||||
std::vector<Matcher> res;
|
std::vector<Matcher> res;
|
||||||
|
|
||||||
|
auto anyIndexMatchers = false;
|
||||||
|
|
||||||
for (auto & s : _matchers) {
|
for (auto & s : _matchers) {
|
||||||
if (auto n = string2Int<size_t>(s))
|
if (auto n = string2Int<size_t>(s)) {
|
||||||
res.push_back(*n);
|
res.push_back(*n);
|
||||||
|
anyIndexMatchers = true;
|
||||||
|
}
|
||||||
else if (store->isStorePath(s))
|
else if (store->isStorePath(s))
|
||||||
res.push_back(s);
|
res.push_back(s);
|
||||||
else
|
else
|
||||||
res.push_back(RegexPattern{s,std::regex(s, std::regex::extended | std::regex::icase)});
|
res.push_back(RegexPattern{s,std::regex(s, std::regex::extended | std::regex::icase)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (anyIndexMatchers) {
|
||||||
|
warn("Indices are deprecated and will be removed in a future version!\n"
|
||||||
|
" Refer to packages by their `Name` as printed by `nix profile list`.\n"
|
||||||
|
" See https://github.com/NixOS/nix/issues/9171 for more information.");
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,8 +506,7 @@ public:
|
||||||
} else if (auto path = std::get_if<Path>(&matcher)) {
|
} else if (auto path = std::get_if<Path>(&matcher)) {
|
||||||
if (element.storePaths.count(store.parseStorePath(*path))) return true;
|
if (element.storePaths.count(store.parseStorePath(*path))) return true;
|
||||||
} else if (auto regex = std::get_if<RegexPattern>(&matcher)) {
|
} else if (auto regex = std::get_if<RegexPattern>(&matcher)) {
|
||||||
if (element.source
|
if (std::regex_match(element.name, regex->reg))
|
||||||
&& std::regex_match(element.source->attrPath, regex->reg))
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -556,62 +590,83 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
||||||
Installables installables;
|
Installables installables;
|
||||||
std::vector<size_t> indices;
|
std::vector<size_t> indices;
|
||||||
|
|
||||||
|
auto matchedCount = 0;
|
||||||
auto upgradedCount = 0;
|
auto upgradedCount = 0;
|
||||||
|
|
||||||
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
||||||
auto & element(manifest.elements[i]);
|
auto & element(manifest.elements[i]);
|
||||||
if (element.source
|
if (!matches(*store, element, i, matchers)) {
|
||||||
&& !element.source->originalRef.input.isLocked()
|
continue;
|
||||||
&& matches(*store, element, i, matchers))
|
|
||||||
{
|
|
||||||
upgradedCount++;
|
|
||||||
|
|
||||||
Activity act(*logger, lvlChatty, actUnknown,
|
|
||||||
fmt("checking '%s' for updates", element.source->attrPath));
|
|
||||||
|
|
||||||
auto installable = make_ref<InstallableFlake>(
|
|
||||||
this,
|
|
||||||
getEvalState(),
|
|
||||||
FlakeRef(element.source->originalRef),
|
|
||||||
"",
|
|
||||||
element.source->outputs,
|
|
||||||
Strings{element.source->attrPath},
|
|
||||||
Strings{},
|
|
||||||
lockFlags);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
matchedCount++;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
upgradedCount++;
|
||||||
|
|
||||||
|
Activity act(*logger, lvlChatty, actUnknown,
|
||||||
|
fmt("checking '%s' for updates", element.source->attrPath));
|
||||||
|
|
||||||
|
auto installable = make_ref<InstallableFlake>(
|
||||||
|
this,
|
||||||
|
getEvalState(),
|
||||||
|
FlakeRef(element.source->originalRef),
|
||||||
|
"",
|
||||||
|
element.source->outputs,
|
||||||
|
Strings{element.source->attrPath},
|
||||||
|
Strings{},
|
||||||
|
lockFlags);
|
||||||
|
|
||||||
|
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) {
|
if (upgradedCount == 0) {
|
||||||
for (auto & matcher : matchers) {
|
if (matchedCount == 0) {
|
||||||
if (const size_t * index = std::get_if<size_t>(&matcher)){
|
for (auto & matcher : matchers) {
|
||||||
warn("'%d' is not a valid index", *index);
|
if (const size_t * index = std::get_if<size_t>(&matcher)){
|
||||||
} else if (const Path * path = std::get_if<Path>(&matcher)){
|
warn("'%d' is not a valid index", *index);
|
||||||
warn("'%s' does not match any paths", *path);
|
} else if (const Path * path = std::get_if<Path>(&matcher)){
|
||||||
} else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)){
|
warn("'%s' does not match any paths", *path);
|
||||||
warn("'%s' does not match any packages", regex->pattern);
|
} 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.");
|
warn ("Use 'nix profile list' to see the current profile.");
|
||||||
}
|
}
|
||||||
|
@ -657,9 +712,10 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro
|
||||||
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
||||||
auto & element(manifest.elements[i]);
|
auto & element(manifest.elements[i]);
|
||||||
if (i) logger->cout("");
|
if (i) logger->cout("");
|
||||||
logger->cout("Index: " ANSI_BOLD "%s" ANSI_NORMAL "%s",
|
logger->cout("Name: " ANSI_BOLD "%s" ANSI_NORMAL "%s",
|
||||||
i,
|
element.name,
|
||||||
element.active ? "" : " " ANSI_RED "(inactive)" ANSI_NORMAL);
|
element.active ? "" : " " ANSI_RED "(inactive)" ANSI_NORMAL);
|
||||||
|
logger->cout("Index: %s", i);
|
||||||
if (element.source) {
|
if (element.source) {
|
||||||
logger->cout("Flake attribute: %s%s", element.source->attrPath, element.source->outputs.to_string());
|
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());
|
logger->cout("Original flake URL: %s", element.source->originalRef.to_string());
|
||||||
|
|
|
@ -47,7 +47,7 @@ cp ./config.nix $flake1Dir/
|
||||||
|
|
||||||
# Test upgrading from nix-env.
|
# Test upgrading from nix-env.
|
||||||
nix-env -f ./user-envs.nix -i foo-1.0
|
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 install $flake1Dir -L
|
||||||
nix profile list | grep -A4 'Index:.*1' | grep 'Locked flake URL:.*narHash'
|
nix profile list | grep -A4 'Index:.*1' | grep 'Locked flake URL:.*narHash'
|
||||||
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]]
|
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]]
|
||||||
|
@ -81,7 +81,7 @@ nix profile rollback
|
||||||
|
|
||||||
# Test uninstall.
|
# Test uninstall.
|
||||||
[ -e $TEST_HOME/.nix-profile/bin/foo ]
|
[ -e $TEST_HOME/.nix-profile/bin/foo ]
|
||||||
nix profile remove 0
|
nix profile remove foo
|
||||||
(! [ -e $TEST_HOME/.nix-profile/bin/foo ])
|
(! [ -e $TEST_HOME/.nix-profile/bin/foo ])
|
||||||
nix profile history | grep 'foo: 1.0 -> ∅'
|
nix profile history | grep 'foo: 1.0 -> ∅'
|
||||||
nix profile diff-closures | grep 'Version 3 -> 4'
|
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)
|
nix profile install $(nix-build --no-out-link ./simple.nix)
|
||||||
[[ $(cat $TEST_HOME/.nix-profile/hello) = "Hello World!" ]]
|
[[ $(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.
|
# Test wipe-history.
|
||||||
nix profile wipe-history
|
nix profile wipe-history
|
||||||
[[ $(nix profile history | grep Version | wc -l) -eq 1 ]]
|
[[ $(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"
|
nix profile history | grep "packages.$system.default: 1.0, 1.0-man -> 3.0, 3.0-man"
|
||||||
|
|
||||||
# Test new install of CA package.
|
# Test new install of CA package.
|
||||||
nix profile remove 0
|
nix profile remove flake1
|
||||||
printf 4.0 > $flake1Dir/version
|
printf 4.0 > $flake1Dir/version
|
||||||
printf Utrecht > $flake1Dir/who
|
printf Utrecht > $flake1Dir/who
|
||||||
nix profile install $flake1Dir
|
nix profile install $flake1Dir
|
||||||
|
@ -112,26 +119,26 @@ nix profile install $flake1Dir
|
||||||
[[ $(nix path-info --json $(realpath $TEST_HOME/.nix-profile/bin/hello) | jq -r .[].ca) =~ fixed:r:sha256: ]]
|
[[ $(nix path-info --json $(realpath $TEST_HOME/.nix-profile/bin/hello) | jq -r .[].ca) =~ fixed:r:sha256: ]]
|
||||||
|
|
||||||
# Override the outputs.
|
# Override the outputs.
|
||||||
nix profile remove 0 1
|
nix profile remove simple flake1
|
||||||
nix profile install "$flake1Dir^*"
|
nix profile install "$flake1Dir^*"
|
||||||
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Utrecht" ]]
|
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Utrecht" ]]
|
||||||
[ -e $TEST_HOME/.nix-profile/share/man ]
|
[ -e $TEST_HOME/.nix-profile/share/man ]
|
||||||
[ -e $TEST_HOME/.nix-profile/include ]
|
[ -e $TEST_HOME/.nix-profile/include ]
|
||||||
|
|
||||||
printf Nix > $flake1Dir/who
|
printf Nix > $flake1Dir/who
|
||||||
nix profile upgrade 0
|
nix profile upgrade flake1
|
||||||
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Nix" ]]
|
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Nix" ]]
|
||||||
[ -e $TEST_HOME/.nix-profile/share/man ]
|
[ -e $TEST_HOME/.nix-profile/share/man ]
|
||||||
[ -e $TEST_HOME/.nix-profile/include ]
|
[ -e $TEST_HOME/.nix-profile/include ]
|
||||||
|
|
||||||
nix profile remove 0
|
nix profile remove flake1
|
||||||
nix profile install "$flake1Dir^man"
|
nix profile install "$flake1Dir^man"
|
||||||
(! [ -e $TEST_HOME/.nix-profile/bin/hello ])
|
(! [ -e $TEST_HOME/.nix-profile/bin/hello ])
|
||||||
[ -e $TEST_HOME/.nix-profile/share/man ]
|
[ -e $TEST_HOME/.nix-profile/share/man ]
|
||||||
(! [ -e $TEST_HOME/.nix-profile/include ])
|
(! [ -e $TEST_HOME/.nix-profile/include ])
|
||||||
|
|
||||||
# test priority
|
# test priority
|
||||||
nix profile remove 0
|
nix profile remove flake1
|
||||||
|
|
||||||
# Make another flake.
|
# Make another flake.
|
||||||
flake2Dir=$TEST_ROOT/flake2
|
flake2Dir=$TEST_ROOT/flake2
|
||||||
|
|
Loading…
Reference in a new issue