back

by kristjansson·5y ago·view on hn ↗
Nix is really cool for demos like this, but seems to get complicated if you need to grab specific versions of a dependency. At last check, that involved digging through the history of the package/channel, grabbing the hash of the package corresponding to the desired version, and then referencing the package by hash, all with basically no tool support?

For system that is so proud of allowing multiple versions of dependencies to coexist, it seemed surprisingly difficult to actually find and use specific versions of a dependency in, you know, applications.

5 comments
Incredible, many many people responded to you, no one gave you a useable answer at all.

There are at least two answers.

The most popular one, which I disagree with, is to find an older nixpkgs hash and just use that. This works of you don't care about having all dependencies downgraded or security updates of anything in nix. There is even this tool to help you do it: https://lazamar.co.uk/nix-versions/

However, I dislike that method a lot.

The correct method is via overrideAttrs possible with an overlay. This post explains that pretty well: https://discourse.nixos.org/t/how-to-override-package-versio...

Interesting, thanks for the pointers.

So AIUI the second, recommended method, it's overwriting the source location for that particular package, and then letting nix build that with an otherwise unchanged nix recipe/script/thing? Am I responsible (at the point of use) to make any other changes required to make that version build successfully? Do dependents of that package get rebuilt too?

An overlay could define a package with the same name as one from a lower level.

IE python = super.python...

It could also completely override the entire definition of something if you wanted.

IE python = super.stdenv.mkDerivation { ... };

But you can also add new names, and not modify the existing definitions.

IE python-for-me = super.python.overrideAttrs (...);

In the last case you can reference your modified copy while other things in nixpkgs can use the one built upstream.

The use case you're describing is impossible with other packaging tools, so really the choice is either "nothing" or "write a nix script".

(Tool support would be nice, but nixpkgs has no standard way to specify packages across languages and runtimes, so not possible right now.)

That's a fair point, I don't have the same expectation of other os-level package managers.

There are alternatives in other projects that bridge build system and package management while directly addressing versioning of dependencies e.g. Spack, Conda, ... The first in particular does exactly what I want, so I suppose I should just be happy with it. Still would be nice for Nix to compete in the same space, esp. with the really slick translation from local to containerized environment displayed in here

Nix can do what you want, it just requires a little bit more manual intervention.

There's benefits, too, though - you know the C compilers, build flags, versions and libraries of everything that goes into making a Python library run. It's easy to change any of it, integrate with your own custom libraries or other languages and test the whole contraption in its entirety.

This is whats so powerful and wonderful about it, your end result isn't simply based on the direct dependents, but also includes the build chain for all things.

That's just not that common in other build tools.

Flakes, which is currently an experimental feature, is gonna solve the issue. It works similar to cargo and npm, in that it pins the dependencies and creates a lockfile.
As I understand it, flakes don't really solve the problem parent is talking about. The discoverability is still poor. Say I need a python package at version x.x.x. How do I find which nixpkgs commit hash I should use to get that specific version?
> How do I find which nixpkgs commit hash I should use to get that specific version

This is answered in a sibling comment here: https://news.ycombinator.com/item?id=28243409

The tool does exactly that.

However:

> find and use specific versions of a dependency in, you know, applications.

This is not what nixpkgs currently provides, or aims to provide.

nixpkgs is like the Debian, Gentoo, etc. package repos: In most cases, it provides one version of a package. If you want to use an older one, you have to use older version of all dependent packages, as they were at that time; this in practice means using software with security vulnerabilities that are only fixed in current nixpkgs.

nixpkgs is not currently designed to let you compose your application from libraries taken from different nixpkgs versions. It is insecure (see above) and slow (evaluating more than a few different nixpkgs versions takes a long time).

The current correct way is to use a current nixpkgs version as a base, and then use an override to update the sources of the packages you want to have a different version of explicitly. Or use a tool that does this automatically for your programming ecosystem based on that one's package manager, like `stack2nix` for Haskell, `yarn2nix` for Javascript, and so on. Nixpkgs makes such overrides easy.

What you describe is weird.

It's very common to pin dependencies to specific versions. And upgrade them carefully, also to specific versions.

If I use a dependency at version X.Y, a vulnerability is discovered in it, and fixed in version X.Y.Z, I don't want to "start from base nixpkgs". I want to upgrade that specific dependency to a very specific version.

From how you described it, it's either not possible or extremely cumbersome to do with nix. Which begs the question: what's the point?

> I want to upgrade that specific dependency to a very specific version.

Yes, this is what I am arguing you should do, by overriding its source to that version (which I described as being easy).

You should not try to pull in a version of nixpkgs (whole OS) that has X.Y.Z, and combine it with another version of nixpkgs (whole OS) that has some other library's version A.B.C.

In the same way as you don't try to combine 3 different versions of Debian to get specific versions of 3 different Python libraries.

Concrete example:

Current nixpkgs has libpng-1.3 and libjpeg-4.6. For building your app you desire libpng-1.2.3 and libjpeg-4.5.6.

To get them, you use the current nixpkgs, and override `libpng` and `libjpeg` to the versions you want using e.g. `libpng.override { src = fetchGit ... }`. You do NOT do `libpng = (import <older-nixpkgs-version-1> {})` and `libjpeg = (import <older-nixpkgs-version-2> {})`. Because that way you will also end up with 3 different versions of e.g. `glibc`, which may be incompatible.

I hope this clarifies it!

We need to specify hashes for `libpng-1.3` and `libjpeg-4.6` right? Is there tooling to fill-out them automatically?

How to find out what versions of `libpng` are available to be used in `shell.nix` or `shell.flake` with nix tools?

I believe you can override the version property of the derivation that builds the package, if its build script is compatible enough between the two versions.
If your dependency is on GitHub, this tool makes it super easy. https://github.com/seppeljordan/nix-prefetch-github