From 10f8563b9a9d3986f558a32d69d1ca218532ea14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Thu, 25 Dec 2025 00:31:33 +0100 Subject: [PATCH 1/5] feat(nix): add desktop app integration --- flake.nix | 18 +++++- nix/desktop.nix | 133 +++++++++++++++++++++++++++++++++++++++++++ nix/node-modules.nix | 25 +++++--- nix/opencode.nix | 8 ++- 4 files changed, 172 insertions(+), 12 deletions(-) create mode 100644 nix/desktop.nix diff --git a/flake.nix b/flake.nix index a6614a5dc9c..224dc95cf1c 100644 --- a/flake.nix +++ b/flake.nix @@ -67,9 +67,9 @@ hash = nodeModulesHash; }; mkPackage = pkgs.callPackage ./nix/opencode.nix { }; - in - { - default = mkPackage { + mkDesktop = pkgs.callPackage ./nix/desktop.nix { }; + + opencodePkg = mkPackage { version = packageJson.version; src = ./.; scripts = ./nix/scripts; @@ -77,6 +77,18 @@ modelsDev = "${modelsDev.${system}}/dist/_api.json"; mkNodeModules = mkNodeModules; }; + + desktopPkg = mkDesktop { + version = packageJson.version; + src = ./.; + scripts = ./nix/scripts; + mkNodeModules = mkNodeModules; + opencode = opencodePkg; + }; + in + { + default = opencodePkg; + desktop = desktopPkg; } ); diff --git a/nix/desktop.nix b/nix/desktop.nix new file mode 100644 index 00000000000..7ae564a6697 --- /dev/null +++ b/nix/desktop.nix @@ -0,0 +1,133 @@ +{ + lib, + stdenv, + rustPlatform, + bun, + pkg-config, + dbus, + openssl, + glib, + gtk3, + libsoup_3, + webkitgtk_4_1, + librsvg, + libappindicator-gtk3, + cargo, + rustc, + makeBinaryWrapper, + nodejs, + jq, +}: +args: +let + scripts = args.scripts; + mkModules = + attrs: + args.mkNodeModules ( + attrs + // { + canonicalizeScript = scripts + "/canonicalize-node-modules.ts"; + normalizeBinsScript = scripts + "/normalize-bun-binaries.ts"; + } + ); +in +rustPlatform.buildRustPackage rec { + pname = "opencode-desktop"; + version = args.version; + + src = args.src; + + # We need to set the root for cargo, but we also need access to the whole repo. + postUnpack = '' + # Update sourceRoot to point to the tauri app + sourceRoot+=/packages/desktop/src-tauri + ''; + + cargoLock = { + lockFile = ../packages/desktop/src-tauri/Cargo.lock; + allowBuiltinFetchGit = true; + }; + + node_modules = mkModules { + version = version; + src = src; + }; + + nativeBuildInputs = [ + pkg-config + bun + makeBinaryWrapper + cargo + rustc + nodejs + jq + ]; + + buildInputs = [ + dbus + openssl + glib + gtk3 + libsoup_3 + webkitgtk_4_1 + librsvg + libappindicator-gtk3 + ]; + + preBuild = '' + # Restore node_modules + pushd ../../.. + + # Copy node_modules from the fixed-output derivation + # We use cp -r --no-preserve=mode to ensure we can write to them if needed, + # though we usually just read. + cp -r ${node_modules}/node_modules . + cp -r ${node_modules}/packages . + + # Fix permissions just in case + chmod -R u+w node_modules packages + # Patch shebangs so scripts can run + patchShebangs node_modules + + # Copy sidecar + mkdir -p packages/desktop/src-tauri/sidecars + targetTriple=${stdenv.hostPlatform.rust.rustcTarget} + cp ${args.opencode}/bin/opencode packages/desktop/src-tauri/sidecars/opencode-cli-$targetTriple + # Merge prod config into tauri.conf.json + jq -s '.[0] * .[1]' packages/desktop/src-tauri/tauri.conf.json packages/desktop/src-tauri/tauri.prod.conf.json > packages/desktop/src-tauri/tauri.conf.json.tmp + mv packages/desktop/src-tauri/tauri.conf.json.tmp packages/desktop/src-tauri/tauri.conf.json + + # Build the frontend + cd packages/desktop + + # The 'build' script runs 'bun run typecheck && vite build'. + bun run build + + popd + ''; + + # Tauri bundles the assets during the rust build phase (which happens after preBuild). + # It looks for them in the location specified in tauri.conf.json. + + postInstall = '' + # Wrap the binary to ensure it finds the libraries + wrapProgram $out/bin/opencode-desktop \ + --prefix LD_LIBRARY_PATH : ${ + lib.makeLibraryPath [ + gtk3 + webkitgtk_4_1 + librsvg + glib + libsoup_3 + ] + } + ''; + + meta = with lib; { + description = "OpenCode Desktop App"; + homepage = "https://opencode.ai"; + license = licenses.mit; + maintainers = with maintainers; [ ]; + mainProgram = "opencode-desktop"; + }; +} diff --git a/nix/node-modules.nix b/nix/node-modules.nix index 7b22ef8e7da..cb1366ee452 100644 --- a/nix/node-modules.nix +++ b/nix/node-modules.nix @@ -1,18 +1,27 @@ -{ hash, lib, stdenvNoCC, bun, cacert, curl }: +{ + hash, + lib, + stdenvNoCC, + bun, + cacert, + curl, +}: args: stdenvNoCC.mkDerivation { pname = "opencode-node_modules"; version = args.version; src = args.src; - impureEnvVars = - lib.fetchers.proxyImpureEnvVars - ++ [ - "GIT_PROXY_COMMAND" - "SOCKS_SERVER" - ]; + impureEnvVars = lib.fetchers.proxyImpureEnvVars ++ [ + "GIT_PROXY_COMMAND" + "SOCKS_SERVER" + ]; - nativeBuildInputs = [ bun cacert curl ]; + nativeBuildInputs = [ + bun + cacert + curl + ]; dontConfigure = true; diff --git a/nix/opencode.nix b/nix/opencode.nix index 87b3f17ba99..5a8e5a52368 100644 --- a/nix/opencode.nix +++ b/nix/opencode.nix @@ -1,4 +1,10 @@ -{ lib, stdenvNoCC, bun, ripgrep, makeBinaryWrapper }: +{ + lib, + stdenvNoCC, + bun, + ripgrep, + makeBinaryWrapper, +}: args: let scripts = args.scripts; From 611320aad6140775f1659407c2772ae02e210520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Thu, 25 Dec 2025 00:45:49 +0100 Subject: [PATCH 2/5] refactor(nix): rename mkPackage to mkOpencode --- flake.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 224dc95cf1c..f3e9aaa2b0d 100644 --- a/flake.nix +++ b/flake.nix @@ -66,10 +66,10 @@ mkNodeModules = pkgs.callPackage ./nix/node-modules.nix { hash = nodeModulesHash; }; - mkPackage = pkgs.callPackage ./nix/opencode.nix { }; + mkOpencode = pkgs.callPackage ./nix/opencode.nix { }; mkDesktop = pkgs.callPackage ./nix/desktop.nix { }; - opencodePkg = mkPackage { + opencodePkg = mkOpencode { version = packageJson.version; src = ./.; scripts = ./nix/scripts; From 971c3fda0e88c99e8fc3beb96377c6b41adbcacf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Thu, 25 Dec 2025 01:09:57 +0100 Subject: [PATCH 3/5] fix(nix): address desktop flake review --- flake.nix | 4 ++-- nix/desktop.nix | 48 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/flake.nix b/flake.nix index f3e9aaa2b0d..6e09922cc70 100644 --- a/flake.nix +++ b/flake.nix @@ -68,7 +68,7 @@ }; mkOpencode = pkgs.callPackage ./nix/opencode.nix { }; mkDesktop = pkgs.callPackage ./nix/desktop.nix { }; - + opencodePkg = mkOpencode { version = packageJson.version; src = ./.; @@ -77,7 +77,7 @@ modelsDev = "${modelsDev.${system}}/dist/_api.json"; mkNodeModules = mkNodeModules; }; - + desktopPkg = mkDesktop { version = packageJson.version; src = ./.; diff --git a/nix/desktop.nix b/nix/desktop.nix index 7ae564a6697..a8a78c628b6 100644 --- a/nix/desktop.nix +++ b/nix/desktop.nix @@ -4,14 +4,15 @@ rustPlatform, bun, pkg-config, - dbus, + dbus ? null, openssl, - glib, - gtk3, - libsoup_3, - webkitgtk_4_1, - librsvg, - libappindicator-gtk3, + glib ? null, + gtk3 ? null, + libsoup_3 ? null, + webkitgtk_4_1 ? null, + librsvg ? null, + libappindicator-gtk3 ? null, + darwin ? null, cargo, rustc, makeBinaryWrapper, @@ -64,28 +65,39 @@ rustPlatform.buildRustPackage rec { ]; buildInputs = [ - dbus openssl + ] + ++ lib.optionals stdenv.isLinux [ + dbus glib gtk3 libsoup_3 webkitgtk_4_1 librsvg libappindicator-gtk3 - ]; + ] + ++ lib.optionals stdenv.isDarwin ( + with darwin.apple_sdk.frameworks; + [ + AppKit + WebKit + ] + ); preBuild = '' # Restore node_modules pushd ../../.. # Copy node_modules from the fixed-output derivation - # We use cp -r --no-preserve=mode to ensure we can write to them if needed, + # We use cp -r --no-preserve=mode to ensure we can write to them if needed, # though we usually just read. cp -r ${node_modules}/node_modules . cp -r ${node_modules}/packages . - # Fix permissions just in case - chmod -R u+w node_modules packages + # Ensure node_modules is writable so patchShebangs can update script headers + chmod -R u+w node_modules + # Ensure workspace packages are writable for tsgo incremental outputs (.tsbuildinfo) + chmod -R u+w packages # Patch shebangs so scripts can run patchShebangs node_modules @@ -93,8 +105,15 @@ rustPlatform.buildRustPackage rec { mkdir -p packages/desktop/src-tauri/sidecars targetTriple=${stdenv.hostPlatform.rust.rustcTarget} cp ${args.opencode}/bin/opencode packages/desktop/src-tauri/sidecars/opencode-cli-$targetTriple + # Merge prod config into tauri.conf.json - jq -s '.[0] * .[1]' packages/desktop/src-tauri/tauri.conf.json packages/desktop/src-tauri/tauri.prod.conf.json > packages/desktop/src-tauri/tauri.conf.json.tmp + if ! jq -s '.[0] * .[1]' \ + packages/desktop/src-tauri/tauri.conf.json \ + packages/desktop/src-tauri/tauri.prod.conf.json \ + > packages/desktop/src-tauri/tauri.conf.json.tmp; then + echo "Error: failed to merge tauri.conf.json with tauri.prod.conf.json" >&2 + exit 1 + fi mv packages/desktop/src-tauri/tauri.conf.json.tmp packages/desktop/src-tauri/tauri.conf.json # Build the frontend @@ -109,7 +128,7 @@ rustPlatform.buildRustPackage rec { # Tauri bundles the assets during the rust build phase (which happens after preBuild). # It looks for them in the location specified in tauri.conf.json. - postInstall = '' + postInstall = lib.optionalString stdenv.isLinux '' # Wrap the binary to ensure it finds the libraries wrapProgram $out/bin/opencode-desktop \ --prefix LD_LIBRARY_PATH : ${ @@ -129,5 +148,6 @@ rustPlatform.buildRustPackage rec { license = licenses.mit; maintainers = with maintainers; [ ]; mainProgram = "opencode-desktop"; + platforms = platforms.linux ++ platforms.darwin; }; } From 08ecd975eeff422288d9a5b10ba78c9e303c76c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Thu, 25 Dec 2025 01:32:22 +0100 Subject: [PATCH 4/5] ci: catch nix desktop build regressions on macOS/Linux --- .github/workflows/nix-desktop.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/nix-desktop.yml diff --git a/.github/workflows/nix-desktop.yml b/.github/workflows/nix-desktop.yml new file mode 100644 index 00000000000..d3e6fbf13de --- /dev/null +++ b/.github/workflows/nix-desktop.yml @@ -0,0 +1,29 @@ +name: nix desktop + +on: + pull_request: + branches: [dev] + workflow_dispatch: + +jobs: + build-desktop: + strategy: + fail-fast: false + matrix: + os: + - blacksmith-4vcpu-ubuntu-2404 + - macos-latest + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Setup Nix + uses: DeterminateSystems/nix-installer-action@v21 + + - name: Build desktop via flake + run: | + set -euo pipefail + nix --version + nix build .#desktop -L From d40562a24748f42d1d9f036cad7112ea730fc403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Thu, 25 Dec 2025 01:40:15 +0100 Subject: [PATCH 5/5] fix(nix): drop legacy darwin apple_sdk frameworks --- nix/desktop.nix | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/nix/desktop.nix b/nix/desktop.nix index a8a78c628b6..4b659413aaa 100644 --- a/nix/desktop.nix +++ b/nix/desktop.nix @@ -12,7 +12,6 @@ webkitgtk_4_1 ? null, librsvg ? null, libappindicator-gtk3 ? null, - darwin ? null, cargo, rustc, makeBinaryWrapper, @@ -75,14 +74,7 @@ rustPlatform.buildRustPackage rec { webkitgtk_4_1 librsvg libappindicator-gtk3 - ] - ++ lib.optionals stdenv.isDarwin ( - with darwin.apple_sdk.frameworks; - [ - AppKit - WebKit - ] - ); + ]; preBuild = '' # Restore node_modules