Packages

Package Ecosystem & Customization

A multi-layered package management strategy combining standard Nixpkgs with local extensions and overlays.

📦 The Package Hierarchy

This repository manages packages through three distinct layers:

1. Nixpkgs (The Base)

We primarily track nixos-unstable for the latest hardware support and software features. Standard packages are accessed via the global pkgs attribute.

2. Local Packages (hey.packages)

Custom derivations located in the packages/ directory are automatically scanned and exposed under the hey.packages namespace.

  • Exposure: Handled by flake.nix and lib/nixos.nix.
  • Purpose: Ideal for small utilities (like betterfox) that aren’t in upstream Nixpkgs or require specialized versions.
  • Usage: Always prefer hey.packages.name for files inside this repo to ensure reproducibility.

3. Overlays (overlays/)

Overlays allow us to modify existing packages in Nixpkgs globally.

  • Mechanism: Every file in overlays/ is automatically imported and applied to the pkgs set.
  • Example: Overriding a kernel version for specific hardware or fixing a broken library across all applications.

🛠️ Custom Packages Logic

The mapModules Pipeline

We use a custom library function to auto-load packages:

packages = mapModules ./packages import;

This means adding a new package is as simple as creating a folder in packages/ with a default.nix.

Versions & Determinism

For local packages that fetch from GitHub (like betterfox), we use fetchFromGitHub with explicit commit hashes and sha256 SRI hashes. This guarantees that your environment stays identical regardless of when or where it is built.


🏗️ Stable vs. Unstable

Our flake includes a nixpkgs-stable input. In rare cases where a package is broken on unstable, we can access the stable version via pkgs.pkgs-stable.<name>. This is implemented as a global overlay in lib/nixos.nix.


🛠️ Developer: Local Package Lifecycle

When adding or updating a package in packages/, follow this “Surgical Iteration” workflow to avoid slow rebuilds.

1. Isolated Build Test

Don’t run hey sync to test a package change. Build it in isolation using a temporary expression:

nix-build -E 'with import <nixpkgs> {}; callPackage ./packages/your-pkg/default.nix { self = {}; }'

Note: If the package requires specific overrides (like rofi-wayland), pass them in the attribute set.

2. Idiomatic Patching: substituteInPlace

Avoid using .patch files for simple path redirections. Patches are brittle and break when upstream line numbers change. Use substituteInPlace in the postPatch phase:

postPatch = ''
  substituteInPlace configure.ac \
    --replace-fail '[PLUGIN_DIR]="`$PKG_CONFIG --variable=pluginsdir rofi`"' \
                   '[PLUGIN_DIR]="$out/lib/rofi"'
'';

This is “Search & Replace” for your source code, making it resilient to version bumps.

3. Pruning Broken Upstream Logic

If an upstream update breaks secondary components (like a test suite or documentation generator), don’t revert. Prune it in prePatch:

prePatch = ''
  # Skip building broken tests
  sed -i 's/SUBDIRS = src tests/SUBDIRS = src/' Makefile.am
  sed -i 's/tests\/Makefile//' configure.ac
'';

🚀 Adding a Local Package

  1. Create packages/<your-pkg>/default.nix.
  2. Write a standard Nix derivation.
  3. It will be immediately available as hey.packages.<your-pkg> in any module.
  4. If you want it available globally as just <your-pkg>, add an overlay in overlays/.