treefmt icon indicating copy to clipboard operation
treefmt copied to clipboard

Autosourcing commands from `nix-index` & `nix-index-databse`

Open blaggacao opened this issue 2 years ago • 7 comments

Is your feature request related to a problem? Please describe.

Treefmt is a great tool, but it doesn't take away the burdon of sourcing the used commands.

However, in UX categories, that would be in the ballpark of 50% of additional value (see Devenv, Fleek, Bob, etc).

treefmt-nix doesn't cut the deal, because it requires mental overhead, is not dead simple and interferes with my nix-lib project intent which is not commonly about boilerplate tooling or potentially not a nix project at all.

Describe the solution you'd like

  1. Setup and maintenance of CI should be as simple as:

      - name: Install nix
        uses: nixbuild/nix-quick-install-action@v22
      - name: Check formatting
        run: nix run nixpkgs#treefmt -- . --fail-on-change --no-cache

(non-pinning related issues with the tooling formatting are a rare "black-swan" in the horizons of this lifecycle, so we accept them in turn for the 50% additional UX)

  1. Set up and usage of treefmt should be as easy as:
  • add treefmt.toml
  • run treefmt
    • for Nix users which nix, this means feature-flagged (on which nix) profit
    • for no-nix users, this means no deterioration of their current expectations and a pretext to sh <(curl -L https://nixos.org/nix/install), which might be a faster resolution of their problem at hand than any alternative course of action.

Describe alternatives you've considered

treefmt-nix, manual management. It doesn't cut the deal, because it doesn't keep out of my way.

Additional context

I want to do a PR / contribution. Would it be accepted?

https://github.com/Mic92/nix-index-database/issues/44

In Github Actions a hypothetical and arbitrarily pinned nix/nixpkgs could speed up time-to-first-feedback some bits on style checks as it would already have nixpkgs downloaded` and - alas - eval cached.

Potentially, this implementation could attempt to fetch static builds where available to improve the situation further.

blaggacao avatar Apr 15 '23 16:04 blaggacao

👎🏾 This sounds like a complex solution to a simple problem. each nix-index query takes up around 100ms + nix evaluation of the package to retrieve the attribute. nix-index-database might be not in sync with the nixpkgs channel of the user and it's not available for all architectures (only those built on hydra). Also naive usage without sorting might turn into funny results. I just picked the two random samples from the treefmt list and it resulted in this:

$ nix-locate bin/ocamlformat                                                                                                                                                                            
ocamlformat_0_20_0.out                       25,253,440 x /nix/store/hi3bf2l6byshkgh383v0z30gylv0xncz-ocaml4.14.1-ocamlformat-0.20.0/bin/ocamlformat                                                       
ocamlformat_0_22_4.out                       24,745,384 x /nix/store/7h33cv3zxncci59xcjx6g5bwizccgmmi-ocaml4.14.1-ocamlformat-0.22.4/bin/ocamlformat                                                       
ocamlformat_0_22_4.out                       24,812,896 x /nix/store/7h33cv3zxncci59xcjx6g5bwizccgmmi-ocaml4.14.1-ocamlformat-0.22.4/bin/ocamlformat-rpc                                                   
ocamlformat_0_22_4.out                       11,462,152 x /nix/store/7h33cv3zxncci59xcjx6g5bwizccgmmi-ocaml4.14.1-ocamlformat-0.22.4/bin/ocamlformat.parser_recovery.test_driver                           
ocamlformat_0_22_4.out                          935,576 x /nix/store/7h33cv3zxncci59xcjx6g5bwizccgmmi-ocaml4.14.1-ocamlformat-0.22.4/bin/ocamlformat.parser_recovery.test_gen                              
ocamlformat_0_23_0.out                       24,725,664 x /nix/store/8hxnk172dvcr1vpahhavf2l3r2gp2iyx-ocaml4.14.1-ocamlformat-0.23.0/bin/ocamlformat                                                       
ocamlformat_0_23_0.out                       24,789,080 x /nix/store/8hxnk172dvcr1vpahhavf2l3r2gp2iyx-ocaml4.14.1-ocamlformat-0.23.0/bin/ocamlformat-rpc                                                   
ocamlformat_0_23_0.out                       11,480,000 x /nix/store/8hxnk172dvcr1vpahhavf2l3r2gp2iyx-ocaml4.14.1-ocamlformat-0.23.0/bin/ocamlformat.parser_recovery.test_driver                           
ocamlformat_0_23_0.out                          935,576 x /nix/store/8hxnk172dvcr1vpahhavf2l3r2gp2iyx-ocaml4.14.1-ocamlformat-0.23.0/bin/ocamlformat.parser_recovery.test_gen                              
ocamlformat.out                              24,735,976 x /nix/store/gpv0cydm7bv8j7yfz94g7zqchayzpy8l-ocaml4.14.1-ocamlformat-0.24.1/bin/ocamlformat                                                       
ocamlformat.out                              24,799,392 x /nix/store/gpv0cydm7bv8j7yfz94g7zqchayzpy8l-ocaml4.14.1-ocamlformat-0.24.1/bin/ocamlformat-rpc                                                   
ocamlformat.out                              11,480,000 x /nix/store/gpv0cydm7bv8j7yfz94g7zqchayzpy8l-ocaml4.14.1-ocamlformat-0.24.1/bin/ocamlformat.parser_recovery.test_driver                           
ocamlformat.out                                 935,576 x /nix/store/gpv0cydm7bv8j7yfz94g7zqchayzpy8l-ocaml4.14.1-ocamlformat-0.24.1/bin/ocamlformat.parser_recovery.test_gen                              
ocamlformat_0_19_0.out                       23,296,544 x /nix/store/3nsvhijvxll5bf2q27nx4yn74acdfm6i-ocaml4.14.1-ocamlformat-0.19.0/bin/ocamlformat                                                       
ocamlformat_0_20_1.out                       25,266,024 x /nix/store/hlxkf658gb42y4m7i7dc5m62xsplnm72-ocaml4.14.1-ocamlformat-0.20.1/bin/ocamlformat                                                       
ocamlformat_0_21_0.out                       25,338,400 x /nix/store/0lfla9xg9qnxn8g66zpgbr50f535kys9-ocaml4.14.1-ocamlformat-0.21.0/bin/ocamlformat
                                                                                                                                                                                                         
$ nix-locate bin/hlint                                                                                                                                                                                  
ihaskell.out                                        293 x /nix/store/5q85ynqif5i9if3fw7ci6gw241iar8x4-ihaskell-with-packages/bin/hlint                                                                     
(ihaskell.out)                                        0 s /nix/store/6pwqjhsfrzaz3krr5k49954s5628076d-ghc-9.2.7-with-packages/bin/hlint                                                                    
hlint.out                                    22,306,464 x /nix/store/26s0yailyl0284ky95mz4j39z2g0vh6b-hlint-3.4.1/bin/hlint                                                                                
haskellPackages.hlint.out                    22,306,464 x /nix/store/aifqw524819445nzsxi2grn1l7ynv8kc-hlint-3.4.1/bin/hlint                                                                                
haskellPackages.hlint-test.out                  934,400 x /nix/store/1smkch8jfcd51cl31lxr7sxcr0il57x8-hlint-test-0.1.0.0/bin/hlint-test                                                                    

Every by default the tool would have chosen ocamlformat_0_20_0 and ihaskell, which would be odd choices.

Mic92 avatar Apr 16 '23 05:04 Mic92

I appreciate we may find ourselves in a design process in search of the best design of a solution to the problem statement (UX - centered).

I'm not sure, though, how you'd address the problem statement? Maybe we already got carried away on the implementation details?

How do other great UX-optimized tools provide good enough heuristics?

blaggacao avatar Apr 16 '23 14:04 blaggacao

the burdon of sourcing the used commands.

Have you tried JRMurr/direnv-nix-action which bundles cachix/install-nix-action and direnv?

So assuming you have a use_flake in your .envrc you can:

      - name: "Setup env"
        uses: JRMurr/direnv-nix-action@v2
      
      - run: treefmt

srounce avatar Apr 16 '23 19:04 srounce

That is a prohibitively solution.

Let me explain:

  • The key CI metric is cycle time
  • On chores like fmt checks, it typically is Time-To-First feedback
  • Now suppose you lock your dependencies (instead of using a docker image that pins nixpkgs and even holds an eval cache), you need to:
    • download nixpkgs (~2s)
    • eval the cache (~13s)
    • download typically non-static binaries; that isn't efficient at all
  • Now, you "just" use direnv, and voila, you've got your Time-To-First feedback coupled with the inital (uncached) loading of the env
  • Ad an expensive IFD such as haskell.nix and you're waiting long long minutes.
  • At this point, your Nix based solution simply doesn't meet the productivity requirement of your team.

Instead, this proposal holds:

  • Pin by environment, which:
    • leverages baked in nixpkgs and its eval cache (-10s)
  • Use static build
    • matches against static builds (which are potentially instant to fetch) (-45s)
  • JIT and demand-oriented sourcing
    • only sources JIT what's needed (-30min - depending on the env)

I think it's an obvious design choice, when you we want to increase the user acceptance for treefmt (so that we can use the tools we love, when we're not king).

There will be controversy on the heuristics and my preferred option would be to have a community maintained matcher tool for CLI purposes so that we can have the discussion once and not every new tool has to roll with their own solution.

As @Mic92 suggested the initially considered backend might not meet the requirements.

blaggacao avatar Apr 17 '23 05:04 blaggacao

As a Nix user, I expect to be able to pin the versions of my software, and that treefmt would use those same versions. I don't really see how this can be achieved without evaluating the nix code?

Let's say that there is a Nix evaluation snapshot that is possible, it seems to me that this is a generic tool that can be solved outside of treefmt (it would be cool if it existed!). Something akin to Nix binary cache but for evaluation outputs.

One part of the speed issue is that devshells tend to include a lot of unrelated tools that get downloaded. This is why treefmt-nix exists, then you can nix fmt and it will only download the related tools. I'm not sure why editing a treefmt.nix is more complicated than editing a treefmt.toml?

In the same design space, it would also be nice if devshells supported lazy loading.

Another part is that CI runners don't maintain a local /nix/store cache between runs. For this, we have srvos github actions runners that you can self host, or use Hercules CI or Garnix.

zimbatm avatar Apr 28 '23 11:04 zimbatm

As a Nix user, I expect to be able to pin the versions of my software, and that treefmt would use those same versions. I don't really see how this can be achieved without evaluating the nix code?

As everyone else, ... ... multilevel UX problems may be blocking for adoption. Including one of them being the verbosity of Nix.

That person is not me, and I'm assuming the role of acting attorney for people I've interacted with around this topic, but who stay below the threshold to care (about treefmt).

Let's say that there is a Nix evaluation snapshot that is possible, it seems to me that this is a generic tool that can be solved outside of treefmt.

In my mind the simplest potential implementation is nixos/nix:latest-with-eval-cache (docker). Though this, indeed, is orthogonal.


What treefmt can offer is to apply a heuristic command resolution as opt-in service to the non-Nix user persona.

Very oft they don't share at all the same set of values and goals with the Nix persona. That must always be kept in mind.

Heuristic command resolutio eventually may be provided as a Nix ecosystem capability in the form of a library. Treefmt could adopt it, and so could other tools (bob, fleek, etc) converge to shared maintenance.

How thet ultimately choose to feature flag such capability is completely secondary, though feature flagging on the presence of nix seems innocuous and handy (when dealing with non-nix config data).

Another part is that CI runners don't maintain a local /nix/store cache

Outside of Nix, static builds have gained popularity to minimize the impact of missing caches.

This would be a workable solution, here, too.


To clarify my stance, I do acknowledge, than within the Nix Ecosystem, we have different solutions to those problems.

But that isn't really the point here.

We already establish above that goals and values don't necessarily align between Nix and non-Nix user persona.

The logical consequence of this assessment is that giving the non-Nix persona a choice between "making those solution a precondition" or "suffering UX" is a hinderance to (viral) adoption of treefmt.

If treefmt can be considered an entry level drug (I think it can), it may not realize its full potential to help drive adoption of Nix itself.

blaggacao avatar Apr 28 '23 12:04 blaggacao

A draft component-diagram-ish of a mapper's principle. Meant to be design context, not a proposed solution.

blaggacao avatar Apr 29 '23 13:04 blaggacao