Nixpkgs/Patching Nixpkgs: Difference between revisions
replace importing nixpkgs: https://discourse.nixos.org/t/1000-instances-of-nixpkgs/17347 with existing NixOS module param `pkgs`, and replace fetchurl with fetchpatch2 that will do normalization on generated patch |
Add multiple other options to choose from |
||
| Line 1: | Line 1: | ||
Sometimes it may be required to patch a copy of | Sometimes it may be required to patch a copy of nixpkgs directly, rather than use an overlay to patch an individual package. One scenario of where this might happen is if nixpkgs doesn't contain a change you need, but you find some | ||
existing PR that has yet to be merged, and so want to leverage those changes prior to them being merged. | existing PR that has yet to be merged, and so want to leverage those changes prior to them being merged. | ||
There are many ways to patch nixpkgs, each with its own advantages and disadvantages. We will go through the most popular ones: | |||
{| class="wikitable" style="text-align: center; width: 500px;" | |||
|- | |||
! Name !! Method !! IFD !! Requires Flakes | |||
|- | |||
| applyPatch || .patch files || X || | |||
|- | |||
| nixpkgs-patcher || .patch files || X || X | |||
|- | |||
| nix-patcher || .patch files || || X | |||
|- | |||
| gh-cherry-pick || git cherry-pick || || | |||
|} | |||
Most of the solutions work on the <code>.patch</code> files, but those have a huge disadvantage - they quickly go out of date and have to be constantly rebased. This is especially true for solutions that use Git to apply patches. Another disadvantage may be [https://nixos.org/manual/nix/unstable/language/import-from-derivation IFD], which may result in [https://nixcademy.com/posts/what-is-ifd-ups-and-downs/ performance and runtime issues]. | |||
== Usage == | |||
For the sake of example, let's say you are using the software package [https://ghidra-sre.org/ Ghidra], and the latest version available on | |||
nixpkgs unstable is [https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=ghidra 11.1.2]. | |||
You know that the Ghidra developers have recently released 11.2, and want to use it before it's in nixpkgs. You could try to create an overlay and manually update the package, but maybe in the process, you realize it is not a | |||
trivial update. You may find there is already a pending PR to nixpkgs with the updated version and | |||
changes you want, such as [https://github.com/NixOS/nixpkgs/pull/344917 this PR]. If you didn't | |||
want to wait for that PR to be merged into nixpkgs, you could apply the PR patch directly to your nixpkgs instead. There are, of course, other reasons you may wish to use a patched nixpkgs, and the method applies to any of those cases. | |||
To get your patch, you will usually need to find a list of commits from the desired PR and fetch them. Try to avoid fetching something like https://github.com/NixOS/nixpkgs/pull/344917.diff, as it can change if the PR is still open. If you are fetching from GitHub, don't forget to add <code>?full_index=1</code> at the end of the URL for better reproducibility. | |||
<syntaxhighlight lang="nix"> | |||
# for https://github.com/NixOS/nixpkgs/pull/344917 | |||
pkgs.fetchpatch2 { | |||
# note the `.diff` at the end! | |||
url = "https://github.com/NixOS/nixpkgs/commit/55367b381af23045c93f7b85171db850e628ef7d.diff?full_index=1"; | |||
hash = "sha256-/XnEHRQahmHUNstgTVkQC+LGp7Z0wmf+qPVyKntgZG0="; | |||
} | |||
</syntaxhighlight> | |||
Or you can also fetch a range, if you want to fetch multiple commits in one go | |||
<syntaxhighlight lang="nix"> | <syntaxhighlight lang="nix"> | ||
{ pkgs, ... | pkgs.fetchpatch2 { | ||
# 4c030cf is a parent of the first PR commit | |||
url = "https://github.com/NixOS/nixpkgs/compare/4c030cf309bffa9cd87336705e96ce941ce977d9...05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1"; | |||
hash = "sha256-gH8NHJ+i1snTN/ehFopQko8BfeO0PxQaFRCQCoLzVfg="; | |||
} | |||
</syntaxhighlight> | |||
=== Using <code>[https://github.com/NixOS/nixpkgs/blob/f6e07eb0e1e42c38f8e0b6ffc221b7bf73bece4b/pkgs/build-support/trivial-builders/default.nix#L981 applyPatches]</code> function === | |||
<code>applyPatches</code> is a nixpkgs function that applies a list of patches to a source directory. Internally, it just creates a new derivation via <code>stdenv.mkDerivation</code> and passes all of the patches to the usual <code>[https://github.com/NixOS/nixpkgs/blob/12c894cb74171bcec130756cc0f77720fca6587d/pkgs/stdenv/generic/setup.sh#L1371-L1404 patchPhase]</code>. | |||
<syntaxhighlight lang="nix"> | |||
let | let | ||
# to access the `applyPatches` function, you will need | |||
# to import unpatched nixpkgs first | |||
pkgs = import <nixpkgs> { | |||
# with flakes, you may also need to hardcode the system | |||
# system = "x86_64-linux"; | |||
}; | |||
nixpkgs-344917-drv = | nixpkgs-344917-drv = | ||
pkgs.applyPatches { | pkgs.applyPatches { | ||
| Line 28: | Line 65: | ||
patches = [ | patches = [ | ||
(pkgs.fetchpatch2 { | (pkgs.fetchpatch2 { | ||
url = "https://github.com/NixOS/nixpkgs/ | url = "https://github.com/NixOS/nixpkgs/compare/4c030cf309bffa9cd87336705e96ce941ce977d9...05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1"; | ||
hash = "sha256-gH8NHJ+i1snTN/ehFopQko8BfeO0PxQaFRCQCoLzVfg="; | |||
}) | }) | ||
]; | ]; | ||
| Line 35: | Line 72: | ||
nixpkgs-344917 = import nixpkgs-344917-drv { inherit (pkgs.stdenv) system; }; | nixpkgs-344917 = import nixpkgs-344917-drv { inherit (pkgs.stdenv) system; }; | ||
in | in | ||
# ... | nixpkgs-344917.ghidra.version # == 11.2 :tada: | ||
</syntaxhighlight> | |||
In the above example, we create a derivation with the patch applied, called <code>nixpkgs-344917-drv</code>. Pay attention, we take the <code>src</code> attribute to get back the original (unpatched) nixpkgs. We then import that new derivation, which we assign to <code>nixpkgs-344917</code>. Now we can use <code>nixpkgs-344917</code> to access the Ghidra 11.2 package, as well as any other packages normally available in nixpkgs. | |||
=== Using [https://github.com/gepbird/nixpkgs-patcher nixpkgs-patcher] === | |||
nixpkgs-patcher is a nice wrapper around <code>[https://wiki.nixos.org/wiki/Nixpkgs/Patching_Nixpkgs#Using_applyPatches_function applyPatches]</code> which makes integration with NixOS flake's system definition very easy: | |||
<syntaxhighlight lang="nix"> | |||
{ | |||
inputs = { | |||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; | |||
nixpkgs-patcher.url = "github:gepbird/nixpkgs-patcher"; | |||
nixpkgs-patch-ghidra-11-2 = { | |||
url = "https://github.com/NixOS/nixpkgs/commit/55367b381af23045c93f7b85171db850e628ef7d.diff?full_index=1"; | |||
flake = false; | |||
}; | |||
nixpkgs-patch-ghidra-11-2-ret-sync = { | |||
url = "https://github.com/NixOS/nixpkgs/commit/05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1"; | |||
flake = false; | |||
}; | |||
}; | |||
outputs = | |||
{ nixpkgs-patcher, ... }@inputs: | |||
{ | |||
nixosConfigurations.yourHostname = nixpkgs-patcher.lib.nixosSystem { | |||
modules = [ | |||
./configuration.nix | |||
./hardware-configuration.nix | |||
]; | |||
specialArgs = inputs; | |||
}; | |||
}; | |||
} | |||
</syntaxhighlight> | |||
=== Using [https://github.com/katrinafyi/nix-patcher nix-patcher] === | |||
nix-patcher is a script that automatically gets a list of patches from your <code>flake.nix</code> and applies them to your nixpkgs fork sequentially. | |||
<syntaxhighlight lang="nix"># flake.nix | |||
{ | |||
inputs = { | |||
nixpkgs = "github:MyName/nixpkgs/patched-branch"; | |||
nixpkgs-upstream = "github:NixOS/nixpkgs/nixos-unstable"; | |||
nixpkgs-patch-10 = { | |||
url = "https://github.com/NixOS/nixpkgs/commit/55367b381af23045c93f7b85171db850e628ef7d.diff?full_index=1"; | |||
flake = false; | |||
}; | |||
nixpkgs-patch-20 = { | |||
url = "https://github.com/NixOS/nixpkgs/commit/05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1"; | |||
flake = false; | |||
}; | |||
}; | |||
}</syntaxhighlight> | |||
nix-patcher will notice the special "-upstream" and "-patch-" suffixes and match these with "nixpkgs-upstream". When run, the repo and branch to update will be taken from the "nixpkgs" inputs. The upstream, patches, and fork inputs are linked together by their common prefix (here, "nixpkgs"). | |||
=== Using [https://github.com/PerchunPak/gh-cherry-pick gh-cherry-pick] === | |||
gh-cherry-pick interacts directly with the GitHub API to cherry-pick or merge branches. This tool is unique in that it doesn't depend on or use Nix; it is a generic script that fits perfectly for our use case. | |||
<syntaxhighlight lang="bash"> | |||
# This tool requires a classic token with `repo` and `workflow` permissions | |||
# or a fine-grained token with `contents` and `workflows` permissions | |||
GITHUB_TOKEN=ghp_... gh-cherry-pick \ | |||
--target MyOrg/nixpkgs@patched \ | |||
`: # cherry-pick these commits` \ | |||
NixOS/nixpkgs/3f5ba52cc4701bf341457dfe5f6cb58e0cbb7f83 \ | |||
NixOS/nixpkgs/49ba75edefc8dc4fee45482f77a280ddd7121797 \ | |||
`: # or merge the entire branch!` \ | |||
Someone/nixpkgs@pr-branch | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Very similar to [https://wiki.nixos.org/wiki/Nixpkgs/Patching_Nixpkgs#Using_nix-patcher nix-patcher], it maintains a fork of nixpkgs and applies the patches on it sequentially. The main difference is that it doesn't operate on patch files, but runs <code>[https://git-scm.com/docs/git-cherry-pick git cherry-pick]</code> directly through GitHub API. This is a huge advantage, since Git can automatically rebase commits instead of forcing you to do that. | |||
== Resources == | == Resources == | ||
| Line 46: | Line 157: | ||
* [https://ertt.ca/nix/patch-nixpkgs/ Patching <nixpkgs>] | * [https://ertt.ca/nix/patch-nixpkgs/ Patching <nixpkgs>] | ||
* [https://discourse.nixos.org/t/patch-nixpkgs-using-cherry-picks-without-local-clone/76925 gh-cherry-pick announcement (and discussion about other tools)] | |||
Revision as of 18:31, 13 April 2026
Sometimes it may be required to patch a copy of nixpkgs directly, rather than use an overlay to patch an individual package. One scenario of where this might happen is if nixpkgs doesn't contain a change you need, but you find some existing PR that has yet to be merged, and so want to leverage those changes prior to them being merged.
There are many ways to patch nixpkgs, each with its own advantages and disadvantages. We will go through the most popular ones:
| Name | Method | IFD | Requires Flakes |
|---|---|---|---|
| applyPatch | .patch files | X | |
| nixpkgs-patcher | .patch files | X | X |
| nix-patcher | .patch files | X | |
| gh-cherry-pick | git cherry-pick |
Most of the solutions work on the .patch files, but those have a huge disadvantage - they quickly go out of date and have to be constantly rebased. This is especially true for solutions that use Git to apply patches. Another disadvantage may be IFD, which may result in performance and runtime issues.
Usage
For the sake of example, let's say you are using the software package Ghidra, and the latest version available on nixpkgs unstable is 11.1.2. You know that the Ghidra developers have recently released 11.2, and want to use it before it's in nixpkgs. You could try to create an overlay and manually update the package, but maybe in the process, you realize it is not a trivial update. You may find there is already a pending PR to nixpkgs with the updated version and changes you want, such as this PR. If you didn't want to wait for that PR to be merged into nixpkgs, you could apply the PR patch directly to your nixpkgs instead. There are, of course, other reasons you may wish to use a patched nixpkgs, and the method applies to any of those cases.
To get your patch, you will usually need to find a list of commits from the desired PR and fetch them. Try to avoid fetching something like https://github.com/NixOS/nixpkgs/pull/344917.diff, as it can change if the PR is still open. If you are fetching from GitHub, don't forget to add ?full_index=1 at the end of the URL for better reproducibility.
# for https://github.com/NixOS/nixpkgs/pull/344917
pkgs.fetchpatch2 {
# note the `.diff` at the end!
url = "https://github.com/NixOS/nixpkgs/commit/55367b381af23045c93f7b85171db850e628ef7d.diff?full_index=1";
hash = "sha256-/XnEHRQahmHUNstgTVkQC+LGp7Z0wmf+qPVyKntgZG0=";
}
Or you can also fetch a range, if you want to fetch multiple commits in one go
pkgs.fetchpatch2 {
# 4c030cf is a parent of the first PR commit
url = "https://github.com/NixOS/nixpkgs/compare/4c030cf309bffa9cd87336705e96ce941ce977d9...05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1";
hash = "sha256-gH8NHJ+i1snTN/ehFopQko8BfeO0PxQaFRCQCoLzVfg=";
}
Using applyPatches function
applyPatches is a nixpkgs function that applies a list of patches to a source directory. Internally, it just creates a new derivation via stdenv.mkDerivation and passes all of the patches to the usual patchPhase.
let
# to access the `applyPatches` function, you will need
# to import unpatched nixpkgs first
pkgs = import <nixpkgs> {
# with flakes, you may also need to hardcode the system
# system = "x86_64-linux";
};
nixpkgs-344917-drv =
pkgs.applyPatches {
src = pkgs.path;
patches = [
(pkgs.fetchpatch2 {
url = "https://github.com/NixOS/nixpkgs/compare/4c030cf309bffa9cd87336705e96ce941ce977d9...05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1";
hash = "sha256-gH8NHJ+i1snTN/ehFopQko8BfeO0PxQaFRCQCoLzVfg=";
})
];
};
nixpkgs-344917 = import nixpkgs-344917-drv { inherit (pkgs.stdenv) system; };
in
nixpkgs-344917.ghidra.version # == 11.2 :tada:
In the above example, we create a derivation with the patch applied, called nixpkgs-344917-drv. Pay attention, we take the src attribute to get back the original (unpatched) nixpkgs. We then import that new derivation, which we assign to nixpkgs-344917. Now we can use nixpkgs-344917 to access the Ghidra 11.2 package, as well as any other packages normally available in nixpkgs.
Using nixpkgs-patcher
nixpkgs-patcher is a nice wrapper around applyPatches which makes integration with NixOS flake's system definition very easy:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-patcher.url = "github:gepbird/nixpkgs-patcher";
nixpkgs-patch-ghidra-11-2 = {
url = "https://github.com/NixOS/nixpkgs/commit/55367b381af23045c93f7b85171db850e628ef7d.diff?full_index=1";
flake = false;
};
nixpkgs-patch-ghidra-11-2-ret-sync = {
url = "https://github.com/NixOS/nixpkgs/commit/05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1";
flake = false;
};
};
outputs =
{ nixpkgs-patcher, ... }@inputs:
{
nixosConfigurations.yourHostname = nixpkgs-patcher.lib.nixosSystem {
modules = [
./configuration.nix
./hardware-configuration.nix
];
specialArgs = inputs;
};
};
}
Using nix-patcher
nix-patcher is a script that automatically gets a list of patches from your flake.nix and applies them to your nixpkgs fork sequentially.
# flake.nix
{
inputs = {
nixpkgs = "github:MyName/nixpkgs/patched-branch";
nixpkgs-upstream = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-patch-10 = {
url = "https://github.com/NixOS/nixpkgs/commit/55367b381af23045c93f7b85171db850e628ef7d.diff?full_index=1";
flake = false;
};
nixpkgs-patch-20 = {
url = "https://github.com/NixOS/nixpkgs/commit/05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1";
flake = false;
};
};
}
nix-patcher will notice the special "-upstream" and "-patch-" suffixes and match these with "nixpkgs-upstream". When run, the repo and branch to update will be taken from the "nixpkgs" inputs. The upstream, patches, and fork inputs are linked together by their common prefix (here, "nixpkgs").
Using gh-cherry-pick
gh-cherry-pick interacts directly with the GitHub API to cherry-pick or merge branches. This tool is unique in that it doesn't depend on or use Nix; it is a generic script that fits perfectly for our use case.
# This tool requires a classic token with `repo` and `workflow` permissions
# or a fine-grained token with `contents` and `workflows` permissions
GITHUB_TOKEN=ghp_... gh-cherry-pick \
--target MyOrg/nixpkgs@patched \
`: # cherry-pick these commits` \
NixOS/nixpkgs/3f5ba52cc4701bf341457dfe5f6cb58e0cbb7f83 \
NixOS/nixpkgs/49ba75edefc8dc4fee45482f77a280ddd7121797 \
`: # or merge the entire branch!` \
Someone/nixpkgs@pr-branch
Very similar to nix-patcher, it maintains a fork of nixpkgs and applies the patches on it sequentially. The main difference is that it doesn't operate on patch files, but runs git cherry-pick directly through GitHub API. This is a huge advantage, since Git can automatically rebase commits instead of forcing you to do that.
Resources
The following are resources that go into more depth on this topic.