forked from lix-project/lix
Support relative and ~/
paths in config settings
Change-Id: I5566a9858ba255f4ac5051d1368c7dfb24460f0a
This commit is contained in:
parent
5fc6fcb310
commit
690f07272e
30
doc/manual/rl-next/relative-and-tilde-paths-in-config.md
Normal file
30
doc/manual/rl-next/relative-and-tilde-paths-in-config.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
synopsis: Relative and tilde paths in configuration
|
||||||
|
issues: [fj#482]
|
||||||
|
cls: [1851, 1863, 1864]
|
||||||
|
category: Features
|
||||||
|
credits: [9999years]
|
||||||
|
---
|
||||||
|
|
||||||
|
[Configuration settings](@docroot@/command-ref/conf-file.md) can now refer to
|
||||||
|
files with paths relative to the file they're written in or relative to your
|
||||||
|
home directory (with `~/`).
|
||||||
|
|
||||||
|
This makes settings like
|
||||||
|
[`repl-overlays`](@docroot@/command-ref/conf-file.md#conf-repl-overlays) and
|
||||||
|
[`secret-key-files`](@docroot@/command-ref/conf-file.md#conf-repl-overlays)
|
||||||
|
much easier to set, especially if you'd like to refer to files in an existing
|
||||||
|
dotfiles repo cloned into your home directory.
|
||||||
|
|
||||||
|
If you put `repl-overlays = repl.nix` in your `~/.config/nix/nix.conf`, it'll
|
||||||
|
load `~/.config/nix/repl.nix`. Similarly, you can set `repl-overlays =
|
||||||
|
~/.dotfiles/repl.nix` to load a file relative to your home directory.
|
||||||
|
|
||||||
|
Configuration files can also
|
||||||
|
[`include`](@docroot@/command-ref/conf-file.md#file-format) paths relative to
|
||||||
|
your home directory.
|
||||||
|
|
||||||
|
Only user configuration files (like `$XDG_CONFIG_HOME/nix/nix.conf` or the
|
||||||
|
files listed in `$NIX_USER_CONF_FILES`) can use tilde paths relative to your
|
||||||
|
home directory. Configuration listed in the `$NIX_CONFIG` environment variable
|
||||||
|
may not use relative paths.
|
|
@ -131,8 +131,9 @@ void loadConfFile()
|
||||||
globalConfig.resetOverridden();
|
globalConfig.resetOverridden();
|
||||||
|
|
||||||
auto files = settings.nixUserConfFiles;
|
auto files = settings.nixUserConfFiles;
|
||||||
|
auto home = getHome();
|
||||||
for (auto file = files.rbegin(); file != files.rend(); file++) {
|
for (auto file = files.rbegin(); file != files.rend(); file++) {
|
||||||
applyConfigFile(ApplyConfigOptions{.path = *file});
|
applyConfigFile(ApplyConfigOptions{.path = *file, .home = home});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nixConfEnv = getEnv("NIX_CONFIG");
|
auto nixConfEnv = getEnv("NIX_CONFIG");
|
||||||
|
|
|
@ -126,7 +126,7 @@ static void applyConfigInner(const std::string & contents, const ApplyConfigOpti
|
||||||
if (!options.path) {
|
if (!options.path) {
|
||||||
throw UsageError("can only include configuration '%1%' from files", tokens[1]);
|
throw UsageError("can only include configuration '%1%' from files", tokens[1]);
|
||||||
}
|
}
|
||||||
auto pathToInclude = absPath(tokens[1], dirOf(*options.path));
|
auto pathToInclude = absPath(tildePath(tokens[1], options.home), dirOf(*options.path));
|
||||||
if (pathExists(pathToInclude)) {
|
if (pathExists(pathToInclude)) {
|
||||||
auto includeOptions = ApplyConfigOptions {
|
auto includeOptions = ApplyConfigOptions {
|
||||||
.path = pathToInclude,
|
.path = pathToInclude,
|
||||||
|
@ -437,10 +437,16 @@ template class BaseSetting<DeprecatedFeatures>;
|
||||||
|
|
||||||
static Path parsePath(const AbstractSetting & s, const std::string & str, const ApplyConfigOptions & options)
|
static Path parsePath(const AbstractSetting & s, const std::string & str, const ApplyConfigOptions & options)
|
||||||
{
|
{
|
||||||
if (str == "")
|
if (str == "") {
|
||||||
throw UsageError("setting '%s' is a path and paths cannot be empty", s.name);
|
throw UsageError("setting '%s' is a path and paths cannot be empty", s.name);
|
||||||
else
|
} else {
|
||||||
return canonPath(str);
|
auto tildeResolvedPath = tildePath(str, options.home);
|
||||||
|
if (options.path) {
|
||||||
|
return absPath(tildeResolvedPath, dirOf(*options.path));
|
||||||
|
} else {
|
||||||
|
return canonPath(tildeResolvedPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> Path PathsSetting<Path>::parse(const std::string & str, const ApplyConfigOptions & options) const
|
template<> Path PathsSetting<Path>::parse(const std::string & str, const ApplyConfigOptions & options) const
|
||||||
|
|
|
@ -117,6 +117,21 @@ Path realPath(Path const & path)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Path tildePath(Path const & path, const std::optional<Path> & home)
|
||||||
|
{
|
||||||
|
if (path.starts_with("~/")) {
|
||||||
|
if (home) {
|
||||||
|
return *home + "/" + path.substr(2);
|
||||||
|
} else {
|
||||||
|
throw UsageError("`~` path not allowed: %1%", path);
|
||||||
|
}
|
||||||
|
} else if (path.starts_with('~')) {
|
||||||
|
throw UsageError("`~` paths must start with `~/`: %1%", path);
|
||||||
|
} else {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void chmodPath(const Path & path, mode_t mode)
|
void chmodPath(const Path & path, mode_t mode)
|
||||||
{
|
{
|
||||||
if (chmod(path.c_str(), mode) == -1)
|
if (chmod(path.c_str(), mode) == -1)
|
||||||
|
|
|
@ -62,6 +62,16 @@ Path canonPath(PathView path, bool resolveSymlinks = false);
|
||||||
*/
|
*/
|
||||||
Path realPath(Path const & path);
|
Path realPath(Path const & path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a tilde path like `~/puppy.nix` into an absolute path.
|
||||||
|
*
|
||||||
|
* If `home` is given, it's substituted for `~/` at the start of the input
|
||||||
|
* `path`. Otherwise, an error is thrown.
|
||||||
|
*
|
||||||
|
* If the path starts with `~` but not `~/`, an error is thrown.
|
||||||
|
*/
|
||||||
|
Path tildePath(Path const & path, const std::optional<Path> & home = std::nullopt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the permissions of a path
|
* Change the permissions of a path
|
||||||
* Not called `chmod` as it shadows and could be confused with
|
* Not called `chmod` as it shadows and could be confused with
|
||||||
|
|
|
@ -48,7 +48,60 @@ TEST_F(PathsSettingTest, parse)
|
||||||
ASSERT_THAT(config.paths.parse("/puppy/../doggy.nix", {}), Eq<Paths>({"/doggy.nix"}));
|
ASSERT_THAT(config.paths.parse("/puppy/../doggy.nix", {}), Eq<Paths>({"/doggy.nix"}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PathsSettingTest, append) {
|
TEST_F(PathsSettingTest, parseRelative)
|
||||||
|
{
|
||||||
|
auto options = ApplyConfigOptions{.path = "/doggy/kinds/config.nix"};
|
||||||
|
auto config = mkConfig();
|
||||||
|
ASSERT_THAT(
|
||||||
|
config.paths.parse("puppy.nix", options),
|
||||||
|
Eq<Paths>({"/doggy/kinds/puppy.nix"})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Splits on whitespace:
|
||||||
|
ASSERT_THAT(
|
||||||
|
config.paths.parse("puppy.nix /doggy.nix", options), Eq<Paths>({"/doggy/kinds/puppy.nix", "/doggy.nix"})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Canonicizes paths:
|
||||||
|
ASSERT_THAT(config.paths.parse("../soft.nix", options), Eq<Paths>({"/doggy/soft.nix"}));
|
||||||
|
|
||||||
|
// Canonicizes paths:
|
||||||
|
ASSERT_THAT(config.paths.parse("./soft.nix", options), Eq<Paths>({"/doggy/kinds/soft.nix"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PathsSettingTest, parseHome)
|
||||||
|
{
|
||||||
|
auto options = ApplyConfigOptions{
|
||||||
|
.path = "/doggy/kinds/config.nix",
|
||||||
|
.home = "/home/puppy"
|
||||||
|
};
|
||||||
|
auto config = mkConfig();
|
||||||
|
|
||||||
|
ASSERT_THAT(
|
||||||
|
config.paths.parse("puppy.nix", options),
|
||||||
|
Eq<Paths>({"/doggy/kinds/puppy.nix"})
|
||||||
|
);
|
||||||
|
|
||||||
|
ASSERT_THAT(
|
||||||
|
config.paths.parse("~/.config/nix/puppy.nix", options),
|
||||||
|
Eq<Paths>({"/home/puppy/.config/nix/puppy.nix"})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Splits on whitespace:
|
||||||
|
ASSERT_THAT(
|
||||||
|
config.paths.parse("~/puppy.nix ~/doggy.nix", options),
|
||||||
|
Eq<Paths>({"/home/puppy/puppy.nix", "/home/puppy/doggy.nix"})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Canonicizes paths:
|
||||||
|
ASSERT_THAT(config.paths.parse("~/../why.nix", options), Eq<Paths>({"/home/why.nix"}));
|
||||||
|
|
||||||
|
// Home paths for other users not allowed. Needs to start with `~/`.
|
||||||
|
ASSERT_THROW(config.paths.parse("~root/config.nix", options), Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PathsSettingTest, append)
|
||||||
|
{
|
||||||
auto config = mkConfig();
|
auto config = mkConfig();
|
||||||
|
|
||||||
ASSERT_TRUE(config.paths.isAppendable());
|
ASSERT_TRUE(config.paths.isAppendable());
|
||||||
|
|
Loading…
Reference in a new issue