overrideAttrs not taking effect, how to override fetchFromGitHub, and more

I think I’ve got the basics of overlays and overriding packages down, but I’m still running into a couple issues. I wanted to downgrade my version of sway due to a crash on master. I’m using sway from nixpkgs-wayland’s overlay: nixpkgs.overlays = [ inputs.nixpkgs-wayland.overlay ]; and decide to apply overrideAttrs to my sway package. This is inside my home-manager config, but AFAIK it shouldn’t be an issue.

      wayland.windowManager.sway.package = pkgs.sway.overrideAttrs (previousAttrs: {
        src = pkgs.fetchFromGitHub {
          owner = "swaywm";
          repo = "sway";
          rev = "4a2210577c7c4f84a99ca03386b8910d2f419ab6";
          sha256 = "sha256-CZ4BkQ5JfrpTu+nyFmZygYYd69182uSPH2Q2M5YSLY8=";
        };
      });

Unfortunately, nothing happens, and I always have the latest version of sway, not the revision I’ve tried to pin here.

I wondered if this might be occurring because I already have an overlay affecting that passage, so I tried placing it next to that overlay:

  nixpkgs.overlays = [
    inputs.nixpkgs-wayland.overlay
    (self: super: {
      sway = super.sway.overrideAttrs (previousAttrs: {
        src = pkgs.fetchFromGitHub {
          owner = "swaywm";
          repo = "sway";
          rev = "4a2210577c7c4f84a99ca03386b8910d2f419ab6";
          sha256 = "sha256-CZ4BkQ5JfrpTu+nyFmZygYYd69182uSPH2Q2M5YSLY8=";
        };
      });
    })
  ];

Again, no issues building this, but my overlay is completely ignored. I have no idea how overlays are prioritized, and I thought I might need to do something like [(inputs.nixpkgs-wayland.overlay.extend (self: super: ...))]; but that does throw an error: error: value is a function while a set was expected. Makes sense: the manual says " the nixpkgs.overlays option, if present, is passed to the system Nixpkgs directly as an argument" but the way extend works is nixpkgs.extend overlay1.extend overlay2.extend .... I’m still not sure, however, how to make sure one overlay takes priority over another.

I wondered if there was some sort of upstream issue in the sway package causing the issue. I had previously had overrideAttrs fail to do anything because src was defined by variable interpolation, so my override of version wasn’t applied to src. I took a look at the derivation. and see that it has some finalAttrs argument I’m unsure about, but after a bit of research and skimming stdenv.mkDerivation: overlay style overridable recursive attributes by roberth · Pull Request #119942 · NixOS/nixpkgs · GitHub I think this is actually the best practice which should make overrides easier. Good to know, but doesn’t help me diagnose my issue.

While reading through that issue, I also came across Make fetchFromGitHub & friends overridable by piegamesde · Pull Request #158968 · NixOS/nixpkgs · GitHub and wondered if I needed to apply two overrides, like so:

      package = pkgs.sway.overrideAttrs (previousAttrs: {
        src = previousAttrs.src.override {
          rev = "4a2210577c7c4f84a99ca03386b8910d2f419ab6";
          sha256 = "sha256-CZ4BkQ5JfrpTu+nyFmZygYYd69182uSPH2Q2M5YSLY8=";
        };

This also gives me an error, saying error: attribute 'src' missing from previousAttrs. While this is kind of annoying, it feels like progress, because now I’ve run into something that should work and is giving me an error instead of silently failing. I’m pretty sure the way I’ve structured this code should work based on Make fetchFromGitHub & friends overridable by piegamesde · Pull Request #158968 · NixOS/nixpkgs · GitHub.

Would anyone mind letting me know if there are errors in my understanding (since this has also been a learning exercise) and also how I might be able to resolve my problem? To make it clear, the goal of this is just to easily go back to an old version of software (in this case for easy git bisecting), so I’m happy with any solution that lets me do that with the matryoshka of overlays, home-manager, etc. that’s been working well for me.

Always glad to see people using sway on nixos!

Out of curiosity, why do you need the nixpkgs-wayland overlay? I have been using sway on nixos for a few years now, and the only issues I have ever had were related to webrtc video calls and gtk themes (both addressed in Sway - NixOS Wiki)

IMO overlays are definitely one of the more fragile abstractions available in nixpkgs, so I tend to avoid them for base system stuff.

1 Like

Derivations with finalAttrs can be overridden just like any other derivation. The issue is that you are overriding the wrong attribute:

You can either override sway-unwrapped globally, or override the sway-unwrapped input for sway attribute using override.

3 Likes

one more reason why it should have been sway/sway-wrapped rather than sway-unwrapped/sway :cry:

2 Likes

The latest release was very good to me too, but I needed to use WLR_RENDERER=vulkan on an Nvidia machine to avoid the awful flickering. The de-hardcoding to support that with Nix hasn’t been released in wlroots yet : Sway does not start with WLR_RENDERER=vulkan on Nvidia · Issue #216002 · NixOS/nixpkgs · GitHub

It looks like the next release is getting closer though, and I’m looking forward to switching back to stable.

Thanks! (And yeah, I was trying to say that I had ruled out finalAttrs as an issue, it just confused me a little on my first read of the code. It’s a neat way of handling it though!)

For posterity, I ended up solving this with the following:

wayland.windowManager.sway = {
  package = pkgs.sway.override (previous: {
    sway-unwrapped = previous.sway-unwrapped.overrideAttrs (previousAttrs: {
      src = previousAttrs.src.override {
        rev = ...
      };
    });
  });

That’s a lot of overrides, so I see your point teto :slight_smile:

If you consider it important enough, you could define an attribute to make overriding simpler for users of the package. Something like this:

sway = mkDerivation (finalAttrs: {
  # ...
  passthru.overrideSwayBuildAttrs = f: finalAttrs.finalPackage.overrideAttrs (prevAttrs: {
    sway-unwrapped = prevAttrs.sway-unwrapped.overrideAttrs f;
  });
});

I think .override is not available this way, as I don’t think callPackage does an override with passthru.override =, but rather a //. So you’d have to refer from sway to sway-unwrapped through finalAttrs instead of the package function (ie override) argument, except when initializing the attribute, in which case it does come from the package function arguments.

1 Like

Thank you! I saw nix build without complaint and declared the case closed a little too early. I read through Nixpkgs manual: Recursive attributes in mkDerivation and ELI5 how finalAttrs design pattern works but I’m still confused about how to use a different sway-unwrapped in this case.

I think I see how your example attribute works : users can pass a function like (prev: foo = prev.foo ...) to overrideAttrs, and then the overridden wrapper is given to finalAttrs and therefore mkDerivation. This works because of the lazy ‘function-result-as-argument.’ (Please correct me if I’m wrong.) But how would I do this outside of the package? I will take a look at the callPackage source a little later to see if it helps.

This might not be the same cause for your original issue but I ran into a similar problem in that overrideAttrs would silently just keep the original package. According to a different thread, if the sha256 hash matches a hash from nixpkgs, it would just use the original version without checking that the rev has changed.

This was surprising, as I’ve previously used this method to get the actual hash from the new version in other contexts, so I’m not sure what was different from this method.

My dumb workaround was to change one char in the hash or there’s the nix-prefetch-git workaround.

Perhaps a typo? fixed-output derivations are fundamentally stored under their hash, if you specify the wrong hash you’ll get whatever that has points to in your store currently, even if the url you’re downloading from changes. Nix can’t know if a URL change means that data should have changed, after all it may just be a different mirror.

You can also leave the hash as an empty string, or use lib.fakeHash. You don’t need to copy the original expression in the first place just to get the right number of characters.

It was just simpler to copy the original block, although ironically I wouldn’t have run into it had I just typed it up from scratch. Well, thanks for the information, that does make it clearer.

EDIT: Wouldn’t a change to rev imply a change to the data and so the hash should be expected to have changed as well? Changing the url = no change to hash does make sense, however. Anyway, will just use one of those workarounds in the future to avoid the issue.

No, that’s what I was trying (and failed) to explain. Imagine for example, you’re an organization trying to ensure your software remains buildable for 10 years. You write a package using nix because supposedly it’s good at that.

Now, you also hear that sometimes websites stop existing. You realize that all of your inputs point at various websites that you are not in control of. Any of them may disappear at any time, but all your source code lives on them. This is terrifying!

The solution is to grab copies of all the sources you use and host them on your own infrastructure somewhere, and change the URL for your packages to point to those locations.

The data is the same, the URL is not. It’d really suck if you had to rebuild your entire software stuck just because of this change.

Basically, two URLs may point at the same data, re-fetching just because a URL changes isn’t the right abstraction. The user should tell nix when the data has changed by updating the hash; the hash is the ultimate source of truth.

It might be a reasonable idea to throw a warning when people update URLs without updating the hash, though, if that’s at all possible. It is a common mistake.

Edit: Oh, you’re specifically referring to the rev of fetchFromGitHub. Nix calculates the hash based on the data it actually receives for fixed-output derivations, there’s no guarantee the server doesn’t misbehave and send different data than the rev you asked for. Since the hash is the single source of truth, the rev is ignored.