thefuck icon indicating copy to clipboard operation
thefuck copied to clipboard

feat: new rule for `nix-shell`

Open thenbe opened this issue 1 year ago • 17 comments

Implementation is similar to the one explained in https://github.com/nvbn/thefuck/issues/912#issue-441679613.

In a nutshell, it tries to wrap the user's failed command in a nix-shell call.

$ ponysay moo
The program 'ponysay' is not in your PATH. You can make it available in an
ephemeral shell by typing:
  nix-shell -p ponysay

$ fuck
nix-shell -p ponysay --run "ponysay moo" [enter/↑/↓/ctrl+c]

Further info on nix-shell: https://thiagowfx.github.io/2022/02/nix-shell-in-a-nutshell/#hello-world-classic

thenbe avatar Jul 28 '23 19:07 thenbe

Been loving this. Hope it gets reviewed and upstreamed for all. Thanks for making this!

kdb424 avatar Oct 14 '23 08:10 kdb424

I've just discovered thefuck and I can't imagine how I didn't stumble into it earlier.

This PR would fit like a glove for me that just switched to nixos and haven't yet grown the muscle memory of typing the nix-shell whenever my command fails.

wesleycoder avatar Dec 21 '23 02:12 wesleycoder

i would love to see this merged as well

KiaraGrouwstra avatar Feb 12 '24 18:02 KiaraGrouwstra

looks like you can use this already using e.g. an overlay, altho i had a bit of trouble getting it to work out of the box. specifically, without adding doCheck = false;, i would run into this error:

error: builder for '/nix/store/rl44gb6qd4x2myclj9i8cpkfrvw6ysqa-thefuck-3.32.drv' failed with exit code 2;
       last 10 log lines:
       > thefuck/system/unix.py:6
       >   /build/source/thefuck/system/unix.py:6: DeprecationWarning: The distutils package is deprecated and slated for removal in Python 3.12. Use setuptools or check PEP 632 for potential alternatives
       >     from distutils.spawn import find_executable
       >
       > -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
       > =========================== short test summary info ============================
       > ERROR  - ModuleNotFoundError: No module named 'pytest_docker_pexpect'
       > !!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
       > ========================= 1 warning, 1 error in 0.09s ==========================
       > /nix/store/bknngadwym46j65qs14ic2w79rpav888-stdenv-linux/setup: line 1582: pop_var_context: head of shell_variables not a function context

i had tried removing the added test, altho that appeared not to resolve the issue.

KiaraGrouwstra avatar Feb 13 '24 14:02 KiaraGrouwstra

it would seem cool to similarly get an approach using nix run, i.e. go from suggesting nix-shell -p ponysay --run "ponysay moo" to nix run nixpkgs#ponysay -- moo - this might eventually help extend beyond just nixpkgs.

edit: https://github.com/KiaraGrouwstra/thefuck/commit/81d6786c80b86f2cc80b3ea90adc214df8266643

KiaraGrouwstra avatar Feb 23 '24 11:02 KiaraGrouwstra

I've been using a custom rule that supports the new unified CLI for a while, and was planning on opening a PR once this one has been merged (I hesitate to update this current PR as it's already tested and ready to be merged). I don't know if that will happen soon, so in the meantime I've pushed the changes to this new branch instead, which builds on this here PR. You can use the updated rule by adding it as a custom rule to your config.

In the new rule, three variants are suggested. Assuming I run cowsay hello world, I am presented with the following:

  1. nix run nixpkgs#cowsay -- hello world: This runs my command in a non-interactive shell. Uses the nix unified CLI.
  2. nix shell nixpkgs#cowsay: This enters an interactive shell with cowsay available, but does not run any command. This is useful if you'd rather run the command yourself after entering the shell because your command requires delicate massaging (e.g. running it with sudo, prefixing it with environment variable, juggling quote variants, etc).
  3. nix-shell -p cowsay --run "cowsay hello world". This runs my command in a non-interactive shell. Uses the nix original CLI.
  4. nix shell nixpkgs#cowsay --command cowsay hello world: Very similar to the first one so I've personally disabled this one.

Thoughts on future updates:

  • It'd be nice if there was a variant that runs my command and then keeps me in the shell.
    • For the original CLI, we can add a --command "echo hello; return" to our nix-shell invocation.
    • For the unified CLI: not sure yet, we might need to do something like this example from the docs: nix shell nixpkgs#gnumake --command sh -c "cd src && make"
  • We should expose a couple of flags for users to configure this.
    • disable_unified_cli (boolean)
    • disable_original_cli (boolean)
  • As far as I can tell, the command-not-found db doesn't really play nice if you use flakes to configure your system and might return stale results (unless you update it manually?). nix-index seems to be the go-to alternative. It'd be great if we could optionally use that instead (perhaps behind a flag enable_nix_index for users who have installed nix-index (programs.nix-index.enable = true; in home manager).

thenbe avatar Feb 23 '24 15:02 thenbe

@thenbe i agree integrating with nix-index's command-not-found replacement seems cool, as a flake user. i kinda wish we could have command-not-found (and this thefuck integration) extend to flake inputs beyond nixpkgs as well, such as to packages from NUR for example. preferably this should be dynamic based on your inputs rather than hardcoded to specific ones like nixpkgs, or NUR for that matter. i'll admit i haven't really figured out how that might work tho.

KiaraGrouwstra avatar Feb 23 '24 21:02 KiaraGrouwstra

just tried these with a command like program_i_have | program_i_dont_have, seems that may complicate the suggestions a bit

KiaraGrouwstra avatar Feb 24 '24 01:02 KiaraGrouwstra

I'm not sure if thefuck can handle piping.

If I make a typo git statis it will correct me to git status. But if I do echo hello | git statis it does not correct my typo. thefuck seems to work mostly on single commands AFAICT.

thenbe avatar Feb 24 '24 01:02 thenbe

@thenbe hm, i'm not sure.

fortune | cowsay
The program 'cowsay' is not in your PATH. It is provided by several packages.
You can make it available in an ephemeral shell by typing one of the following:
  nix-shell -p cowsay
  nix-shell -p neo-cowsay
$ fuck
nix run nixpkgs#fortune | cowsay

feels like it knows about the whole command given it's reproducing it?

KiaraGrouwstra avatar Feb 24 '24 03:02 KiaraGrouwstra

another common nix thing we might be able to address from thefuck would be errors about packages being unfree

edit: https://github.com/KiaraGrouwstra/thefuck/commit/16d838bf6f63117b161a2f1e6572e06108b007eb

KiaraGrouwstra avatar Feb 24 '24 20:02 KiaraGrouwstra

@thenbe what was the argument to favor nix run over nix shell again? i guess the latter seems a bit more generic in case of handling non-standard binaries at least

KiaraGrouwstra avatar Feb 24 '24 20:02 KiaraGrouwstra

If I'm only looking to execute a program (and don't need to be dropped into a shell) then I prefer nix run over nix shell as the documentation suggests nix run specifically for this use case.

I also recall nix run being more performant (perhaps because we forego the overhead of launching a shell?). This last point is not derived from benchmarks, only anecdotal evidence.

i guess the latter seems a bit more generic in case of handling non-standard binaries at least

I've added this variant (the 4th one in my previous post), but disabled it after a while when I realized that I never reach for it. Do you find that you still need it over nix run (the 1st variant in my previous post)?

thenbe avatar Feb 25 '24 03:02 thenbe

another common nix thing we might be able to address from thefuck would be errors about packages being unfree

edit: KiaraGrouwstra@16d838b

This would be useful. Does it still complain about the --impure flag? Or do you use a workaround for that?

thenbe avatar Feb 25 '24 03:02 thenbe

i've been using thefuck mostly thru its zsh plugin, which just gets you the top suggestion. i found that failed for me for e.g. poppler_utils, which bundles multiple binaries. to be fair tho, i'm not sure that accounts for a large portion of its invocations, so maybe it could make sense to just actually type out fuck in those cases.

what was the --impure error? i'm not sure i'd run into that.

by the way, had you managed to also package your branch for nix? considering i seemed to need that doCheck = false; to get our branches to build thru nix.

KiaraGrouwstra avatar Feb 25 '24 11:02 KiaraGrouwstra

I just have it aliased to f for extra convenience.

I opted not to package it for nix separately since fuck already exposes a method for easily adding custom rules. Instead, I placed the rule in ~/mydotfiles/thefuck/rules/nix-shell.py then told home-manager to symlink it to the appropriate place in .config:

# home.nix
home.file.".config/thefuck/rules/nix-shell.py".source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/mydotfiles/thefuck/rules/nix-shell.py";

This way I don't need to rebuild every time I tweak the rule.

what was the --impure error?

The unified CLI commands (nix shell, nix run, etc) will not acknowledge environment variables unless the --impure flag is used.

output
$ NIXPKGS_ALLOW_UNFREE=1 nix shell nixpkgs#github-copilot-cli

error:
       … in the condition of the assert statement

         at /nix/store/xwc3zfc544jg6zhr0wi6k8253s7mwlhi-source/lib/customisation.nix:267:17:

          266|     in commonAttrs // {
          267|       drvPath = assert condition; drv.drvPath;
             |                 ^
          268|       outPath = assert condition; drv.outPath;

       … while evaluating the attribute 'handled'

         at /nix/store/xwc3zfc544jg6zhr0wi6k8253s7mwlhi-source/pkgs/stdenv/generic/check-meta.nix:490:7:

          489|       # or, alternatively, just output a warning message.
          490|       handled =
             |       ^
          491|         (

       (stack trace truncated; use '--show-trace' to show the full trace)

       error: Package ‘github-copilot-cli-0.1.36’ in /nix/store/xwc3zfc544jg6zhr0wi6k8253s7mwlhi-source/pkgs/tools/misc/github-copilot-cli/default.nix:21 has
 an unfree license (‘unfree’), refusing to evaluate.

       a) To temporarily allow unfree packages, you can use an environment variable
          for a single invocation of the nix tools.

            $ export NIXPKGS_ALLOW_UNFREE=1

          Note: When using `nix shell`, `nix build`, `nix develop`, etc with a flake,
                then pass `--impure` in order to allow use of environment variables.

       b) For `nixos-rebuild` you can set
         { nixpkgs.config.allowUnfree = true; }
       in configuration.nix to override this.

       Alternatively you can configure a predicate to allow specific packages:
         { nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
             "github-copilot-cli-0.1.36"
           ];
         }

       c) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add
         { allowUnfree = true; }
       to ~/.config/nixpkgs/config.nix.


# it wants this instead:
$ NIXPKGS_ALLOW_UNFREE=1 nix shell nixpkgs#github-copilot-cli --impure

thenbe avatar Feb 25 '24 16:02 thenbe

aah i see! i'd yet to take that into account. 🙈

specifying the rules rather than doing overlays makes sense - thanks!

KiaraGrouwstra avatar Feb 25 '24 20:02 KiaraGrouwstra