Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ jobs:
node-version: ${{ matrix.node-version }}
cache: npm

- name: Seed verified ripgrep install cache
run: node scripts/release/prime-ripgrep-cache.mjs

- name: Install dependencies
run: npm ci

Expand Down
237 changes: 237 additions & 0 deletions .github/workflows/desktop-linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
name: Linux desktop

on:
workflow_dispatch:
pull_request:
push:
branches:
- main

permissions:
contents: read

concurrency:
group: desktop-linux-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

# Build artifacts only. Publishing remains a separate release decision.
jobs:
build:
name: Build Linux x64 deb and AppImage (glibc 2.35)
runs-on: ubuntu-22.04
timeout-minutes: 90
outputs:
asset_stem: ${{ steps.metadata.outputs.asset_stem }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Set up pinned Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22.22.2'
architecture: x64
cache: npm

- name: Check build host and define canonical assets
id: metadata
run: |
set -euo pipefail
test "$(uname -s)" = Linux
test "$(uname -m)" = x86_64
test "$(getconf GNU_LIBC_VERSION)" = "glibc 2.35"
test "$(node --version)" = v22.22.2
VERSION="$(node -p "require('./package.json').version")"
echo "asset_stem=gajae-app-desktop-${VERSION}-linux-x64" >> "$GITHUB_OUTPUT"

- name: Install Tauri 2 Linux prerequisites
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y \
build-essential pkg-config curl wget file unzip git ca-certificates \
libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev \
libxdo-dev libssl-dev librsvg2-dev patchelf xdg-utils libnss3

- name: Set up Rust
run: |
set -euo pipefail
rustup toolchain install 1.85.1 --profile minimal --component rustfmt
rustup default 1.85.1
rustup target add x86_64-unknown-linux-gnu
rustc --version

- name: Seed verified ripgrep install cache
run: node scripts/release/prime-ripgrep-cache.mjs

- name: Install dependencies
run: npm ci

- name: Fetch and check pinned Bun runtime
run: |
set -euo pipefail
node scripts/fetch-bun.mjs
test "$(dist-native/bun --version)" = 1.4.0

- name: Check product identity
run: npm run check:identity

- name: Build bundled Linux desktop packages
env:
# Allow AppImage build tools to run on runners without a FUSE mount.
APPIMAGE_EXTRACT_AND_RUN: '1'
run: env -u CI npm run desktop:build:linux

- name: Check Tauri formatting and tests
run: |
set -euo pipefail
cargo fmt --manifest-path src-tauri/Cargo.toml -- --check
cargo test --locked --manifest-path src-tauri/Cargo.toml

- name: Verify canonical package names and checksums
env:
ASSET_STEM: ${{ steps.metadata.outputs.asset_stem }}
run: |
set -euo pipefail
shopt -s nullglob dotglob
assets=(release/desktop/*)
if [ "${#assets[@]}" -ne 4 ]; then
echo "Expected only the canonical deb, AppImage, and their checksums." >&2
exit 1
fi
for extension in deb AppImage; do
test -f "release/desktop/$ASSET_STEM.$extension"
test -f "release/desktop/$ASSET_STEM.$extension.sha256"
(
cd release/desktop
sha256sum --check "$ASSET_STEM.$extension.sha256"
)
done
test "$(dpkg-deb --field "release/desktop/$ASSET_STEM.deb" Architecture)" = amd64
dpkg-deb --info "release/desktop/$ASSET_STEM.deb"

- name: Upload Linux desktop build artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: gajae-app-desktop-linux-x64
path: release/desktop/*
if-no-files-found: error
compression-level: 0
retention-days: 14

package-smoke:
name: Check packaged server and GUI on ${{ matrix.os }}
needs: build
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os:
- ubuntu-22.04
- ubuntu-24.04
env:
ASSET_STEM: ${{ needs.build.outputs.asset_stem }}
steps:
- name: Checkout smoke harness
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Set up Node.js for the smoke harness
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22.22.2'
architecture: x64

- name: Install Linux desktop libraries
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y \
libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev \
libxdo-dev libssl-dev librsvg2-dev xdg-utils git ca-certificates libnss3 \
python3 python3-gi python3-xlib gir1.2-gtk-3.0 \
xvfb xauth dbus-x11 libgl1-mesa-dri fontconfig fonts-dejavu-core \
tesseract-ocr tesseract-ocr-eng

- name: Check GUI smoke regression tests
run: node --test scripts/release/smoke-linux-desktop.test.mjs

- name: Download Linux desktop build artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: gajae-app-desktop-linux-x64
path: release/desktop

- name: Verify downloaded package checksums
run: |
set -euo pipefail
shopt -s nullglob dotglob
assets=(release/desktop/*)
if [ "${#assets[@]}" -ne 4 ]; then
echo "Smoke job received non-canonical desktop assets." >&2
exit 1
fi
for extension in deb AppImage; do
test -f "release/desktop/$ASSET_STEM.$extension"
test -f "release/desktop/$ASSET_STEM.$extension.sha256"
(
cd release/desktop
sha256sum --check "$ASSET_STEM.$extension.sha256"
)
done
test "$(dpkg-deb --field "release/desktop/$ASSET_STEM.deb" Architecture)" = amd64

- name: Smoke extracted deb server and GUI outside the checkout
run: |
set -euo pipefail
LINUX_ROOT="$(mktemp -d "$RUNNER_TEMP/gajae-deb.XXXXXX")"
trap 'rm -rf "$LINUX_ROOT"' EXIT
dpkg-deb -x "release/desktop/$ASSET_STEM.deb" "$LINUX_ROOT"
test -x "$LINUX_ROOT/usr/bin/gajae-app-desktop"
npm run smoke:packaged-server -- --linux-root "$LINUX_ROOT"
npm run smoke:packaged-server -- --linux-root "$LINUX_ROOT" --data-survival
python3 scripts/release/smoke-linux-desktop.py \
--linux-root "$LINUX_ROOT" --format deb \
--artifacts "$RUNNER_TEMP/gajae-gui-smoke/deb"

- name: Smoke extracted AppImage server and GUI outside the checkout
run: |
set -euo pipefail
APPIMAGE="$GITHUB_WORKSPACE/release/desktop/$ASSET_STEM.AppImage"
EXTRACT_DIR="$(mktemp -d "$RUNNER_TEMP/gajae-appimage.XXXXXX")"
trap 'rm -rf "$EXTRACT_DIR"' EXIT
chmod +x "$APPIMAGE"
(
cd "$EXTRACT_DIR"
"$APPIMAGE" --appimage-extract > "$RUNNER_TEMP/gajae-appimage-extract.log"
)
test -x "$EXTRACT_DIR/squashfs-root/AppRun"
test -x "$EXTRACT_DIR/squashfs-root/usr/bin/gajae-app-desktop"
npm run smoke:packaged-server -- --linux-root "$EXTRACT_DIR/squashfs-root"
npm run smoke:packaged-server -- --linux-root "$EXTRACT_DIR/squashfs-root" --data-survival
npm run smoke:packaged-server -- --linux-root "$EXTRACT_DIR/squashfs-root" --appimage-env
python3 scripts/release/smoke-linux-desktop.py \
--linux-root "$EXTRACT_DIR/squashfs-root" --format appimage \
--artifacts "$RUNNER_TEMP/gajae-gui-smoke/appimage"

- name: Verify installed Debian launcher and URI registration
run: |
set -euo pipefail
sudo apt-get install --no-install-recommends -y "./release/desktop/$ASSET_STEM.deb"
DESKTOP_NAME="$(node -p "require('./package.json').productName")"
desktop-file-validate "/usr/share/applications/$DESKTOP_NAME.desktop"
python3 -c 'import json; from gi.repository import Gio; name=json.load(open("package.json"))["productName"]; app=Gio.DesktopAppInfo.new_from_filename("/usr/share/applications/"+name+".desktop"); assert app and app.supports_uris(); assert Gio.AppInfo.get_default_for_uri_scheme("gajae-app") is not None'
python3 scripts/release/smoke-linux-desktop.py \
--linux-root / --format deb \
--artifacts "$RUNNER_TEMP/gajae-gui-smoke/installed-deb"

- name: Upload GUI smoke evidence
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: gajae-linux-gui-${{ matrix.os }}
path: ${{ runner.temp }}/gajae-gui-smoke/
if-no-files-found: warn
retention-days: 14
26 changes: 26 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ job projection protocol). `scripts/` holds build/release/verify tooling.
- Server binds loopback by default (fail-closed; it can run shell commands).
`SERVER_PORT` defaults to 3001, Vite dev on 5173. Do not export `SERVER_PORT=0`.
- Tauri builds choke on `CI=1`: use `env -u CI npm run tauri -- build`.
- Linux desktop packaging targets native `x86_64-unknown-linux-gnu`, bundles
Node **22.22.2** and Bun **1.4.0**, and needs GTK 3/WebKitGTK 4.1 plus the
prerequisites in `docs/DESKTOP-LINUX.md`. CI builds on Ubuntu 22.04/glibc 2.35;
local Ubuntu 24.04/glibc 2.39 artifacts do not establish that compatibility floor.

## Commands

Expand All @@ -41,6 +45,11 @@ npm run check:core # cargo fmt --check + clippy -D warnings + cargo test
npm run verify # FULL GATE: audit + typecheck + check:core + test + lint + check:identity + build
npm run test:e2e:gjc # 7 GJC wire/browser e2e tests (separate from npm test)
npm run desktop:dev # Tauri dev shell
npm run server:payload:linux # Linux x64 payload + pinned runtimes
env -u CI npm run desktop:build:linux # payload + Tauri deb/AppImage + release/desktop staging
npm run smoke:packaged-server -- --linux-root <extracted-dir> # extracted deb or squashfs-root
npm run smoke:packaged-server -- --linux-root <extracted-dir> --data-survival
npm run smoke:packaged-server -- --linux-root <squashfs-root> --appimage-env # AppRun + real Python/gio terminal probes
```

Run a single test file (match the runner's env):
Expand All @@ -58,6 +67,21 @@ dist-native/bun test src/shared/view/ui/ActionMenu.dom.bun.test.tsx

`npm test` has a `pretest` that builds the Rust core (debug); tests fail without it.

Linux desktop CI is `.github/workflows/desktop-linux.yml`: manual/PR/push-main
builds, with package smokes on Ubuntu 22.04 and 24.04. After the bundle build,
it runs `cargo fmt --manifest-path src-tauri/Cargo.toml -- --check` and
`cargo test --locked --manifest-path src-tauri/Cargo.toml`; `npm run verify`
does not cover these desktop shell checks. It only uploads build
artifacts; it does not publish or announce releases. Stage Linux packages as
`release/desktop/gajae-app-desktop-${package.version}-linux-x64.deb` and
`.AppImage`, each with `.sha256`. Extract outside the checkout before smoking
to prevent repository `node_modules` from masking missing bundled dependencies.
Keep standard smoke and `--data-survival` as separate invocations. Record
packaging, server smoke, and interactive GUI results separately.
`desktop:build:linux` restores the verified AppImage runtime after linuxdeploy
and before staging/checksums; preserve this step so ELF rewriting cannot break
the runtime manifest hashes.

## Frontend stack

React 19.2 + TypeScript 5.9 on Vite 7 with the React Compiler enabled
Expand Down Expand Up @@ -168,4 +192,6 @@ is `.ts`/`.tsx`. Routing is react-router-dom 7.
- `server/GJC-LIVE-SPEC.md` — GJC provider/worker contract.
- `docs/DESKTOP-TAURI-VERIFICATION.md` — desktop packaging/verification (incl. the
human-gated notarization step).
- `docs/DESKTOP-LINUX.md` — Linux x64 desktop prerequisites, package builds,
installation, compatibility floor, and validation procedure.
- `docs/SELF-HOST.md`, `CONTRIBUTING.md` — install/update lifecycle and PR rules.
27 changes: 27 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,33 @@ Useful commands:
| `npm test` | Run the repository test suite. |
| `npm run lint` | Check JavaScript and TypeScript style. |
| `npm run check:identity` | Validate the product and release identity. |
| `npm run server:payload:linux` | Build the Linux x64 server payload with pinned Node 22.22.2 and Bun 1.4.0. |
| `env -u CI npm run desktop:build:linux` | Build and stage Linux x64 `.deb` and `.AppImage` packages with checksums. |

### Linux desktop development

Use native x86_64 Linux with Rust and the GTK 3/WebKitGTK 4.1 development
libraries listed in [docs/DESKTOP-LINUX.md](docs/DESKTOP-LINUX.md). Use Node
**22.22.2** to match desktop CI; the packaged app includes its own Node and Bun.
`npm run desktop:build:linux` builds the payload, runs Tauri for
`x86_64-unknown-linux-gnu`, and stages packages in `release/desktop/` using
`package.json.version` in the filenames.

The separate `.github/workflows/desktop-linux.yml` workflow runs on pull
requests, pushes to `main`, and manual dispatch. It builds on Ubuntu 22.04
(glibc 2.35), then extracts and smokes both package formats on Ubuntu 22.04
and 24.04. It uploads build artifacts without publishing a release or sending
announcements. It also checks Tauri Rust formatting and runs the shell's locked
Cargo tests after the bundle build. Ordinary source verification remains in
the existing CI workflow; `npm run verify` covers the Rust core, not the Tauri
shell tests.

For desktop changes, follow the Linux guide's out-of-checkout package smoke
and `--data-survival` commands, then record the host, commit, package hashes,
and results. An Ubuntu 24.04 local build uses glibc 2.39; only a successful
Ubuntu 22.04 build and smoke provide evidence for the glibc 2.35 floor.
Headless server checks do not verify the desktop window: report interactive
GUI checks separately, and do not describe unrun checks as passed.

## Repository map

Expand Down
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<a href="https://github.com/devswha/gajae-code-app/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/devswha/gajae-code-app/actions/workflows/ci.yml/badge.svg"></a>
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-yellow.svg"></a>
<a href="CHANGELOG.md"><img alt="Version 2.0.0-beta.7" src="https://img.shields.io/badge/version-2.0.0--beta.7-blue"></a>
<a href="#quick-start"><img alt="Platforms: macOS arm64 | Linux server | Source" src="https://img.shields.io/badge/Platforms-macOS%20arm64%20%7C%20Linux%20server%20%7C%20Source-green"></a>
<a href="#quick-start"><img alt="Platforms: macOS arm64 | Linux server | Linux desktop source" src="https://img.shields.io/badge/Platforms-macOS%20arm64%20%7C%20Linux%20server%20%7C%20Linux%20desktop%20source-green"></a>
<a href="https://github.com/devswha/gajae-code"><img alt="Runtime: Gajae Code SDK 0.15.6" src="https://img.shields.io/badge/Runtime-Gajae%20Code%20SDK%200.15.6-blueviolet"></a>
</p>

Expand Down Expand Up @@ -54,7 +54,16 @@ sha256sum --check gajae-app-server-2.0.0-beta.7-linux-x64-node22.tar.gz.sha256

Install, upgrade and rollback steps: [docs/INSTALL.md](docs/INSTALL.md) · [docs/SELF-HOST.md](docs/SELF-HOST.md).

**From source — Node.js 22, Rust, Bun 1.4.0.**
**Linux desktop (x86_64) — build from source.** Native Tauri `.deb` and `.AppImage`
packages bundle Node.js **22.22.2** and Bun **1.4.0**. See the
[Linux desktop guide](docs/DESKTOP-LINUX.md) for prerequisites, local builds,
installation, and package checks. Linux desktop downloads are not yet published;
the separate Linux desktop CI workflow produces build artifacts for validation.
CI is configured to build on Ubuntu 22.04 (glibc 2.35) and check extracted
packages on Ubuntu 22.04 and 24.04; those jobs have not yet run. Local Ubuntu
24.04 validation uses glibc 2.39 and does not establish Ubuntu 22.04 compatibility.

**From source — Node.js 22.x (22.22.2+), Rust, Bun 1.4.0.**

```bash
git clone https://github.com/devswha/gajae-code-app.git
Expand All @@ -65,7 +74,7 @@ npm run dev # server :3001, client :5173
npm run desktop:dev # the same, inside the Tauri desktop shell
```

The app uses the models, presets, skills and credentials of the Gajae Code installation in `~/.gjc`, and you can sign in to providers from inside the app. Intel Mac, Windows and Linux desktop builds are not available yet.
The app uses the models, presets, skills and credentials of the Gajae Code installation in `~/.gjc`, and you can sign in to providers from inside the app. Linux desktop development requires the system libraries listed in the Linux guide. Intel Mac, Windows and Linux arm64 desktop builds are not available yet.

## Permission Modes

Expand Down Expand Up @@ -94,6 +103,7 @@ A card answered in one tab closes in every other viewer. Always deny is offered

- [Self-hosting](docs/SELF-HOST.md) · [Install the server release](docs/INSTALL.md) · [Changelog](CHANGELOG.md)
- [Desktop packaging, signing and notarization](docs/DESKTOP-TAURI-VERIFICATION.md)
- [Linux desktop builds, installation and validation](docs/DESKTOP-LINUX.md)
- [GJC provider and worker contract](server/GJC-LIVE-SPEC.md) · [Worker protocol](docs/GJC-WORKER-PROTOCOL.md)
- [Design system](DESIGN.md) · [Repository guide for agents](AGENTS.md)
- [Licensing](docs/LICENSING.md) · [Relicensing record](docs/RELICENSING.md) · [Upstream intake](docs/UPSTREAM.md)
Expand Down
Loading