numtide / treefmt

one CLI to format your repo [maintainers=@zimbatm,@brianmcgee]
https://treefmt.com
MIT License
620 stars 38 forks source link

`nix fmt` does not work with symlinked `projectRoot` #221

Closed srhb closed 1 year ago

srhb commented 1 year ago

Describe the bug If the projectRoot is a symlink, nix fmt fails

[WARN]: Ignoring path ., it is not in the project root
[WARN]: Aborting, no paths to format

To Reproduce

# ~/scratch/proja/flake.nix
{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    flake-parts.url = "github:hercules-ci/flake-parts";
    treefmt-nix.url = "github:numtide/treefmt-nix";
  };

  description = "treefmt-nix broken on flakes";

  outputs = inputs @ {flake-parts, ...}:
  flake-parts.lib.mkFlake {inherit inputs;} {
      systems = ["x86_64-linux"];
      imports = [inputs.treefmt-nix.flakeModule];
      perSystem = {
        treefmt = {
          projectRootFile = "flake.nix";
          programs.alejandra.enable = true;
        };
      };
    };
}
> ln -s ~/scratch/proja ~/scratch/projb
> cd ~/scratch/proja
> nix fmt

traversed 2 files
matched 1 files to formatters
left with 0 files after cache
of whom 0 files were re-formatted
all of this in 1ms

> cd ~/scratch/projb
> nix fmt
[WARN]: Ignoring path ., it is not in the project root
[WARN]: Aborting, no paths to format
zimbatm commented 1 year ago

It's using test -f to check the existence of the file over here: https://github.com/numtide/treefmt-nix/blob/819dd7f076832838bba238eceef9a3dbfc63f5d0/module-options.nix#L105

I believe test -f returns 0 if the file is a symlink that points to a file, at least that's what I tested locally.

Can you run test -f flake.nix; echo $?

zimbatm commented 1 year ago

Another option that I use sometimes is to set projectRoot = ".git/config";.

srhb commented 1 year ago

Hmm, no, but I guess the problem happens in treefmt itself. Here's a an overridden treefmt-nix:

❯ git diff
diff --git a/module-options.nix b/module-options.nix
index a4fab90..589fe78 100644
--- a/module-options.nix
+++ b/module-options.nix
@@ -99,6 +99,7 @@ in
         default =
           let
             x = pkgs.writeShellScriptBin "treefmt" ''
+              set -x
               find_up() {
                 ancestors=()
                 while true; do

And its output (different path, but still a symlink to a directory)

++ find_up flake.nix
++ ancestors=()
++ true
++ [[ -f flake.nix ]]
++ echo /home/sarah/pex
++ exit 0
+ tree_root=/home/sarah/pex
+ exec /nix/store/zfhsh797srxf3ppar5yv1ywbkn01pm2d-treefmt-0.5.0/bin/treefmt --config-file /nix/store/ldvwxlczhfhrjf3zswqfsbyzs6qg2jz8-treefmt.toml . --tree-root /home/sarah/pex
[WARN]: Ignoring path ., it is not in the project root
[WARN]: Aborting, no paths to format
srhb commented 1 year ago

Ah yes, this looks broken. I'll move this issue later.

https://github.com/numtide/treefmt/blob/main/src/engine.rs#L59-L63

zimbatm commented 1 year ago

symlinks are a nightmare :sweat:

zimbatm commented 1 year ago

I bet that https://github.com/numtide/treefmt/blob/fbc27b5eb8986ec95207fb25245d1c14785eb020/src/lib.rs#L58 is resolving the symlink

srhb commented 1 year ago

Ah, you moved it! Excellent, thanks :pray:

srhb commented 1 year ago

Maybe the correct fix is actually to dereference everywhere rather than trying to preserve the symlink. I think the problem stems from env::current_dir() dereferencing (which is exactly what getcwd() would do as well).

The fact that this behaviour is being fought is making it non-trivial to fix this in one location. I also don't think there's a portable way to fake a version that returns the symlink.

srhb commented 1 year ago

... or to not use absolute paths :grin:

Edit: Jokes aside, this seems very portable. I understand the need for some safety stops, and maybe they should resolve each path to an absolute since determining the meaning of "in the project root" is really hard otherwise.

zimbatm commented 1 year ago

Potentially the issue is not the projectRoot, but the fact that there is a symlink that points outside of the project root. We have protection in place so that the user doesn't inadvertently start formatting files outside of their repos.

Can you try setting global.excludes = [ "flake.nix" ];?

srhb commented 1 year ago

Where do you want that, sorry? Also I don't think that's the case. The problem is wrt. resolving project_root vs work_dir vs paths, I think.

https://github.com/srhb/treefmt-symlink-broken

~/scratch/treefmt-symlink-broken main
❯ cd deref

~/scratch/treefmt-symlink-broken/deref main
❯ nix fmt -- -v .
[DEBUG]: tree_root=/home/sarah/scratch/treefmt-symlink-broken/deref work_dir=/home/sarah/scratch/treefmt-symlink-broken/deref cache_dir=/home/sarah/.cache/treefmt/eval-cache config_file=/nix/store/b4wf3xrrxkpdrnr3gahhwy8f3r8paxw1-treefmt.toml paths=["."]
[DEBUG]: load config: 96.83µs (Δ 96.68µs)
[DEBUG]: Found /nix/store/0sfqggpd9jyp912j7wfy11kb1py926si-alejandra-3.0.0/bin/alejandra at /nix/store/0sfqggpd9jyp912j7wfy11kb1py926si-alejandra-3.0.0/bin/alejandra
[DEBUG]: built glob set; 0 literals, 0 basenames, 0 extensions, 0 prefixes, 0 suffixes, 1 required extensions, 0 regexes
[DEBUG]: load formatters: 389.41µs (Δ 292.58µs)
[DEBUG]: cache: loading from /home/sarah/.cache/treefmt/eval-cache/8550e61d07429a0b9b3b8129cdef69a026cbf75b.toml
[DEBUG]: load cache: 551.96µs (Δ 162.55µs)
[DEBUG]: tree walk: 925.33µs (Δ 373.37µs)
[DEBUG]: filter_matches: 937.95µs (Δ 12.57µs)
[DEBUG]: running "/nix/store/0sfqggpd9jyp912j7wfy11kb1py926si-alejandra-3.0.0/bin/alejandra" "/home/sarah/scratch/treefmt-symlink-broken/deref/flake.nix"
[INFO]: #alejandra: 1 files processed in 1.85ms
[DEBUG]: format: 3.31ms (Δ 2.37ms)
[DEBUG]: cache: writing to /home/sarah/.cache/treefmt/eval-cache/8550e61d07429a0b9b3b8129cdef69a026cbf75b.toml
[DEBUG]: write cache: 3.37ms (Δ 61.39µs)

traversed 2 files
matched 1 files to formatters
left with 1 files after cache
of whom 1 files were re-formatted
all of this in 3ms

~/scratch/treefmt-symlink-broken/deref main*
❯ git checkout .
Updated 1 path from the index

~/scratch/treefmt-symlink-broken/deref main
❯ cd ../symlink

~/scratch/treefmt-symlink-broken/symlink main
❯ nix fmt -- -v .
[DEBUG]: tree_root=/home/sarah/scratch/treefmt-symlink-broken/symlink work_dir=/home/sarah/scratch/treefmt-symlink-broken/deref cache_dir=/home/sarah/.cache/treefmt/eval-cache config_file=/nix/store/b4wf3xrrxkpdrnr3gahhwy8f3r8paxw1-treefmt.toml paths=["."]
[WARN]: Ignoring path ., it is not in the project root
[WARN]: Aborting, no paths to format
soupglasses commented 1 year ago

Must say i just bumped into this myself. And it does indeed seem related to symlinks for me as well.

% pwd
/home/sofi/Code/Github/imsofi/phenix
% pwd -P
/mnt/home/sofi/Code/Github/imsofi/phenix
% 

It also comes into treefmt-nix:

% nix fmt
[WARN]: Ignoring path ., it is not in the project root
[WARN]: Aborting, no paths to format
zimbatm commented 1 year ago

fixed by #252, it's also part of the 0.6.0 release!