Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:02:30 +08:00
commit e03e44248d
17 changed files with 785 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
---
name: Nix Packaging
description: Package new software or update existing packages using Nix
---
# Overview
Create new Nix packages or update existing ones, with language-specific examples and guidance.
## Workflow: Creating a New Package
1. Identify the software source: internal to this repo or external (e.g., a GitHub repository).
2. Determine the programming language(s) used.
3. Analyze the software structure (build system, dependencies, configuration files).
4. Create the package following language-specific guidance below.
5. Optionally, integrate the package into the current project (e.g., add to overlay and expose via `packages` in `flake.nix`).
6. Test iteratively: run `nix build .#<package-name>`, read errors, fix issues, and rebuild until successful.
## Workflow: Updating an Existing Package
Typically, update the `version` and source fetching attributes (e.g., `fetchFromGitHub`). The `hash` field must also be updated using one of these methods:
**Method 1: Calculate the new hash directly**
```bash
# Get the hash
nix-prefetch-url --type sha256 --unpack https://github.com/owner/repo/archive/refs/tags/v<NEW_VERSION>.tar.gz
# Convert to SRI format
nix hash convert --hash-algo sha256 <old-hash>
```
**Method 2: Let Nix tell you the hash**
Set `hash = "";` and run the build. The error message will display the correct hash.
For language-specific update steps, see the references below.
# Language-Specific Packaging Skills
- [Python](./python/python.md) - Packaging Python modules
- [Rust](./rust/rust.md) - Packaging Rust applications
- [JavaScript/TypeScript](./js/js.md) - Packaging npm applications

View File

@@ -0,0 +1,52 @@
{
lib,
buildNpmPackage,
fetchzip,
nodejs_20,
}:
buildNpmPackage rec {
pname = "claude-code";
version = "2.0.21";
nodejs = nodejs_20; # required for sandboxed Nix builds on Darwin
src = fetchzip {
url = "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-${version}.tgz";
hash = "sha256-sX9btcy9uEHloAQNvCJFhwh0U/W14NWz2FjkdLXm1Q0=";
};
npmDepsHash = "sha256-LBc1M3FwWg6SP+p7GQ00LQfiYyhmU1OzsohDu6rukjA=";
postPatch = ''
cp ${./package-lock.json} package-lock.json
'';
dontNpmBuild = true;
AUTHORIZED = "1";
# `claude-code` tries to auto-update by default, this disables that functionality.
# https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview#environment-variables
# The DEV=true env var causes claude to crash with `TypeError: window.WebSocket is not a constructor`
postInstall = ''
wrapProgram $out/bin/claude \
--set DISABLE_AUTOUPDATER 1 \
--unset DEV
'';
passthru.updateScript = ./update.sh;
meta = {
description = "Agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster";
homepage = "https://github.com/anthropics/claude-code";
downloadPage = "https://www.npmjs.com/package/@anthropic-ai/claude-code";
license = lib.licenses.unfree;
maintainers = with lib.maintainers; [
malo
markus1189
omarjatoi
];
mainProgram = "claude";
};
}

View File

@@ -0,0 +1,102 @@
# Package a JavaScript/TypeScript Application with Nix (npm)
Packaging an npm application (`buildNpmPackage`) involves determining the package name, version, and dependencies. The package must be available on the npm registry.
## Normal npm Package
See the example: [claude-code](./claude-code/package.nix)
Key components:
- **Source**: Fetch from npm registry using `fetchzip` with URL pattern:
```nix
url = "https://registry.npmjs.org/@scope/package-name/-/package-name-${version}.tgz";
```
For non-scoped packages, omit the `@scope/` part.
- **Dependencies**: Set `npmDepsHash` (see update workflow below for obtaining the hash)
- **package-lock.json**: Use `postPatch` to copy a local `package-lock.json`:
```nix
postPatch = ''
cp ${./package-lock.json} package-lock.json
'';
```
- **Build control**: Use `dontNpmBuild = true;` if the package doesn't require a build step (already built on npm registry)
## Updating an Existing npm Package
Besides the general update steps, also update npm-specific fields:
1. **Update package-lock.json**: Generate a new lock file for the target version:
```bash
# Find the latest version (or specific version)
npm i --package-lock-only @anthropic-ai/claude-code@"$version"
# Remove package.json (not needed for Nix packaging)
rm -f package.json
```
**Note**: If `npm` is not installed, use `nix-shell` to run it:
```bash
nix-shell -p nodejs --run "npm i --package-lock-only @anthropic-ai/claude-code@\"$version\""
rm -f package.json
```
Copy the generated `package-lock.json` to your package directory.
2. **Update hashes**: Both `hash` (source) and `npmDepsHash` need updating.
**Method 1: Let Nix tell you**
- Set `hash = "";` and run `nix build`. Copy the correct hash from the error message.
- Set `npmDepsHash = "";` and run `nix build`. Copy the correct hash from the error message.
**Method 2: Calculate directly**
```bash
# For source hash
nix-prefetch-url --type sha256 --unpack https://registry.npmjs.org/@scope/package/-/package-${version}.tgz
nix hash convert --hash-algo sha256 <old-hash>
```
3. **Check for breaking changes**: New versions may:
- Change build requirements (update `dontNpmBuild` or add build dependencies)
- Require different Node.js versions (update `nodejs` attribute, e.g., `nodejs_20`)
- Add new environment variables or runtime requirements
## Common Patterns
### Specify Node.js Version
For compatibility, especially on Darwin with sandboxed builds:
```nix
buildNpmPackage rec {
nodejs = nodejs_20;
# ...
}
```
### Disable Auto-Build
If the package is pre-built on the npm registry:
```nix
dontNpmBuild = true;
```
### Set Environment Variables
Use `postInstall` to wrap the binary with required environment variables:
```nix
postInstall = ''
wrapProgram $out/bin/your-binary \
--set ENV_VAR value \
--unset UNWANTED_VAR
'';
```
### Handle Optional Dependencies
npm packages often have platform-specific optional dependencies (like Sharp's native bindings). These are automatically handled by `buildNpmPackage` based on the `package-lock.json`.

View File

@@ -0,0 +1,76 @@
{
lib,
buildPythonPackage,
fetchFromGitHub,
setuptools,
docstring-parser,
gitpython,
httpx,
pydantic-settings,
pydantic,
python-dotenv,
python-multipart,
pyyaml,
rich,
tomli,
typer,
typing-extensions,
# optional
tantivy,
pylance,
lancedb,
qdrant-client,
unstructured,
markdown,
aiofiles,
}:
buildPythonPackage rec {
pname = "agno";
version = "1.7.11";
pyproject = true;
src = fetchFromGitHub {
owner = "agno-agi";
repo = "agno";
rev = "v${version}";
hash = "sha256-9oO4qyYCgMnC1jtFr39Y76t/5/ybUGIhECP+PLhm92s=";
};
sourceRoot = "${src.name}/libs/agno";
build-system = [ setuptools ];
dependencies = [
docstring-parser
gitpython
httpx
pydantic-settings
pydantic
python-dotenv
python-multipart
pyyaml
rich
tomli
typer
typing-extensions
];
optional-dependencies = {
lancedb = [ lancedb tantivy ];
pylance = [ pylance ]; # Useful for lancedb "hybrid" search
qdrant = [ qdrant-client ];
markdown = [ unstructured markdown aiofiles ];
};
pythonImportsCheck = [ "agno" ];
meta = {
description = "Full-stack framework for building Multi-Agent Systems with memory, knowledge and reasoning";
homepage = "https://github.com/agno-agi/agno";
license = lib.licenses.mpl20;
maintainers = with lib.maintainers; [ ];
mainProgram = "agno";
platforms = lib.platforms.all;
};
}

View File

@@ -0,0 +1,61 @@
{ lib, buildPythonPackage, fetchFromGitHub, pythonRelaxDepsHook, setuptools
, holidays, joblib, matplotlib, nfoursid, numpy, pandas, pmdarima, pyod
, requests, scikit-learn, scipy, shap
# , statsforecast
, statsmodels, tbats, tqdm, typing-extensions, xarray, xgboost
, pytorch-lightning }:
buildPythonPackage rec {
pname = "darts";
version = "0.31.0";
format = "pyproject";
src = fetchFromGitHub {
owner = "unit8co";
repo = pname;
rev = version;
hash = "sha256-piSYRJIFr3RQTt/idfTRrqx/dD794He4d2F9flBJv7Q=";
};
nativeBuildInputs = [ pythonRelaxDepsHook ];
buildInputs = [ setuptools ];
propagatedBuildInputs = [
holidays
joblib
matplotlib
nfoursid
numpy
pandas
pmdarima
pyod
requests
scikit-learn
scipy
shap
# statsforecast
statsmodels
tbats
tqdm
typing-extensions
xarray
xgboost
pytorch-lightning
];
pythonRelaxDeps = [ "pmdarima" ];
pythonRemoveDeps = [ "statsforecast" ];
pythonImportsCheck = [ "darts" ];
meta = with lib; {
description = ''
A python library for user-friendly forecasting and anomaly detection on time series
'';
homepage = "https://unit8co.github.io/darts/";
license = licenses.asl20;
maintainers = with maintainers; [ breakds ];
};
}

View File

@@ -0,0 +1,47 @@
# Package a Python Module with Nix
Packaging a Python module (`buildPythonPackage`) involves determining the source, dependencies, and build system, typically from `pyproject.toml`. Ensure all dependencies are already packaged before referencing them.
More complex cases require special handling. Below are examples for different scenarios.
## Normal Python Module
See the simple example: [tyro](./tyro/package.nix)
## Customize Build System and Disable Tests
Some Python modules require additional build tools, or have tests that cannot run in the Nix sandbox. See [Swanboard](./swanboard/package.nix) for an example.
## Customize Source Root and Optional Dependencies
See [agno](./agno/package.nix) for examples of:
- Using `sourceRoot` when the Python module's root differs from the project root (specify the relative path).
- Handling optional dependencies: include them in `optional-dependencies` when available, or safely omit them if not yet packaged.
## Rust-Backed Python Module
For Python packages implemented in Rust with Python bindings, use `rustPlatform` as a helper. See [tantivy](./tantivy/package.nix) for an example.
## Relax or Remove Dependencies
When dependency requirements cannot be satisfied, investigate further:
1. **Version mismatch**: If the dependency is packaged but the version requirement is too strict, add it to `pythonRelaxDeps` to bypass version checking.
2. **Missing dependency**: If the dependency is not packaged, assess whether it's essential for your use case. If not, add it to `pythonRemoveDeps`.
See [darts](./darts/package.nix) for an example.
## Updating an Existing Python Package
Besides the general update steps, also:
1. **Update dependencies**: Check `pyproject.toml` or `setup.py` for changed dependencies and update:
- `dependencies` - Runtime dependencies
- `build-system` - Build-time dependencies (e.g., setuptools, poetry-core)
- `optional-dependencies` - If relevant to your use case
2. **Handle breaking changes**: New versions may:
- Drop Python version support (update `pythonOlder` or `disabled`)
- Change build backends (update `build-system` based on `pyproject.toml`)
- Add new test dependencies (update `nativeCheckInputs` or `checkInputs`)

View File

@@ -0,0 +1,44 @@
{ lib, buildPythonPackage, pythonOlder, fetchFromGitHub, pytestCheckHook
, hatchling, hatch-fancy-pypi-readme, hatch-requirements-txt, swankit, fastapi
, uvicorn, peewee, ujson, psutil, pyyaml, setuptools, nanoid, numpy }:
# TODO(breakds): Build the UI. It seemed pretty straight forward but
# for some reason I will run into this "dead spiral" of fetchYarnDeps
# always complain about a changed yarn.lock (and hash).
buildPythonPackage rec {
pname = "swanboard";
version = "0.1.7-beta.1";
format = "pyproject";
disabled = pythonOlder "3.8";
src = fetchFromGitHub {
owner = "SwanHubX";
repo = "SwanLab-Dashboard";
rev = "v${version}";
hash = "sha256-jBYlBJaEZPJ2tORfeSUnTpwyAjENh8QYTfVb6o2UNZg=";
};
build-system =
[ hatchling hatch-fancy-pypi-readme hatch-requirements-txt setuptools ];
dependencies = [ swankit fastapi uvicorn peewee ujson psutil pyyaml ];
pythonImportsCheck = [ "swanboard" ];
nativeCheckInputs = [ pytestCheckHook nanoid numpy ];
disabledTests = [
"test_get_package_version_installed"
"test_get_package_version_not_installed"
# Temporarily disable because there is a small bug that needs to be fixed.
"TestExperiment"
];
meta = with lib; {
description = "Swanlab's Dashboard";
homepage = "https://github.com/SwanHubX/SwanLab-Dashboard";
license = licenses.asl20;
maintainers = with maintainers; [ breakds ];
};
}

View File

@@ -0,0 +1,39 @@
{
lib
, buildPythonPackage
, fetchFromGitHub
, rustPlatform
}:
buildPythonPackage rec {
pname = "tantivy";
version = "0.25.0";
format = "pyproject";
src = fetchFromGitHub {
owner = "quickwit-oss";
repo = "tantivy-py";
tag = version;
hash = "sha256-ZVQOzKojBf7yNkgiOV4huNnuxCmiFwJb610sD4M2/MU=";
};
cargoDeps = rustPlatform.fetchCargoVendor {
inherit pname version src;
hash = "sha256-/OADcVm01PbHp3bcw62Zt6+9ZmT96Bic+EBbPUhdoOI=";
};
build-system = with rustPlatform; [ cargoSetupHook maturinBuildHook ];
pythonImportsCheck = [ "tantivy" ];
meta = {
description = ''
Python bindings for Tantivy; Tantivy is a full-text search engine library
inspired by Apache Lucene and written in Rust
'';
homepage = "https://github.com/quickwit-oss/tantivy-py";
changeLog = "https://github.com/quickwit-oss/tantivy-py/releases/tag/${version}";
license = lib.licenses.mit;
maintainers = with lib.maintainers; [ breakds ];
};
}

View File

@@ -0,0 +1,27 @@
{ lib, buildPythonPackage, fetchFromGitHub, setuptools, docstring-parser
, typing-extensions, rich, shtab, typeguard, hatchling }:
buildPythonPackage rec {
pname = "tyro";
version = "0.9.9";
format = "pyproject";
src = fetchFromGitHub {
owner = "brentyi";
repo = pname;
rev = "v${version}";
hash = "sha256-iFKgnKd4606S/hEMHD7ZaTnGF16gmvbaE62nifw4o7c=";
};
build-system = [ hatchling ];
dependencies = [ docstring-parser typing-extensions rich shtab typeguard ];
pythonImportsCheck = [ "tyro" ];
meta = with lib; {
description = "tool for generating CLI interfaces in Python";
homepage = "https://brentyi.github.io/tyro";
license = licenses.mit;
};
}

View File

@@ -0,0 +1,74 @@
{
lib,
stdenv,
rustPlatform,
fetchFromGitHub,
installShellFiles,
nix-update-script,
pkg-config,
openssl,
versionCheckHook,
installShellCompletions ? stdenv.buildPlatform.canExecute stdenv.hostPlatform,
}:
rustPlatform.buildRustPackage (finalAttrs: {
pname = "codex";
version = "0.46.0";
src = fetchFromGitHub {
owner = "openai";
repo = "codex";
tag = "rust-v${finalAttrs.version}";
hash = "sha256-o898VjjPKevr1VRlRhJUNWsrHEGEn7jkdzWBj+DpbCs=";
};
sourceRoot = "${finalAttrs.src.name}/codex-rs";
cargoHash = "sha256-Qp5zezXjVdOp8OylLgUZRLc0HQlgII6nOZodnOrok6U=";
nativeBuildInputs = [
installShellFiles
pkg-config
];
buildInputs = [ openssl ];
# NOTE: part of the test suite requires access to networking, local shells,
# apple system configuration, etc. since this is a very fast moving target
# (for now), with releases happening every other day, constantly figuring out
# which tests need to be skipped, or finding workarounds, was too burdensome,
# and in practice not adding any real value. this decision may be reversed in
# the future once this software stabilizes.
doCheck = false;
postInstall = lib.optionalString installShellCompletions ''
installShellCompletion --cmd codex \
--bash <($out/bin/codex completion bash) \
--fish <($out/bin/codex completion fish) \
--zsh <($out/bin/codex completion zsh)
'';
doInstallCheck = true;
nativeInstallCheckInputs = [ versionCheckHook ];
passthru = {
updateScript = nix-update-script {
extraArgs = [
"--version-regex"
"^rust-v(\\d+\\.\\d+\\.\\d+)$"
];
};
};
meta = {
description = "Lightweight coding agent that runs in your terminal";
homepage = "https://github.com/openai/codex";
changelog = "https://raw.githubusercontent.com/openai/codex/refs/tags/rust-v${finalAttrs.version}/CHANGELOG.md";
license = lib.licenses.asl20;
mainProgram = "codex";
maintainers = with lib.maintainers; [
malo
delafthi
];
platforms = lib.platforms.unix;
};
})

View File

@@ -0,0 +1,14 @@
# Packaging Rust Applications with Nix
See [codex](./codex/package.nix) for an example.
## Updating an Existing Rust Package
Besides the general update steps, also update the `cargoHash`:
```bash
# Set cargoHash = ""; then run nix build
# Nix will fail and show: "got: sha256-XXXXX"
```
If `Cargo.toml` dependencies changed, the build will automatically fetch new dependencies after you update the `cargoHash`.