From fb5db6e30240dcec16a5b9c64ca2ce446a09580e Mon Sep 17 00:00:00 2001 From: Alexander Miroshnichenko Date: Wed, 29 Apr 2026 16:04:58 +0300 Subject: [PATCH] Initialize Nix overlay repository with blueprint, goose-cli, and docs Add the complete overlay structure using numtide/blueprint with: - Two overlay strategies (default and shared-nixpkgs) - goose-cli package with custom librusty_v8 fetcher - Interactive package launcher via fzf - Flake input caching utility - Comprehensive README and AGENTS.md documentation --- AGENTS.md | 273 +++++++++++++++++++++++++++++ README.md | 127 +++++++++++++- flake.lock | 140 +++++++++++++++ flake.nix | 51 ++++++ overlays/default.nix | 6 + overlays/shared-nixpkgs.nix | 10 ++ packages/default/default.nix | 19 ++ packages/default/package.nix | 60 +++++++ packages/flake-inputs/default.nix | 12 ++ packages/goose-cli/default.nix | 9 + packages/goose-cli/fetchers.nix | 21 +++ packages/goose-cli/librusty_v8.nix | 13 ++ packages/goose-cli/package.nix | 71 ++++++++ packages/goose-cli/update.py | 107 +++++++++++ 14 files changed, 918 insertions(+), 1 deletion(-) create mode 100644 AGENTS.md create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 overlays/default.nix create mode 100644 overlays/shared-nixpkgs.nix create mode 100644 packages/default/default.nix create mode 100644 packages/default/package.nix create mode 100644 packages/flake-inputs/default.nix create mode 100644 packages/goose-cli/default.nix create mode 100644 packages/goose-cli/fetchers.nix create mode 100644 packages/goose-cli/librusty_v8.nix create mode 100644 packages/goose-cli/package.nix create mode 100755 packages/goose-cli/update.py diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..ed7a4bf --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,273 @@ +# AGENTS.md - Instructions for AI Agents + +This file provides guidance to AI agents (such as Claude, GPT, Goose, etc.) when working with this Nix overlay repository. + +## Project Overview + +This is a **Nix flake overlay** repository that provides additional packages for Nix/NixOS users. It uses [numtide/blueprint](https://github.com/numtide/blueprint) as its foundation, which simplifies flake development by providing sensible defaults and automatic package discovery. + +**Repository URL**: `git+https://git.millerson.name/alex/millerson-overlay.nix.git` + +## Key Concepts + +### Blueprint Framework + +This project uses `numtide/blueprint` which: +- Automatically discovers packages in the `packages/` directory +- Provides `perSystem` outputs for multi-system builds +- Handles formatting, checking, and devShell generation +- Exposes `mkPackagesFor` function to build packages against any nixpkgs instance + +### Two Overlay Strategies + +1. **`overlays.default`** - Binary-cache-friendly + - Uses packages built against this flake's nixpkgs revision + - Better for binary cache hits when using the same nixpkgs + +2. **`overlays.shared-nixpkgs`** - Dependency-sharing + - Builds packages against the consumer's nixpkgs (`final`) + - Shares dependencies with the rest of the system + - No second nixpkgs evaluation + - Trade-off: binary cache only hits when consumer's nixpkgs matches ours + +## Repository Structure + +``` +nix-overlay/ +├── flake.nix # Main flake definition with inputs and outputs +├── overlays/ +│ ├── default.nix # Overlay using pre-built packages (binary cache friendly) +│ └── shared-nixpkgs.nix # Overlay building against consumer's nixpkgs +├── packages/ +│ ├── default/ # Meta-package listing all visible packages +│ │ ├── default.nix +│ │ └── package.nix +│ ├── goose-cli/ # Example package: Goose AI agent CLI +│ │ ├── default.nix +│ │ ├── package.nix # Main package definition +│ │ ├── fetchers.nix # Custom fetchers (if needed) +│ │ ├── librusty_v8.nix # V8 library pre-built binary +│ │ └── update.py # Update script for version bumps +│ └── flake-inputs/ # Utility to cache all flake inputs +│ └── default.nix +├── README.md # User-facing documentation +├── AGENTS.md # This file - AI agent instructions +├── LICENSE +└── flake.lock +``` + +## Package Definition Pattern + +### Standard Package Structure + +Each package should follow this structure: + +``` +packages// +├── default.nix # Wrapper that receives blueprint args (pkgs, perSystem, etc.) +└── package.nix # Actual package definition using pkgs.callPackage pattern +``` + +### `default.nix` Pattern + +```nix +{ + pkgs, + perSystem, + ... +}: +pkgs.callPackage ./package.nix { + # Pass any extra arguments here +} +``` + +### `package.nix` Pattern + +```nix +{ + lib, + stdenv, + # ... package-specific dependencies +}: + +stdenv.mkDerivation rec { + pname = "my-package"; + version = "1.0.0"; + + src = fetchFromGitHub { + owner = "user"; + repo = "repo"; + rev = "v${version}"; + hash = "sha256-..."; + }; + + # ... build instructions + + meta = with lib; { + description = "Brief description"; + homepage = "https://..."; + license = licenses.mit; + maintainers = with maintainers; [ ]; + platforms = platforms.all; + mainProgram = "program-name"; + }; +} +``` + +### Rust Packages + +For Rust packages, use `rustPlatform.buildRustPackage`: + +```nix +{ + lib, + rustPlatform, + fetchFromGitHub, + ... +}: + +rustPlatform.buildRustPackage rec { + pname = "my-rust-app"; + version = "1.0.0"; + + src = fetchFromGitHub { ... }; + + cargoHash = "sha256-..."; + + # ... build instructions + + meta = { ... }; +} +``` + +## Adding a New Package + +When adding a new package: + +1. **Create the directory**: `mkdir -p packages/` + +2. **Create `package.nix`** with the actual derivation + +3. **Create `default.nix`** as a wrapper: + ```nix + { pkgs, ... }: + pkgs.callPackage ./package.nix { } + ``` + +4. **Test the package**: + ```bash + nix build .# + nix run .# + ``` + +5. **Update README.md** to document the new package in the Available Packages table + +6. **Set appropriate metadata**: + - `meta.description` - Required, shown in package listings + - `meta.mainProgram` - Set for packages providing a CLI executable + - `meta.passthru.category` - Optional, used for organization (e.g., "AI Coding Agents") + - `meta.passthru.hideFromDocs` - Set to `true` to exclude from documentation + +## Common Tasks + +### Updating a Package Version + +1. Update `version` in `package.nix` +2. Update `src.hash` (use `nix-prefetch-github` or let Nix tell you the correct hash) +3. Update `cargoHash` for Rust packages (build will fail and tell you the correct hash) +4. Test: `nix build .#` +5. Update README if needed + +### Testing Changes + +```bash +# Build specific package +nix build .#goose-cli + +# Build all packages for current system +nix build .#packages + +# Enter dev shell +nix develop + +# Check formatting +nix flake check +``` + +### Working with Overlays + +When modifying overlay behavior: + +- **`overlays/default.nix`**: Receives `packages` from blueprint outputs, maps them to `final.stdenv.hostPlatform.system` +- **`overlays/shared-nixpkgs.nix`**: Receives `mkPackagesFor`, uses it to build against consumer's `final` + +## Important Notes + +### Do's + +- ✅ Use `pkgs.callPackage` pattern for package definitions +- ✅ Set proper `meta` attributes (description, license, homepage) +- ✅ Test packages with `nix build` before committing +- ✅ Use `passthru.category` for organizational grouping +- ✅ Follow Nixpkgs conventions for package naming and structure + +### Don'ts + +- ❌ Don't modify `flake.lock` manually - use `nix flake update` +- ❌ Don't hardcode system-specific paths or assumptions +- ❌ Don't forget to set `meta.mainProgram` for CLI tools +- ❌ Don't use `with pkgs;` at top level (can cause scope issues) +- ❌ Don't commit packages that don't build + +## Debugging Tips + +### Package Doesn't Build + +1. Check the error message carefully +2. Verify all dependencies are listed +3. Check if the source hash is correct +4. For Rust packages, verify `cargoHash` matches + +### Overlay Not Working + +1. Verify the overlay is correctly imported +2. Check if the package exists in `packages` output +3. For `shared-nixpkgs`, ensure `mkPackagesFor` is available + +### Binary Cache Issues + +- The `default` overlay is more likely to get cache hits +- The `shared-nixpkgs` overlay builds from source unless nixpkgs revisions match + +## Resources + +- [Nixpkgs Manual](https://nixos.org/manual/nixpkgs/stable/) +- [Blueprint Documentation](https://github.com/numtide/blueprint) +- [Nix Flake Patterns](https://github.com/NixOS/flake-patterns) +- [Rust in Nixpkgs](https://nixos.org/manual/nixpkgs/stable/#rust) + +## Example Workflow: Adding a New Package + +```bash +# 1. Create package directory +mkdir -p packages/my-tool + +# 2. Create package.nix (use appropriate template above) + +# 3. Create default.nix wrapper +cat > packages/my-tool/default.nix << 'EOF' +{ pkgs, ... }: +pkgs.callPackage ./package.nix { } +EOF + +# 4. Test build +nix build .#my-tool + +# 5. If build fails, fix issues, then retry + +# 6. Update README.md with new package info + +# 7. Commit changes +git add packages/my-tool README.md +git commit -m "Add my-tool package" +``` diff --git a/README.md b/README.md index e1e1ef5..88325a3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,127 @@ -# nix-overlay +# millerson.name Nix Overlay +A custom Nix overlay and flake providing additional packages not found in upstream nixpkgs, built using [numtide/blueprint](https://github.com/numtide/blueprint) for streamlined flake development. + +## Features + +- **Custom packages** - Additional software packaged for Nix +- **Two overlay strategies** - Choose between binary-cache-friendly or dependency-sharing overlays +- **Blueprint-based** - Uses modern Nix flake patterns with `numtide/blueprint` + +## Available Packages + +| Package | Description | Category | +|---------|-------------|----------| +| `goose-cli` | CLI for Goose - a local, extensible, open source AI agent that automates engineering tasks | AI Coding Agents | + +## Usage + +### As a Flake + +Add to your `flake.nix` inputs: + +```nix +inputs.millerson-nix-overlay.url = "git+https://git.millerson.name/alex/millerson-overlay.nix.git"; +``` + +Then use in your outputs: + +```nix +outputs = { nixpkgs, millerson-nix-overlay, ... }: { + nixosConfigurations.your-host = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [ + millerson-nix-overlay.nixosModules.default + # ... your other modules + ]; + }; +}; +``` + +### As an Overlay + +#### Option 1: Default Overlay (Binary Cache Friendly) + +Uses the packages built against this flake's nixpkgs revision. Binary cache hits work best when using the same nixpkgs revision. + +```nix +nixpkgs.overlays = [ millerson-nix-overlay.overlays.default ]; +``` + +#### Option 2: Shared Nixpkgs Overlay (Dependency Sharing) + +Builds packages against your system's nixpkgs, sharing dependencies with the rest of your system. No second nixpkgs evaluation, but binary cache only hits when your nixpkgs revision matches ours. + +```nix +nixpkgs.overlays = [ millerson-nix-overlay.overlays.shared-nixpkgs ]; +``` + +### Installing Packages + +With flakes enabled: + +```bash +# Try a package +nix run git+https://git.millerson.name/alex/millerson-overlay.nix.git#goose-cli + +# Install permanently +nix profile install git+https://git.millerson.name/alex/millerson-overlay.nix.git#goose-cli +``` + +## Development + +### Prerequisites + +- Nix with flakes enabled +- [treefmt-nix](https://github.com/numtide/treefmt-nix) for formatting (optional) + +### Building Packages + +```bash +# Build all packages for current system +nix build .#packages + +# Build specific package +nix build .#goose-cli + +# Enter development shell +nix develop +``` + +### Project Structure + +``` +nix-overlay/ +├── flake.nix # Flake definition +├── overlays/ # Overlay implementations +│ ├── default.nix # Binary-cache-friendly overlay +│ └── shared-nixpkgs.nix # Dependency-sharing overlay +├── packages/ # Package definitions +│ ├── default/ # Meta-package listing all packages +│ ├── goose-cli/ # Goose CLI package +│ └── flake-inputs/ # Utility for caching flake inputs +└── README.md +``` + +### Adding New Packages + +1. Create a new directory under `packages//` +2. Add a `package.nix` with the package definition +3. Create a `default.nix` that imports `package.nix` with proper arguments +4. The package will be automatically picked up by blueprint + +Example structure: + +``` +packages/my-package/ +├── default.nix # Import wrapper +└── package.nix # Actual package definition +``` + +## License + +See [LICENSE](LICENSE) file for details. + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..7219697 --- /dev/null +++ b/flake.lock @@ -0,0 +1,140 @@ +{ + "nodes": { + "blueprint": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "systems": [ + "systems" + ] + }, + "locked": { + "lastModified": 1776249299, + "narHash": "sha256-Dt9t1TGRmJFc0xVYhttNBD6QsAgHOHCArqGa0AyjrJY=", + "owner": "numtide", + "repo": "blueprint", + "rev": "56131e8628f173d24a27f6d27c0215eff57e40dd", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "blueprint", + "type": "github" + } + }, + "bun2nix": { + "inputs": { + "flake-parts": [ + "flake-parts" + ], + "nixpkgs": [ + "nixpkgs" + ], + "systems": [ + "systems" + ], + "treefmt-nix": [ + "treefmt-nix" + ] + }, + "locked": { + "lastModified": 1777369708, + "narHash": "sha256-1xW7cRZNsFNPQD+cE0fwnLVStnDth0HSoASEIFeT7uI=", + "owner": "nix-community", + "repo": "bun2nix", + "rev": "e659e1cc4b8e1b21d0aa85f1c481f9db61ecfa98", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "staging-2.1.0", + "repo": "bun2nix", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775087534, + "narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1777270315, + "narHash": "sha256-yKB4G6cKsQsWN7M6rZGk6gkJPDNPIzT05y4qzRyCDlI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6368eda62c9775c38ef7f714b2555a741c20c72d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "blueprint": "blueprint", + "bun2nix": "bun2nix", + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs", + "systems": "systems", + "treefmt-nix": "treefmt-nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775636079, + "narHash": "sha256-pc20NRoMdiar8oPQceQT47UUZMBTiMdUuWrYu2obUP0=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "790751ff7fd3801feeaf96d7dc416a8d581265ba", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..6ed398c --- /dev/null +++ b/flake.nix @@ -0,0 +1,51 @@ +{ + description = "Various packages for Nix"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + systems.url = "github:nix-systems/default"; + blueprint = { + url = "github:numtide/blueprint"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.systems.follows = "systems"; + }; + treefmt-nix = { + url = "github:numtide/treefmt-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + flake-parts = { + url = "github:hercules-ci/flake-parts"; + inputs.nixpkgs-lib.follows = "nixpkgs"; + }; + bun2nix = { + # TODO(#4001): drop the branch pin once catalog support + # (nix-community/bun2nix#86) reaches the default branch. + url = "github:nix-community/bun2nix/staging-2.1.0"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.systems.follows = "systems"; + inputs.treefmt-nix.follows = "treefmt-nix"; + inputs.flake-parts.follows = "flake-parts"; + }; + }; + + outputs = + inputs: + let + blueprintOutputs = inputs.blueprint { + inherit inputs; + nixpkgs.config.allowUnfree = true; + }; + + in + blueprintOutputs + // { + overlays = { + default = import ./overlays { + inherit (blueprintOutputs) packages; + }; + shared-nixpkgs = import ./overlays/shared-nixpkgs.nix { + inherit (blueprintOutputs) mkPackagesFor; + }; + }; + }; +} diff --git a/overlays/default.nix b/overlays/default.nix new file mode 100644 index 0000000..30130c3 --- /dev/null +++ b/overlays/default.nix @@ -0,0 +1,6 @@ +{ + packages, +}: +final: _prev: { + millerson-nix-overlay = packages.${final.stdenv.hostPlatform.system} or { }; +} diff --git a/overlays/shared-nixpkgs.nix b/overlays/shared-nixpkgs.nix new file mode 100644 index 0000000..be59c5f --- /dev/null +++ b/overlays/shared-nixpkgs.nix @@ -0,0 +1,10 @@ +{ + mkPackagesFor, +}: +# Builds the packages/ tree against the consumer's `final`, so deps are +# shared with the rest of their system and no second nixpkgs is +# evaluated. Trade-off vs overlays.default: the binary cache only hits +# when the consumer's nixpkgs revision matches ours. +final: _prev: { + millerson-nix-overlay = mkPackagesFor final; +} diff --git a/packages/default/default.nix b/packages/default/default.nix new file mode 100644 index 0000000..afb0cd5 --- /dev/null +++ b/packages/default/default.nix @@ -0,0 +1,19 @@ +{ + pkgs, + perSystem, + ... +}: +let + allPackages = perSystem.self; + + # Filter to visible, runnable packages + visibleNames = builtins.filter ( + name: name != "default" && !(allPackages.${name}.passthru.hideFromDocs or false) + ) (builtins.attrNames allPackages); + + # Build "name\tdescription" lines + packageLines = map (name: "${name}\t${allPackages.${name}.meta.description or ""}") visibleNames; + + packageList = builtins.concatStringsSep "\n" packageLines; +in +pkgs.callPackage ./package.nix { inherit packageList; } diff --git a/packages/default/package.nix b/packages/default/package.nix new file mode 100644 index 0000000..ccf9144 --- /dev/null +++ b/packages/default/package.nix @@ -0,0 +1,60 @@ +{ + lib, + writeShellApplication, + fzf, + nix, + util-linux, + packageList, +}: + +let + packageListFile = builtins.toFile "millerson-overlay-packages.tsv" packageList; +in +writeShellApplication { + name = "millerson-overlay-launcher"; + + runtimeInputs = [ + fzf + nix + util-linux # column + ]; + + text = '' + # Format for fzf: "name description" (tab-aligned) + entries=$(column -t -s $'\t' < "${packageListFile}") + + if [[ -z $entries ]]; then + echo "No packages found" >&2 + exit 1 + fi + + # Let user pick with fzf + selected=$(echo "$entries" | fzf \ + --header="Select an pkg to run (ESC to cancel)" \ + --preview-window=hidden \ + --no-multi \ + --height=~40% \ + --layout=reverse) || exit 0 + + # Extract package name (first word) + pkg_name=$(echo "$selected" | awk '{print $1}') + + if [[ -z $pkg_name ]]; then + exit 0 + fi + + echo "→ Running: nix run git.millerson.name/alex/nix-overlay.git#$pkg_name" + exec nix run "git.millerson.name/alex/nix-overlay.git#$pkg_name" + ''; + + meta = with lib; { + description = "Interactive fzf launcher for millerson-overlay.nix packages"; + license = licenses.mit; + mainProgram = "millerson-overlay-launcher"; + platforms = platforms.all; + }; + + passthru = { + hideFromDocs = true; + }; +} diff --git a/packages/flake-inputs/default.nix b/packages/flake-inputs/default.nix new file mode 100644 index 0000000..5b3d81e --- /dev/null +++ b/packages/flake-inputs/default.nix @@ -0,0 +1,12 @@ +{ + inputs, + pkgs, + ... +}: +# A derivation that references all flake inputs to ensure they get cached +pkgs.runCommand "flake-inputs" { } '' + echo ${pkgs.lib.concatMapStringsSep " " (name: inputs.${name}) (builtins.attrNames inputs)} > $out +'' +// { + passthru.hideFromDocs = true; +} diff --git a/packages/goose-cli/default.nix b/packages/goose-cli/default.nix new file mode 100644 index 0000000..550923f --- /dev/null +++ b/packages/goose-cli/default.nix @@ -0,0 +1,9 @@ +{ + pkgs, + ... +}: +pkgs.callPackage ./package.nix { + librusty_v8 = pkgs.callPackage ./librusty_v8.nix { + inherit (pkgs.callPackage ./fetchers.nix { }) fetchLibrustyV8; + }; +} diff --git a/packages/goose-cli/fetchers.nix b/packages/goose-cli/fetchers.nix new file mode 100644 index 0000000..7decccd --- /dev/null +++ b/packages/goose-cli/fetchers.nix @@ -0,0 +1,21 @@ +# Fetchers for goose-cli pre-built dependencies +# Based on deno's approach for handling rusty_v8 +{ + lib, + stdenv, + fetchurl, +}: + +{ + fetchLibrustyV8 = + args: + fetchurl { + name = "librusty_v8-${args.version}"; + url = "https://github.com/denoland/rusty_v8/releases/download/v${args.version}/librusty_v8_release_${stdenv.hostPlatform.rust.rustcTarget}.a.gz"; + sha256 = args.shas.${stdenv.hostPlatform.system}; + meta = { + inherit (args) version; + sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ]; + }; + }; +} diff --git a/packages/goose-cli/librusty_v8.nix b/packages/goose-cli/librusty_v8.nix new file mode 100644 index 0000000..be5e4ce --- /dev/null +++ b/packages/goose-cli/librusty_v8.nix @@ -0,0 +1,13 @@ +# Pre-built librusty_v8 library for goose-cli +# This file specifies the rusty_v8 version and hashes for all supported platforms +{ fetchLibrustyV8 }: + +fetchLibrustyV8 { + version = "145.0.0"; + shas = { + x86_64-linux = "sha256-chV1PAx40UH3Ute5k3lLrgfhih39Rm3KqE+mTna6ysE="; + aarch64-linux = "sha256-4IivYskhUSsMLZY97+g23UtUYh4p5jk7CzhMbMyqXyY="; + x86_64-darwin = "sha256-1jUuC+z7saQfPYILNyRJanD4+zOOhXU2ac/LFoytwho="; + aarch64-darwin = "sha256-yHa1eydVCrfYGgrZANbzgmmf25p7ui1VMas2A7BhG6k="; + }; +} diff --git a/packages/goose-cli/package.nix b/packages/goose-cli/package.nix new file mode 100644 index 0000000..5b40757 --- /dev/null +++ b/packages/goose-cli/package.nix @@ -0,0 +1,71 @@ +{ + lib, + fetchFromGitHub, + rustPlatform, + pkg-config, + openssl, + libxcb, + dbus, + versionCheckHook, + librusty_v8, +}: + +rustPlatform.buildRustPackage rec { + pname = "goose-cli"; + version = "1.33.1"; + + src = fetchFromGitHub { + owner = "block"; + repo = "goose"; + rev = "v${version}"; + hash = "sha256-FBICGOfVs2jbOdLWSInqfTYBdnCcbcGWHwqY/b6v8eg="; + }; + + cargoHash = "sha256-fN0FKDYFkZrQQPWdUlemOaGzIAZhqFyskz9TEmG+X4o="; + + nativeBuildInputs = [ pkg-config ]; + + buildInputs = [ + openssl + libxcb + dbus + ]; + + # The v8 package will try to download a `librusty_v8.a` release at build time to our read-only filesystem + # To avoid this we pre-download the file and export it via RUSTY_V8_ARCHIVE + env.RUSTY_V8_ARCHIVE = librusty_v8; + + # Build only the CLI package + cargoBuildFlags = [ + "--package" + "goose-cli" + ]; + + # Enable tests with proper environment + doCheck = true; + checkPhase = '' + export HOME=$(mktemp -d) + export XDG_CONFIG_HOME=$HOME/.config + export XDG_DATA_HOME=$HOME/.local/share + export XDG_STATE_HOME=$HOME/.local/state + export XDG_CACHE_HOME=$HOME/.cache + mkdir -p $XDG_CONFIG_HOME $XDG_DATA_HOME $XDG_STATE_HOME $XDG_CACHE_HOME + + # Run tests for goose-cli package only + cargo test --package goose-cli --release + ''; + + doInstallCheck = true; + nativeInstallCheckInputs = [ versionCheckHook ]; + + passthru.category = "AI Coding Agents"; + + meta = with lib; { + description = "CLI for Goose - a local, extensible, open source AI agent that automates engineering tasks"; + homepage = "https://github.com/block/goose"; + changelog = "https://github.com/block/goose/releases/tag/v${version}"; + license = licenses.asl20; + sourceProvenance = with sourceTypes; [ fromSource ]; + mainProgram = "goose"; + }; +} diff --git a/packages/goose-cli/update.py b/packages/goose-cli/update.py new file mode 100755 index 0000000..687dc0e --- /dev/null +++ b/packages/goose-cli/update.py @@ -0,0 +1,107 @@ +#!/usr/bin/env nix +#! nix shell --inputs-from .# nixpkgs#python3 --command python3 + +"""Update script for goose-cli package. + +This script updates both the goose-cli version and the librusty_v8 hashes. +The v8 version is extracted from the Cargo.lock file of the goose repository. +""" + +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent.parent.parent / "scripts")) + +from updater import ( + calculate_platform_hashes, + fetch_text, + load_hashes, + save_hashes, +) + +HASHES_FILE = Path(__file__).parent / "librusty_v8_hashes.json" + +PLATFORMS = { + "x86_64-linux": "x86_64-unknown-linux-gnu", + "aarch64-linux": "aarch64-unknown-linux-gnu", + "x86_64-darwin": "x86_64-apple-darwin", + "aarch64-darwin": "aarch64-apple-darwin", +} + + +def fetch_v8_version_from_cargo_lock(goose_version: str) -> str: + """Extract the v8 version from goose's Cargo.lock file.""" + url = f"https://raw.githubusercontent.com/block/goose/v{goose_version}/Cargo.lock" + cargo_lock = fetch_text(url) + + # Parse the Cargo.lock to find v8 version + lines = cargo_lock.split("\n") + for i, line in enumerate(lines): + if line.strip() == 'name = "v8"': + # Look for version in the next few lines + for j in range(i + 1, min(i + 10, len(lines))): + if "version = " in lines[j]: + return lines[j].split('"')[1] + + msg = "Could not find v8 version in Cargo.lock" + raise ValueError(msg) + + +def main() -> None: + """Update the librusty_v8 hashes for goose-cli.""" + # Read the current goose-cli version from package.nix + package_nix = (Path(__file__).parent / "package.nix").read_text() + for line in package_nix.split("\n"): + if "version = " in line and '"' in line: + goose_version = line.split('"')[1] + break + else: + msg = "Could not find version in package.nix" + raise ValueError(msg) + + print(f"Goose version: {goose_version}") + + # Get the v8 version from Cargo.lock + v8_version = fetch_v8_version_from_cargo_lock(goose_version) + print(f"V8 version: {v8_version}") + + # Check if we need to update + try: + data = load_hashes(HASHES_FILE) + current_v8 = data.get("version", "") + if current_v8 == v8_version: + print(f"V8 hashes already up to date ({v8_version})") + return + except FileNotFoundError: + print("No existing hashes file, creating new one") + + # Calculate hashes for all platforms + url_template = f"https://github.com/denoland/rusty_v8/releases/download/v{v8_version}/librusty_v8_release_{{platform}}.a.gz" + hashes = calculate_platform_hashes(url_template, PLATFORMS) + + # Save the hashes + save_hashes(HASHES_FILE, {"version": v8_version, "hashes": hashes}) + + # Update librusty_v8.nix + librusty_v8_nix = Path(__file__).parent / "librusty_v8.nix" + content = f"""# Pre-built librusty_v8 library for goose-cli +# This file specifies the rusty_v8 version and hashes for all supported platforms +{{ fetchLibrustyV8 }}: + +fetchLibrustyV8 {{ + version = "{v8_version}"; + shas = {{ + x86_64-linux = "{hashes["x86_64-linux"]}"; + aarch64-linux = "{hashes["aarch64-linux"]}"; + x86_64-darwin = "{hashes["x86_64-darwin"]}"; + aarch64-darwin = "{hashes["aarch64-darwin"]}"; + }}; +}} +""" + librusty_v8_nix.write_text(content) + + print(f"Updated librusty_v8 to {v8_version}") + + +if __name__ == "__main__": + main()