feat: add Nix flake for reproducible development environment

This commit is contained in:
randomizedcoder dave.seddon.ca@gmail.com 2026-01-17 14:25:24 -08:00
parent 802b40a790
commit c5fa177bd6
5 changed files with 455 additions and 5 deletions

5
.gitignore vendored
View file

@ -41,6 +41,11 @@ logs
# Turborepo
.turbo
# Nix
result
result-*
.direnv
# IntelliJ and Qodo plugin folders
.idea/
.qodo/

View file

@ -6,6 +6,7 @@
- Fix: Reset invalid model selection when using OpenAI Codex provider (PR #10777 by @hannesrudolph)
- Fix: Add openai-codex to providers that don't require an API key (PR #10786 by @roomote)
- Fix: Detect Gemini models with space-separated names for proper thought signature injection in LiteLLM (PR #10787 by @daniel-lxs)
- Add Nix flake for reproducible development environment with Node.js, pnpm, and native build dependencies
## [3.41.1] - 2026-01-16

View file

@ -35,7 +35,7 @@
- [简体中文](locales/zh-CN/README.md)
- [繁體中文](locales/zh-TW/README.md)
- ...
</details>
</details>
---
@ -66,10 +66,10 @@ Learn more: [Using Modes](https://docs.roocode.com/basic-usage/using-modes) •
<div align="center">
| | | |
| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| <a href="https://www.youtube.com/watch?v=Mcq3r1EPZ-4"><img src="https://img.youtube.com/vi/Mcq3r1EPZ-4/maxresdefault.jpg" width="100%"></a><br><b>Installing Roo Code</b> | <a href="https://www.youtube.com/watch?v=ZBML8h5cCgo"><img src="https://img.youtube.com/vi/ZBML8h5cCgo/maxresdefault.jpg" width="100%"></a><br><b>Configuring Profiles</b> | <a href="https://www.youtube.com/watch?v=r1bpod1VWhg"><img src="https://img.youtube.com/vi/r1bpod1VWhg/maxresdefault.jpg" width="100%"></a><br><b>Codebase Indexing</b> |
| <a href="https://www.youtube.com/watch?v=iiAv1eKOaxk"><img src="https://img.youtube.com/vi/iiAv1eKOaxk/maxresdefault.jpg" width="100%"></a><br><b>Custom Modes</b> | <a href="https://www.youtube.com/watch?v=Ho30nyY332E"><img src="https://img.youtube.com/vi/Ho30nyY332E/maxresdefault.jpg" width="100%"></a><br><b>Checkpoints</b> | <a href="https://www.youtube.com/watch?v=HmnNSasv7T8"><img src="https://img.youtube.com/vi/HmnNSasv7T8/maxresdefault.jpg" width="100%"></a><br><b>Context Management</b> |
| | | |
| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| <a href="https://www.youtube.com/watch?v=Mcq3r1EPZ-4"><img src="https://img.youtube.com/vi/Mcq3r1EPZ-4/maxresdefault.jpg" width="100%"></a><br><b>Installing Roo Code</b> | <a href="https://www.youtube.com/watch?v=ZBML8h5cCgo"><img src="https://img.youtube.com/vi/ZBML8h5cCgo/maxresdefault.jpg" width="100%"></a><br><b>Configuring Profiles</b> | <a href="https://www.youtube.com/watch?v=r1bpod1VWhg"><img src="https://img.youtube.com/vi/r1bpod1VWhg/maxresdefault.jpg" width="100%"></a><br><b>Codebase Indexing</b> |
| <a href="https://www.youtube.com/watch?v=iiAv1eKOaxk"><img src="https://img.youtube.com/vi/iiAv1eKOaxk/maxresdefault.jpg" width="100%"></a><br><b>Custom Modes</b> | <a href="https://www.youtube.com/watch?v=Ho30nyY332E"><img src="https://img.youtube.com/vi/Ho30nyY332E/maxresdefault.jpg" width="100%"></a><br><b>Checkpoints</b> | <a href="https://www.youtube.com/watch?v=HmnNSasv7T8"><img src="https://img.youtube.com/vi/HmnNSasv7T8/maxresdefault.jpg" width="100%"></a><br><b>Context Management</b> |
</div>
<p align="center">
@ -149,6 +149,32 @@ If you prefer to install the VSIX package manually:
code --install-extension bin/roo-cline-<version>.vsix
```
### Nix Flake
A `flake.nix` is provided for [Nix](https://nixos.org/) users:
```sh
nix develop # Enter dev shell with all dependencies
pnpm install # Install npm packages
pnpm build # Build the project
pnpm test # Run tests
pnpm vsix # Create VSIX package
```
To build and install the extension directly:
```sh
nix build .#vsix && code --install-extension result/*.vsix
```
The dev shell provides Node.js 20.x, pnpm, ripgrep, and build tools for native modules.
After updating dependencies, run `nix run .#update-deps` to update `flake.lock` and the pnpm dependency hash.
If flakes aren't enabled, prefix commands with: `nix --extra-experimental-features 'nix-command flakes' <command>`
> **Note:** Roo Code is also available as a pre-built extension in [nixpkgs](https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/applications/editors/vscode/extensions/rooveterinaryinc.roo-cline/default.nix) via `vscode-extensions.rooveterinaryinc.roo-cline`.
---
We use [changesets](https://github.com/changesets/changesets) for versioning and publishing. Check our `CHANGELOG.md` for release notes.

61
flake.lock generated Normal file
View file

@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1768564909,
"narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

357
flake.nix Normal file
View file

@ -0,0 +1,357 @@
# Roo Code Nix Flake
#
# Provides a reproducible development environment
#
# Usage:
# nix develop # Enter dev shell
# nix run # Show welcome banner
# nix fmt # Format nix files
# nix flake check # Run all checks (lint, typecheck, test)
#
# Build and install extension:
# nix build .#vsix && code --install-extension result/*.vsix
#
# Inside dev shell:
# pnpm install # Install npm packages
# pnpm build # Build the project
# pnpm test # Run tests
# pnpm vsix # Create VS Code extension
#
# Maintenance:
# nix run .#update-deps # Update flake.lock and pnpmDeps hash
#
# If flakes aren't enabled, prefix commands with:
# nix --extra-experimental-features 'nix-command flakes' <command>
#
# See README.md section "Nix Flake" for more information
{
description = "Roo Code - AI-Powered Dev Team, Right in Your Editor";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
{
self,
nixpkgs,
flake-utils,
}:
let
versionFromPackageJson = (builtins.fromJSON (builtins.readFile ./src/package.json)).version;
in
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
lib = pkgs.lib;
nodejs = pkgs.nodejs_20;
pnpm = pkgs.pnpm;
ignoredPaths = [
".direnv"
"result"
"result-dev"
"node_modules"
".turbo"
"dist"
"bin"
".vscode"
".cursor"
];
src = lib.cleanSourceWith {
src = lib.cleanSource ./.;
filter =
path: type:
let
baseName = builtins.baseNameOf path;
in
!(builtins.elem baseName ignoredPaths);
};
chromiumPath =
if pkgs.stdenv.isDarwin then
"${pkgs.chromium}/Applications/Chromium.app/Contents/MacOS/Chromium"
else
"${pkgs.chromium}/bin/chromium";
basePnpmAttrs = {
inherit src;
strictDeps = true;
nativeBuildInputs = [
nodejs
pnpm
pkgs.pnpmConfigHook
];
pnpmDeps = self.packages.${system}.pnpmDeps;
};
roo-code-welcome = pkgs.writeShellApplication {
name = "roo-code-welcome";
runtimeInputs = [
pkgs.jp2a
nodejs
pnpm
];
text = ''
echo ""
if [ -f "./src/assets/images/roo.png" ]; then
jp2a --colors --width=40 ./src/assets/images/roo.png 2>/dev/null || echo "🦘 Roo Code"
else
echo "🦘 Roo Code Development Environment"
fi
echo ""
echo ""
echo "Node.js: $(node --version)"
echo "pnpm: $(pnpm --version)"
echo ""
echo "📦 pnpm install - Install dependencies"
echo "🔧 pnpm build - Build the project"
echo "📋 pnpm test - Run tests"
echo "📦 pnpm vsix - Create VS Code extension"
echo ""
echo "For VS Code debugging: Press F5"
echo ""
'';
};
mkPnpmCheck =
name: script: extraAttrs:
pkgs.stdenvNoCC.mkDerivation (
basePnpmAttrs
// {
name = "roo-code-${name}";
buildPhase = ''
runHook preBuild
export HOME=$TMPDIR
# Disable pnpm self-management (pnpm is provided by Nix)
echo "manage-package-manager-versions=false" >> .npmrc
pnpm run ${script}
runHook postBuild
'';
installPhase = ''
mkdir -p $out
echo "${name} passed" > $out/result
'';
}
// extraAttrs
);
in
{
formatter = pkgs.nixfmt-tree;
devShells.default = pkgs.mkShell {
name = "roo-code-dev";
packages = with pkgs; [
nodejs
pnpm
git
python3
pkg-config
gnumake
turbo
chromium
ripgrep
jp2a
nodePackages.typescript-language-server
nil
];
buildInputs =
with pkgs;
[
openssl
]
++ lib.optionals stdenv.isLinux [
stdenv.cc.cc.lib
];
env = {
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = "1";
PUPPETEER_EXECUTABLE_PATH = chromiumPath;
RIPGREP_PATH = "${pkgs.ripgrep}/bin/rg";
NODE_ENV = "development";
};
shellHook = ''
${lib.getExe roo-code-welcome}
'';
};
packages =
let
version = versionFromPackageJson;
in
{
# Pre-fetch pnpm dependencies (for reproducible builds)
# Update hash when pnpm-lock.yaml changes:
# 1. Set hash = lib.fakeHash;
# 2. Run: nix build .#pnpmDeps
# 3. Copy the "got:" hash from the error message
pnpmDeps = pkgs.fetchPnpmDeps {
pname = "roo-code-pnpm-deps";
inherit version src;
fetcherVersion = 2;
hash = "sha256-AuDK65r2lanMp9sj9JdTljYIQ06goT08AxwcG+LawgI=";
};
vsix = pkgs.stdenvNoCC.mkDerivation (
basePnpmAttrs
// {
pname = "roo-code-vsix";
inherit version;
buildPhase = ''
runHook preBuild
export HOME=$TMPDIR
# Disable pnpm self-management (pnpm is provided by Nix)
echo "manage-package-manager-versions=false" >> .npmrc
pnpm run vsix
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p $out
cp -v bin/*.vsix $out/
runHook postInstall
'';
meta = with lib; {
description = "AI-powered autonomous coding agent that lives in your editor";
homepage = "https://github.com/RooCodeInc/Roo-Code";
license = licenses.asl20;
platforms = platforms.unix;
};
}
);
default = self.packages.${system}.vsix;
};
apps = {
welcome = {
type = "app";
program = lib.getExe roo-code-welcome;
};
build-vsix = {
type = "app";
program = lib.getExe (
pkgs.writeShellApplication {
name = "build-vsix";
text = ''
echo "Building VSIX..."
nix build .#vsix --print-out-paths
'';
}
);
};
update-deps = {
type = "app";
program = lib.getExe (
pkgs.writeShellApplication {
name = "update-deps";
runtimeInputs = [
pkgs.gnused
pkgs.gnugrep
];
text = ''
# Safety check: ensure flake.nix has no uncommitted changes
if ! git diff --quiet flake.nix 2>/dev/null; then
echo "Error: flake.nix has uncommitted changes. Commit or stash them first."
exit 1
fi
echo "==> Updating flake inputs (nixpkgs, flake-utils)..."
nix flake update
echo ""
echo "==> Updating pnpmDeps hash..."
# Set hash to empty to trigger rebuild
sed -i 's|hash = "sha256-[^"]*";|hash = "";|' flake.nix
# Build and capture the error output
if output=$(nix build .#pnpmDeps 2>&1); then
echo "Build succeeded - hash unchanged"
# Restore the original hash since build succeeded
git checkout flake.nix 2>/dev/null || true
exit 0
fi
# Extract the correct hash from "got: sha256-..." (with fallback patterns)
new_hash=$(echo "$output" | grep -oP 'got:\s+\Ksha256-[A-Za-z0-9+/=]+' | head -1)
# Fallback: try alternative pattern if first didn't match
if [ -z "$new_hash" ]; then
new_hash=$(echo "$output" | grep -oE 'sha256-[A-Za-z0-9+/=]+' | tail -1)
fi
if [ -z "$new_hash" ]; then
echo "Error: Could not extract hash from build output"
echo "$output"
# Restore flake.nix since we failed
git checkout flake.nix 2>/dev/null || true
exit 1
fi
# Update flake.nix with the new hash
sed -i "s|hash = \"\";|hash = \"$new_hash\";|" flake.nix
echo "Updated pnpmDeps hash to: $new_hash"
echo ""
echo "Run 'nix fmt' to format, then commit flake.nix and flake.lock"
'';
}
);
};
default = self.apps.${system}.welcome;
};
checks = {
lint = mkPnpmCheck "lint" "lint" { };
typecheck = mkPnpmCheck "typecheck" "check-types" { };
test = mkPnpmCheck "test" "test" {
nativeBuildInputs = basePnpmAttrs.nativeBuildInputs ++ [ pkgs.chromium ];
CI = "true";
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = "1";
PUPPETEER_EXECUTABLE_PATH = chromiumPath;
};
format = pkgs.stdenvNoCC.mkDerivation {
name = "roo-code-format-check";
src = ./.;
strictDeps = true;
nativeBuildInputs = [ pkgs.nixfmt-tree ];
buildPhase = ''
runHook preBuild
nixfmt --check flake.nix
runHook postBuild
'';
installPhase = ''
mkdir -p $out
echo "Format check passed" > $out/result
'';
};
};
}
)
// {
overlays.default = final: prev: {
roo-code-vsix = self.packages.${final.system}.vsix;
};
};
}