Suffix

Building a Nix package

How to package a simple Go application for NixOS.

I was looking for the bluewalker BLE scanner application on NixOS and it wasn’t available yet. A great opportunity to learn how to package a simple command line application in Nix! First, check if the package does not exist yet. Next, check the nixpkgs repo open issues to make sure it’s not already being worked on. Nothing there so let’s give it a shot.

I’ll use my bluewalker package pull request as an example.

Clone the NixOS package repository and create a new folder to store the application’s configuration. Add a ‘default.nix’ file in that folder, this is where we’ll add the package’s build instructions.

git clone --depth 1 git@github.com:nixos/nixpkgs.git /tmp/nixpkgs
mkdir /tmp/nixpkgs/pkgs/tools/bluetooth/bluewalker
touch /tmp/nixpkgs/pkgs/tools/bluetooth/bluewalker/default.nix

I used an existing Golang config as a base and modified it a bit. Most options make perfect sense, the ldflags are optional but should make the resulting binary a little smaller by removing some debug info.

{ lib, buildGoModule, fetchFromGitLab }:
buildGoModule rec {
  pname = "bluewalker";
  version = "0.3.0";
  src = fetchFromGitLab {
    owner = "jtaimisto";
    repo = pname;
    rev = "v${version}";
    sha256 = "sha256-spuJRiNiaBV4EsetUq8vUfR6ejUNZxLhVzS3AZZyrrQ=";
  };
  vendorSha256 = "189qs6vmx63vwsjmc4qgf1y8xjsi7x6l1f5c3kd8j8jnagl26z4h";
  ldflags = [
    "-w"
    "-s"
  ];
  meta = with lib; {
    description = "Simple command line Bluetooth LE scanner";
    homepage = "https://gitlab.com/jtaimisto/bluewalker";
    changelog = "https://gitlab.com/jtaimisto/bluewalker/-/tags/v${version}";
    license = licenses.bsd2;
    maintainers = with maintainers; [ cimm ];
    platforms = platforms.linux;
  };
}
↑ Example default.nix for the bluewalker package.

The configuration includes two SHA-256 hashes, which was the most cryptic part at first. Nix will calculate the hash of the downloaded source to make sure it matches what I had while packaging, a security measure. Both hashes need to be calculated and added to the config.

The first, the one in the ‘fetchFromGitLab’ section, is the most complex. To calculate this hash, I downloaded the source code in a temporary folder, making sure to use the exact version with the git tag. I removed the ‘.git’ folder and used the ‘nix hash-path’ command to calculate the hash. I then added the resulting hash to the default.nix configuration.

git clone --branch "v0.3.0" https://gitlab.com/jtaimisto/bluewalker.git /tmp/bluewalker
rm -r /tmp/bluewalker/.git
nix hash-path /tmp/bluewalker

Since the bluewalker’s Go modules are not vendored I added the ‘vendorSha256’ hash as well. For this hash I built the package a first time with ‘vendorSha256=lib.fakeSha256;’ as temporary SHA-256 value. The build failed and had the expected hash in the output. I copied the correct SHA-256 hash and built the package again.

nix-build -E 'with import <nixpkgs> {}; callPackage ./default.nix {}'

This worked. I could now run ‘/etc/nixos/result/bin/bluewalker -h’ to test if the application worked. Great! Finally, I added the newly built package to my main ‘configuration.nix’.

environment.systemPackages = [ pkgs.bluewalker ];

And build NixOS with the local package repository.

nixos-rebuild switch -I nixpkgs=/tmp/nixpkgs

Done! Well, almost, since I took the time adding the application, I might as well be a good open-source citizen and contribute the changes back to the main Nix package repository. Who knows, it might even be included so others can install the application more easily. I linked the package in the ‘pkgs/top-level/all-packages.nix’ file and added myself to the ‘maintainers/maintainer-list.nix’ file before opening a new pull request.