9.2 KiB
Adding Packages Guide
How to add new packages to m3ta-nixpkgs.
Overview
Packages in m3ta-nixpkgs are organized using a callPackage registry pattern. Each package lives in its own directory and is registered centrally.
Quick Start
Using Templates
Use the package template for quick setup:
nix flake init -t .#package my-new-package
This creates a template structure in templates/package/ that you can copy.
Automatic Updates
Important: This repository uses automated package updates via Gitea Actions and nix-update. When adding a new package:
- Use any stable, working version for the initial package
- You don't need to use the absolute latest version
- The automation will keep the package updated automatically on a weekly basis
- Review and merge automated update PRs as they come in
See the main README.md for more details on the automated update workflow.
Manual Setup
- Create directory:
pkgs/your-package/ - Write
default.nixwith package definition - Register in
pkgs/default.nix
Package Structure
pkgs/your-package/
├── default.nix # Package definition (required)
├── source.py # Optional: additional source files
├── wrapper.sh # Optional: wrapper scripts
└── README.md # Optional: package documentation
Common Patterns
Rust Package
{
lib,
rustPlatform,
fetchFromGitHub,
}:
rustPlatform.buildRustPackage rec {
pname = "my-rust-app";
version = "1.0.0";
src = fetchFromGitHub {
owner = "author";
repo = "my-rust-app";
rev = "v${version}";
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
cargoLock.lockFile = src + "/Cargo.lock";
buildInputs = [openssl];
nativeBuildInputs = [pkg-config];
meta = with lib; {
description = "My Rust application";
homepage = "https://github.com/author/my-rust-app";
license = licenses.mit;
platforms = platforms.linux;
mainProgram = "my-rust-app";
};
}
Python Package
{
lib,
python3,
fetchFromGitHub,
}:
python3.pkgs.buildPythonPackage rec {
pname = "my-python-app";
version = "1.0.0";
pyproject = true;
src = fetchFromGitHub {
owner = "author";
repo = "my-python-app";
rev = "v${version}";
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
build-system = with python3.pkgs; [setuptools];
dependencies = with python3.pkgs; [
requests
click
];
optional-dependencies = with python3.pkgs; {
extras = [pyyaml];
};
doCheck = true;
pythonImportsCheck = ["myapp"];
meta = with lib; {
description = "My Python application";
homepage = "https://github.com/author/my-python-app";
license = licenses.mit;
platforms = platforms.linux;
mainProgram = "my-app";
};
}
Shell Script Package
{
lib,
writeShellScriptBin,
}:
writeShellScriptBin "my-script" ''
#!/usr/bin/env bash
set -euo pipefail
echo "Hello from my script!"
# Your script logic here
''
# If you need to add dependencies
{
lib,
writeShellApplication,
bash,
curl,
}:
writeShellApplication {
name = "my-script";
runtimeInputs = [bash curl];
text = ''
#!/usr/bin/env bash
set -euo pipefail
curl -s https://example.com
'';
}
AppImage Package
{
lib,
appimageTools,
fetchurl,
}:
appimageTools.wrapType2 rec {
name = "my-app";
version = "1.0.0";
src = fetchurl {
url = "https://github.com/author/my-app/releases/download/v${version}/My-App-${version}.AppImage";
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
meta = with lib; {
description = "My AppImage application";
homepage = "https://github.com/author/my-app";
license = licenses.unfree;
platforms = platforms.linux;
mainProgram = name;
};
}
Custom Source with Patch
{
lib,
stdenv,
fetchFromGitHub,
fetchpatch,
buildGoModule,
}:
buildGoModule rec {
pname = "my-go-app";
version = "1.0.0";
src = fetchFromGitHub {
owner = "author";
repo = "my-go-app";
rev = "v${version}";
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
patches = [
# Add local patch
./fix-build.patch
# Add patch from URL
(fetchpatch {
url = "https://github.com/author/my-app/pull/123.patch";
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
})
];
vendorHash = "sha256-BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=";
meta = with lib; {
description = "My Go application";
homepage = "https://github.com/author/my-go-app";
license = licenses.mit;
platforms = platforms.linux;
mainProgram = "my-app";
};
}
Package with Custom Installation
{
lib,
stdenv,
fetchFromGitHub,
makeWrapper,
}:
stdenv.mkDerivation rec {
pname = "my-app";
version = "1.0.0";
src = fetchFromGitHub {
owner = "author";
repo = "my-app";
rev = "v${version}";
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
nativeBuildInputs = [makeWrapper];
buildPhase = ''
make build
'';
installPhase = ''
install -Dm755 my-app $out/bin/my-app
# Wrap with runtime dependencies
wrapProgram $out/bin/my-app \
--prefix PATH : ${lib.makeBinPath [some-dep]}
'';
meta = with lib; {
description = "My custom application";
homepage = "https://github.com/author/my-app";
license = licenses.mit;
platforms = platforms.linux;
mainProgram = "my-app";
};
}
Registration
Register in pkgs/default.nix
Add your package to the registry:
{
inherit (pkgs) callPackage;
} rec {
# Existing packages
code2prompt = callPackage ./code2prompt {};
zellij-ps = callPackage ./zellij-ps {};
# Your new package
my-new-package = callPackage ./my-new-package {};
}
With Custom Arguments
If your package needs custom arguments:
# pkgs/default.nix
{
inherit (pkgs) callPackage;
} rec {
my-new-package = callPackage ./my-new-package {
customArg = "value";
};
}
# pkgs/my-new-package/default.nix
{
lib,
stdenv,
fetchurl,
customArg, # This will be passed from the registry
}:
stdenv.mkDerivation {
# ...
}
Getting Hashes
Using lib.fakeHash
During development, use lib.fakeHash to get the real hash:
src = fetchFromGitHub {
owner = "author";
repo = "my-app";
rev = "v${version}";
hash = lib.fakeHash; # Temporary placeholder
};
Build the package:
nix build .#my-new-package
Copy the actual hash from the error message and update the package:
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; # Real hash
Important: Never commit lib.fakeHash to the repository.
Testing
Build Package
nix build .#my-new-package
Test Execution
nix run .#my-new-package -- --help
Run in Shell
nix shell .#my-new-package
my-new-app --version
Linting
nix develop
statix check pkgs/my-new-package/
Best Practices
Meta Fields
Always include complete meta information:
meta = with lib; {
description = "Short one-line description";
longDescription = ''
Longer description explaining what the package does,
its features, and use cases.
'';
homepage = "https://github.com/author/repo";
changelog = "https://github.com/author/repo/releases/tag/v${version}";
license = licenses.mit;
platforms = platforms.linux;
mainProgram = "program-name";
};
Dependencies
Explicitly declare all dependencies:
{
lib,
stdenv,
fetchFromGitHub,
# Runtime dependencies
openssl,
curl,
# Native build dependencies
pkg-config,
cmake,
}:
Versioning
Use rec to reference version in multiple places:
rec {
pname = "my-app";
version = "1.0.0";
src = fetchFromGitHub {
rev = "v${version}";
# ...
};
}
Platform Restrictions
If package is platform-specific:
meta = with lib; {
# Linux only
platforms = platforms.linux;
# Or specific platforms
platforms = ["x86_64-linux" "aarch64-linux"];
# Or exclude platforms
broken = stdenv.isDarwin;
};
Troubleshooting
Hash Mismatch
Error: got: sha256-AAAAAAAA... expected: sha256-BBBBBB...
Solution: Copy got hash and update package definition.
Dependency Not Found
Error: error: undefined variable 'somedep'
Solution: Add dependency to function arguments and build inputs:
{
lib,
somedep, # Add here
}:
stdenv.mkDerivation {
buildInputs = [somedep]; # Add here
}
Import Check Failure
Error: error: Python module 'mymodule' not found
Solution: Disable or fix imports check:
pythonImportsCheck = ["mymodule"]; # Check this is correct
# Or if importing creates side effects:
pythonImportsCheck = [];
Examples
See existing packages in the repository:
pkgs/code2prompt/- Rust packagepkgs/mem0/- Python packagepkgs/hyprpaper-random/- Shell scriptpkgs/msty-studio/- AppImagepkgs/zellij-ps/- Fetch from Gitea
Next Steps
- Architecture - Understanding package organization
- Using Modules - If you need to create modules
- Contributing - Code style and guidelines