# 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: ```bash 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 1. Create directory: `pkgs/your-package/` 2. Write `default.nix` with package definition 3. 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 ```nix { 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 ```nix { 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 ```nix { 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 ```nix { 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 ```nix { 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 ```nix { 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: ```nix { 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: ```nix # 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: ```nix src = fetchFromGitHub { owner = "author"; repo = "my-app"; rev = "v${version}"; hash = lib.fakeHash; # Temporary placeholder }; ``` Build the package: ```bash nix build .#my-new-package ``` Copy the actual hash from the error message and update the package: ```nix hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; # Real hash ``` **Important**: Never commit `lib.fakeHash` to the repository. ## Testing ### Build Package ```bash nix build .#my-new-package ``` ### Test Execution ```bash nix run .#my-new-package -- --help ``` ### Run in Shell ```bash nix shell .#my-new-package my-new-app --version ``` ### Linting ```bash nix develop statix check pkgs/my-new-package/ ``` ## Best Practices ### Meta Fields Always include complete `meta` information: ```nix 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: ```nix { lib, stdenv, fetchFromGitHub, # Runtime dependencies openssl, curl, # Native build dependencies pkg-config, cmake, }: ``` ### Versioning Use `rec` to reference `version` in multiple places: ```nix rec { pname = "my-app"; version = "1.0.0"; src = fetchFromGitHub { rev = "v${version}"; # ... }; } ``` ### Platform Restrictions If package is platform-specific: ```nix 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: ```nix { 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: ```nix pythonImportsCheck = ["mymodule"]; # Check this is correct # Or if importing creates side effects: pythonImportsCheck = []; ``` ## Examples See existing packages in the repository: - `pkgs/code2prompt/` - Rust package - `pkgs/mem0/` - Python package - `pkgs/hyprpaper-random/` - Shell script - `pkgs/msty-studio/` - AppImage - `pkgs/zellij-ps/` - Fetch from Gitea ## Next Steps - [Architecture](../ARCHITECTURE.md) - Understanding package organization - [Using Modules](./using-modules.md) - If you need to create modules - [Contributing](../CONTRIBUTING.md) - Code style and guidelines