-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fish configuration snippets are read prematurely because they are sourced from $__fish_datadir/config.fish #3918
Comments
Nix is using the wrong file. /etc/fish/config.fish, like anything in /etc, is supposed to belong to the administrator. There's a variety of things that nix can do, among them
@Profpatsch: Care to comment on this? Any reason you're using $__fish_sysconfdir/config.fish here? The reason I think this should be changed in Nix is that we want this sourcing order. We want config.fish to override snippets. |
Just to be clear since my previous comment could be read as a bit grumpy: I'm happy to help Nix here, but since I don't use it, I'm going to need to have my hand held a bit. Specifically, I'm going to need information on why the way we've envisioned this (__fish_build_paths.fish) isn't working (if it isn't, otherwise please just use that and then everyone's happy). Then I'm going to need information on what you need. What I won't do is change the file precedence, since I strongly believe that what we have here is the right way of doing it. We read /usr/share/fish/config.fish first because that's an internal fish thing that just happens to be named "config". Then we read the snippets and then the administrator and user files so that both the administrator and user can override whatever the snippets (which could be included in packages or downloaded with OMF or fisherman or something) decide. @therealpxc: You've stated in #3099 (comment):
Implementing this in C++ wouldn't fix this since we'd want the same order. And also
Well, it is. But that would also apply to fish. And both are non-standard in interesting ways, which means I'm happy to help. You don't just demand shebang lines use We've added stuff to help Haiku, so we can also add stuff to help Nix. But for that we need to know what to add. |
Great! I'm appreciate your help and I look forward to a little back and forth about this. :-) So there are two places where Nix/NixOS deals with fish: one is a module, which is an operating-system level configuration tool, and the other is simply a package for fish. In NixOS, the module is activated by the system administrator when they choose to add This is also the only way to modify /etc/fish/config.fish in NixOS; in NixOS all of /etc are symlinks into read-only files in the Does that explanation of why /etc/fish/config.fish is involved make sense? |
So long as we end up with fish working right on NixOS, I'm of course happy with whatever load order is appropriate for fish on its own terms, so to speak. But I am curious about why the mode of overriding snippets previously described in the documentation (snippets of the same names override each other according to precedence which prefers user snippets to admin snippets and admin snippets to vendor snippets, if I read it correctly) is inadequate for allowing appropriate overriding. Is it just that doing so is trickier, because you have to figure out which snippet is causing what and then write a snippet of the same name? I can understand wanting to avoid that. |
So if I'd install Nix I'd pick "the fish module" and "the KDE module" and "the firefox module" to end up with fish as my shell, KDE as my desktop and firefox as my browser?
So in that sense it's a configuration "wizard"? And if I understand Nix correctly this all ends up as part of the package, in the exact state the user picked?
Yes, it appears like it.
That actually still happens. Only we distinguish between "snippets" (which are files in the "conf.d" directories) and config.fish. All the config.fish should always be read (since you want those to be additive), but snippets have the precedence thing. So the way it currently works:
The path to /usr/share/fish/config.fish is compiled in (as $__fish_datadir) and can be picked via ./configure. The path to the "vendor" snippets is read from $__fish_datadir/__fish_build_paths.fish, which is built from share/__fish_build_paths.fish.in via ./configure. This is also a full-fledged fish script that is simply So, with that out of the way, here's what you want, from my understanding:
and
but:
and
Is that correct? Thinking about this, __fish_build_paths.fish would probably work for you (though we should read it earlier). I wouldn't recommend a custom snippet (because ordering is annoying there). However: How are you doing this for other shells? We already read a variety of more-or-less system-specific configuration files (like systemd's locale.conf or OSX's /etc/paths), so we could also add one more. What we would need for such a file though would be a guarantee that it sticks to a small subset of POSIX-sh, like a simple Am I making any sense? |
Yes, I think we're getting somewhere. :-)
More or less. So Nix/NixOS kind of operates on two levels: one is a package manager (called Nix), which can be used atop other distros. The other level is NixOS, which is a whole distribution based upon the Nix package manager and other tools built around Nix-the-language, which is also used by the package manager which shares its name (the naming thing is kind of terrible, I know). So on my system, the relevant lines for those three things (all of which I use) are
When the operating system configuration is realized (happens when the user runs If I wanted to install fish without using it as a login shell, I could either include it in the system configuration ( So the module system and the package system are distinct but build upon one another. Adding stuff to vendor_conf.d would mean changing the initialization behavior at the package level instead of the module level, which is not comparable to the way we initialize any other shells. The same goes for modifying __fish_build_paths. We could just have that look for a file in a special place (perhaps /etc/fish/nix.init.fish) and source it if it exists. Then the NixOS module can drop that file into place if necessary, and otherwise it won't affect other users.
NixOS could be described as an operating-system level declarative configuration management tool. The way that Nix realizes package installation and the way it realizes other system configuration tasks are essentially the same, but technically here it's not the fish package that gets modified but what is called the ‘system profile’. Which is good, since rebuilding fish itself for OS configuration changes would be not be. :-) The impressions you give about our desiderata in your bulleted list are largely correct. Currently, we insert the configuration stuff we want into the fish config simply using string interpolation, which is built into Nix-the-language. We do have a bunch of variables we want to set early on, and we do not need command substitution but we do use some statements besides export statements. The file which we use for this purpose on my system currently looks like this:
To get fish to understand this file without too much pain, we leverage the foreign-env fish plugin from oh-my-fish, which allows us to run bash scripts and import the variable changes they make back into fish. That solution works well, hackish though it is. Your explanation of the way fish sourcing currently works was very clear and quite helpful. For the short term, all we need to do is figure out where to hook in; NixOS already takes care of pulling the fenv plugin down, and it knows precisely where it lives, so adding it to the fish_function_path is just a matter of string interpolation in whatever config file we generate for initializing fish. So if we can have a place to drop a fish script, we can make sure that the fish script can successfully call fenv on the sh script NixOS is already using. I'm thinking the following would be natural, let me know what you think
PS: The distinction between $__fish_sysconfdir and /etc/fish on NixOS is important. Because of the way Nix isolates its packages from one another (so it can selectively reunite them in a controlled way), $__fish_sysconfdir ends up being under a package-specific etc. To illustrate
So currently we generate our real /etc/fish/config.fish, which plays the usual role, at the module level. Then at the package level we tell give the fish package a local etc/fish/config.fish which tells it to look for the global one and source it if it exists. What I tried to describe above is doing something similar for the stuff that needs to be sourced early. |
While I'm here, I'd like to share a cute story: My love of fish is actually the reason I discovered and started using Nix. A couple years ago, I wanted to use fish on computers in the lab at my university. However, it wasn't installed, and when I asked the administrators about adding it, they told me they only installed software on those systems for faculty who needed them for official projects or classes. I wasn't satisfied with that answer, so I searched the web for tools I could use to install Linux packages in usermode. Since at the time LinuxBrew didn't have any binary caching, Nix turned out to be the most convenient option. With a little bit of messing around, I was able to install any package in the whole NixOS/Nixpkgs collection with a guarantee that it would not interfere with any system packages. I was delighted. Fish was of course the very first thing I installed with Nix! I only decided to try NixOS some time later. I'd never have gotten a taste of it if I hadn't been so hungry for fish. :-) |
Obviously, I don't love this solution. I can see why you need it though. The example you provided looks like it's composed of parts that fish can deal with (the
Oh yes, of course. It's just that I'm used to typing "/etc" to make it easier to digest for new users.
That would probably work, I'm just not sure what the indirection is good for. Why not have __fish_build_paths.fish source nix-init.fish if it exists directly?
Hehe 😄. |
It would be more general, which means that other distros or users who might need similar hooks could more easily take advantage of them. One comes to mind immediately: NixOS has something of a sister project in GuixSD, a GNU distribution whose mechanisms of package and configuration management are inspired by Nix (currently they still share some code deep down, but that may be rewritten at some point), but which uses GNU Guile instead of the Nix language. (Guix has wonderful documentation and a killer Emacs mode, if you're into those things and you'd like to explore these package management concepts some time.)
Good eye. I'm not sure why it does that. Perhaps just to make sure that $EDITOR has some value and because more sophisticated code generation would be more work. I don't know how that file gets generated, but that would be worth looking into because it's probably around there where we could just emit shell-dependent code instead of always doing POSIX and then wrapping it with fenv for fish. That would also make it easier to add login support for other non-POSIX shells, like Oil Shell or Elvish. |
Well, you'd be adding your stuff regardless. I'm proposing you append it to __fish_build_paths.fish or replace that file completely. If you can do that (and the code to do it is borderline trivial), then the additional file doesn't add much value.
I'm a sucker for a good Emacs mode, yes.
If it were me, I'd emit "KEY=VALUE" or at least always "export KEY=VALUE", no quotes, value goes to the end of the line. That format is easy to parse for anything. Essentially, dump the output of
IIRC, Oil does the hybrid thing of a bash-mode (that is supposed to be compatible except for one corner-case) and a "native" mode. The author has written a lot of interesting stuff about parsing shell scripts. |
I did end up solving the NixOS Fish initialization problems in the way you described, @faho . Thanks for helping me to reason through and understand the Fish initialization process! I will likely reach out again about possibly upstreaming other changes (minor changes to that $__fish_datadir/config.fish file) after I add some more features to Fish's Nix package, but by the time that happens I'll have some cool demos you can play with to show why those changes are worthwhile and sane. And special thanks again for helping to review my changes on the NixOS PR even though you're not a Nix user. That was very sweet of you. I appreciate it. Thanks as well to @krader1961 for helping me contextualize this issue in relation to other decisions about the Fish initialization process. Y'all are great, and Fish is a great shell. |
My environment:
fish version:
operating system:
According to the Fish documentation, user snippets (
$XDG_CONFIG_HOME/fish/conf.d/*.fish
) are supposed to be read after system-wide configuration files ($__sysconfdir/conf.d/*.fish
). But they are sourced in$__fish_datadir/config.fish
, which comes before$__fish_sysconfdir/config.fish
.On NixOS, this causes massive breakage, because until
$__fish_sysconfdir/config.fish
(generated by the operating system to set up all the correct paths and include admin-specified config for the operating system) has been sourced, external programs not required by fish itself (you can see the dependencies at the top of that file there) are not on the PATH and there are similar problems for other path-like variables— it's not just about executables. So on my system, for example, when ~/.config/fish/conf.d/omf.conf is sourced and loads up a fish plugin which depends on the programhostname
, I get breakage, before I can even see my prompt. I don't just mean error messages either— I mean the whole console hangs and I have to switch to a new VC.I've been able to work around this issue by making sure the first snippet loaded from my home directory sources /etc/fish/config.fish and that /etc/fish/config.fish exits early if it has already been sourced by a given shell.
The breakage for this is pretty damn pervasive, especially if your display manager runs from your login shell (like SDDM does). When I first encountered this bug, it both killed my display manager and rendered all of my virtual consoles unusable.
The text was updated successfully, but these errors were encountered: