summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/nix-desktop.yml29
-rw-r--r--flake.nix20
-rw-r--r--nix/desktop.nix145
3 files changed, 190 insertions, 4 deletions
diff --git a/.github/workflows/nix-desktop.yml b/.github/workflows/nix-desktop.yml
new file mode 100644
index 000000000..d3e6fbf13
--- /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
diff --git a/flake.nix b/flake.nix
index a578da9c1..e53053217 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 { };
- in
- {
- default = mkPackage {
+ mkOpencode = pkgs.callPackage ./nix/opencode.nix { };
+ mkDesktop = pkgs.callPackage ./nix/desktop.nix { };
+
+ opencodePkg = mkOpencode {
inherit (packageJson) version;
src = ./.;
scripts = ./nix/scripts;
@@ -77,6 +77,18 @@
modelsDev = "${modelsDev.${system}}/dist/_api.json";
inherit mkNodeModules;
};
+
+ desktopPkg = mkDesktop {
+ inherit (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 000000000..4b659413a
--- /dev/null
+++ b/nix/desktop.nix
@@ -0,0 +1,145 @@
+{
+ lib,
+ stdenv,
+ rustPlatform,
+ bun,
+ pkg-config,
+ dbus ? null,
+ openssl,
+ glib ? null,
+ gtk3 ? null,
+ libsoup_3 ? null,
+ webkitgtk_4_1 ? null,
+ librsvg ? null,
+ libappindicator-gtk3 ? null,
+ 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 = [
+ openssl
+ ]
+ ++ lib.optionals stdenv.isLinux [
+ dbus
+ 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 .
+
+ # 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
+
+ # 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
+ 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
+ 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 = lib.optionalString stdenv.isLinux ''
+ # 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";
+ platforms = platforms.linux ++ platforms.darwin;
+ };
+}