Visitar URL original
libflakes: Add repo-url detection to absolute paths (#5836) by syborg64 · Pull Request #16789 · NixOS/nix · GitHub
Skip to content

libflakes: Add repo-url detection to absolute paths (#5836) - #16789

Open
syborg64 wants to merge 1 commit into
NixOS:masterfrom
syborg64:master
Open

syborg64 wants to merge 1 commit into
NixOS:masterfrom
syborg64:master

Conversation

@syborg64

@syborg64 syborg64 commented Nov 9, 2026 •

Copy link
Copy Markdown

Giving a filepath to CLI as a flake url would correctly infer git+path only within an expression (with a baseDir set).
This adds the same functionality to bare absolute paths

Motivation

doing nix registry add myFlake /path/to/flake has unexpected behavior when the flake is part of a git repository.
It converts the file path to a path:// url, regardless of if the flake is part of a repository. It does not infer git+file:// as would be expected.
This is especially problematic because this distinction changes the semantics of src = ./.

This aligns such CLI uses with expression evaluation, where the url scheme is inferred based on the contents/presence of a repository

Implementation

When a 'baseDir' is set, there is already an implementation in place.
A section of that code is copied into the alternate case where no such baseDir is set

This makes behavior more consistent.

Considerations:

  • when resolving the path, the actual filesystem path is checked with repo fetchers

    • this means it can fail for permissions (but not nonexistence)
    • unit tests were using /foo/bar which is not guaranteed to be absent, accessible, nor empty of a contained repo
    • unit tests were changed to use the _NIX_TEST_UNIT_DATA directory to address this
    • unit-testing the detection of the repo would be complicated, in part because nested .git directories are forbidden
  • the case where baseDir is set also has other features. Enabling them for the other case would be a larger departure from current behavior but could be valuable. To taste.

    • it checks that the path exists and resolves to a directory, (or)
    • it converts paths that contain the flake.nix filename to the bare directory and emits a warning
    • it may walk up the tree to find the directory containing flake.nix
    • it walks up the tree to find the root of the repository containing the flake

Closes: #5836


Add 👍 to pull requests you find important.

The Nix maintainer team uses a GitHub project board to schedule and track reviews.

@syborg64
syborg64 requested a review from edolstra as a code owner November 9, 2026 09:46
@syborg64
syborg64 force-pushed the master branch 3 times, most recently from c014f07 to 330cdd8 Compare November 12, 2026 13:41
@Filippo-Galli

Copy link
Copy Markdown
Contributor

Hi, thanks for contributing!

Taking a look at your PR, there is a divergence between the behavior inside if (baseDir) and the proposed else branch: specifically, subdirectory handling within Git repositories.

The proposed patch calls fetchers::getLocalRepoURL(path) directly, which only works if path is the root of the repository containing .git. If the flake resides in a subdirectory of a repository (e.g. /path/to/repo/packages/flake), getLocalRepoURL will return
std::nullopt, failing to infer the git+file: scheme and missing the ?dir= parameter.

Instead of duplicating logic in the else branch, it might be cleaner to refactor parsePathFlakeRefWithFragment to decouple path normalization from flake/VCS detection:

    std::filesystem::path path = match[1].str();
    auto query = decodeQuery(match[3].str(), /*lenient=*/true);
    auto fragment = percentDecode(match[5].str());

    // Path normalization
    if (baseDir) {
        path = absPath(path, get(baseDir), true);
    } else {
        if (!preserveRelativePaths && !path.is_absolute())
            throw BadURL("flake reference '%s' is not an absolute path", url);
    }
    
    if (isFlake && path.is_absolute() && pathExists(path)) {
        // Existing logic: directory checks, searching up for flake.nix, walking up to find repo root and setting `subdir`...
    }
    
    // Fallback to path: scheme
    return fromParsedURL({ .scheme = "path", ... }, isFlake);

This would:

  1. Avoid code duplication.
  2. Ensure subdirectories in Git repos work consistently regardless of whether baseDir was provided.
  3. Preserve compatibility with synthetic paths in unit tests (via pathExists(path)).

What do you think?

@syborg64

Copy link
Copy Markdown
Author

Thank you for the guidance!

I didn't understand that subtlety of the subdirectory mechanism and favored minimal change.

I really like your approach, I'll be amending my pull request

@syborg64

Copy link
Copy Markdown
Author

pathExists can fail and throw with permissionDenied (EACCES)
one would think that pathAccessible would handle this, but its implementation only swallows EPERM

This happens for examples if /foo/bar is not executable(traversable)

This means unit tests can still be made to fail based on the system.
I believe I should propose a fix to this in a separate PR


The unit tests expect /foo/bar etc not to be parsed as git+file:// but this also depends on the system. This false-negative can be triggered by creating /foo/bar/.git

Disabling this in code would require the tests to explicitly opt out of repo detection, which is (to me) out of scope

path urls had detection for repos and the flake root,
but only when a baseDir was set.
These detections are now attempted regardless of baseDir

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flakes using paths do not infer git+file: despite documentation to that effect

2 participants