iTranslated by AI
Common Pitfalls When Submitting PRs to Nixpkgs
Prerequisites
This article focuses on pull requests for adding packages. It is particularly intended for CLI/TUI tools written in Golang or Rust that can be handled relatively easily with nix-init.
Note that this article is merely a summary of points I personally found easy to get "tripped up" on (I have only merged about 5 PRs so far). The most important thing is to read the CONTRIBUTING.md thoroughly. I also recommend taking a look at pkgs/README.md and the documentation for each language.
Basic Initial Workflow
First, fork Nixpkgs and clone it locally. Then, you need to register the original Nixpkgs as upstream.
git remote add upstream git@github.com:NixOS/nixpkgs.git
git fetch upstream
Create a new branch based on the latest master.
git switch --create hello/init upstream/master
There does not seem to be a strict rule for branch names, but the examples in CONTRIBUTING.md show a format like update-hello. I often use <package>/init, but any name that clearly conveys the purpose should be fine.
Once the branch is created, run nix-init at the root of the repository. nix-init is a tool that automatically generates a package.nix template from a repository URL such as GitHub.
It generates output with fields like pname, version, src, hash, vendorHash (for Go), and cargoHash (for Rust) filled in to some extent.
nix run github:nix-community/nix-init
As a general rule, new packages should be placed in pkgs/by-name/<2-character-prefix>/<package-name>/package.nix (nix-init does this automatically). For hello, this would be pkgs/by-name/he/hello/package.nix.
However, nix-init is only a template generation tool. Do not submit the generated definition as-is; always ensure you have manually verified the following items before submitting.
Check if __structuredAttrs = true; is set
The Nixpkgs stdenv documentation clearly states:
All new top level packages must enable
__structuredAttrs.
In other words, all new top-level packages must enable __structuredAttrs.
When enabled, derivation attributes are passed to the builder as a JSON file and a shell script. List-type attributes (such as ldflags and cmakeFlags) are expanded as arrays in bash, allowing values containing spaces to be passed safely. On the other hand, since attributes you want to "export" as environment variables must be defined under the env attribute, you need to be careful when reusing existing templates that pass values like CGO_ENABLED directly.
If you generated the package with the latest version of nix-init, this is included by default. However, it is easy to miss when reusing older templates, so check it just in case.
Register versionCheckHook
The template generated by nix-init does not include versionCheckHook by default. For CLI packages, it is safer to register versionCheckHook to verify that the build artifact actually identifies itself with the correct version.
versionCheckHook is a hook that adds versionCheckPhase to preInstallCheckHooks. It passes --version or --help to the built binary and checks if the ${version} string is included in the output. If it is not found, the build will fail.
{
lib,
stdenv,
versionCheckHook,
# ...
}:
stdenv.mkDerivation (finalAttrs: {
# ...
doInstallCheck = true;
nativeInstallCheckInputs = [ versionCheckHook ];
# ...
})
If meta.mainProgram is set, that executable will be automatically checked. Be careful with programs that require environment variables like PATH or HOME. You may need to adjust versionCheckProgramArg and versionCheckKeepEnvironment, or consider using testers.testVersion as an alternative.
Reference: https://github.com/NixOS/nixpkgs/blob/master/doc/hooks/versionCheckHook.section.md
Keep arguments in order of appearance
It is customary to list the function arguments of a derivation in the order they appear in the function body. The output of nix-init basically follows this order. However, it can accidentally get out of sync when you add buildInputs or other items later. This is a point frequently pointed out in reviews, so check and align them visually from top to bottom before submitting your PR.
{
lib, # lib.* is used first in the function body, so it comes first
stdenv, # next is stdenv.mkDerivation
fetchFromGitHub, # next is src using fetchFromGitHub
versionCheckHook,
nix-update-script,
}:
Verify that Go ldflags are correct
Many Go projects embed version strings into the binary at build time using the -ldflags "-X main.Version=..." format. Check the repository's Makefile or goreleaser configuration and ensure that equivalent values are passed via ldflags.
{
ldflags = [
"-s"
"-w"
"-X main.Version=${finalAttrs.version}"
"-X main.Commit=${finalAttrs.version}"
];
}
The path of the variable specified by -X varies by project. Sometimes it is main.Version, and other times it is a full path like github.com/owner/repo/cmd.version, so always check the original repository's build script. If this is incorrect, versionCheckHook will see an empty string and fail, so it is efficient to check both at the same time.
Reference: https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/go.section.md#var-go-ldflags
Verify meta.description is written correctly
meta.description has strict rules defined in pkgs/README.md.
- Be concise and write in a single sentence
- Start with a capital letter
- Do not start with definite (the) or indefinite (a/an) articles
- Do not start with the package name
- Generally, do not mention the package name
- Do not end with a period (or other punctuation)
- Provide factual information
- Avoid subjective expressions
If you copy-paste a sentence directly from the original repository's README, it is highly likely to violate one of these rules. In particular, subjective or promotional expressions like "A blazing fast ..." and terminal periods are frequently pointed out in reviews, so it is safer to re-read them before submission.
For more details, see https://github.com/NixOS/nixpkgs/blob/master/pkgs/README.md#meta-attributes
Also, by checking the following regarding meta at the same time, you can reduce the number of review cycles:
- Does
meta.licensematch the license of the original repository? - Is
meta.homepageset? - Is
meta.mainProgramset? (Basically required for CLIs with only one executable) - Am I included in
meta.maintainers? (Required for new packages)
Verify that commits follow the rules
Commit conventions are written in https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#commit-conventions and https://github.com/NixOS/nixpkgs/blob/master/pkgs/README.md#commit-conventions.
For new package additions, the commit message should follow this format:
hello: init at 0.1.0
- Add a
(pkg-name):prefix at the beginning (important for CI automatic build targeting) - Do not end with a period
- Consolidate into one commit per logical unit (use
squash)
When reflecting feedback from a review, do not stack new commits. Instead, consolidate into the existing commit using git commit --amend or git rebase -i, and then use git push --force-with-lease. By doing this, the final merged history is kept clean as a single commit.
If it is your first contribution, you need to register your information in maintainers/maintainer-list.nix, so there will be two commits. There are also rules for the order and message in this case.
maintainers: add <handle>
hello: init at 0.1.0
- Place the commit adding yourself as a maintainer first
- The message should be
maintainers: add <handle>
Reference: https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#commit-conventions
Discussion