diff --git a/.gitignore b/.gitignore index 364b391a01..152c482935 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,11 @@ logs # Turborepo .turbo +# Nix +result +result-* +.direnv + # IntelliJ and Qodo plugin folders .idea/ .qodo/ diff --git a/CHANGELOG.md b/CHANGELOG.md index f92674a5b0..953350f1ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 75f37762f9..fed6f8e5a6 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ - [简体中文](locales/zh-CN/README.md) - [繁體中文](locales/zh-TW/README.md) - ... - + --- @@ -66,10 +66,10 @@ Learn more: [Using Modes](https://docs.roocode.com/basic-usage/using-modes) •
-| | | | -| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -|
Installing Roo Code |
Configuring Profiles |
Codebase Indexing | -|
Custom Modes |
Checkpoints |
Context Management | +| | | | +| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +|
Installing Roo Code |
Configuring Profiles |
Codebase Indexing | +|
Custom Modes |
Checkpoints |
Context Management |

@@ -149,6 +149,32 @@ If you prefer to install the VSIX package manually: code --install-extension bin/roo-cline-.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' ` + +> **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. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000..5a9df81916 --- /dev/null +++ b/flake.lock @@ -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 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000..0ef9a46470 --- /dev/null +++ b/flake.nix @@ -0,0 +1,364 @@ +# 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' +# +# 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); + }; + + # Chromium is only available on Linux in nixpkgs + chromiumPath = if pkgs.stdenv.isLinux then "${pkgs.chromium}/bin/chromium" else null; + + 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 + ripgrep + jp2a + nodePackages.typescript-language-server + nil + ] + ++ lib.optionals stdenv.isLinux [ + chromium + ]; + + buildInputs = + with pkgs; + [ + openssl + ] + ++ lib.optionals stdenv.isLinux [ + stdenv.cc.cc.lib + ]; + + env = { + PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = "1"; + RIPGREP_PATH = "${pkgs.ripgrep}/bin/rg"; + NODE_ENV = "development"; + } + // lib.optionalAttrs pkgs.stdenv.isLinux { + PUPPETEER_EXECUTABLE_PATH = chromiumPath; + }; + + 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" ( + { + CI = "true"; + PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = "1"; + } + // lib.optionalAttrs pkgs.stdenv.isLinux { + nativeBuildInputs = basePnpmAttrs.nativeBuildInputs ++ [ pkgs.chromium ]; + 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; + }; + }; +}