mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-11-11 18:17:46 +00:00
In run 38737084248 the credential scrub made Claude Code 2.1.292 require its whole Bash sandbox (bubblewrap and socat) and force the default permission mode, so every ui call failed. The scrub and bubblewrap are dropped: the PreToolUse guard, which runs in -p sessions, keeps Bash to single ui commands, and now logs each decision outside Claude's directory as evidence. The model is Claude Sonnet 5.5 for the last Claude run the session allows. Third Claude run: [claude-test]
463 lines
16 KiB
YAML
463 lines
16 KiB
YAML
name: desktop-linux
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- f/ci-desktop
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
actions: read
|
|
|
|
concurrency:
|
|
group: desktop-linux-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
GHC_VER: 9.6.3
|
|
CABAL_VER: 3.10.2.0
|
|
JAVA_VER: 17
|
|
CORE_PKG: desktop-core-linux-x86_64.tar.gz
|
|
BACKEND_PKG: desktop-linux-backend.tar.gz
|
|
|
|
jobs:
|
|
plan:
|
|
name: find earlier builds
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 10
|
|
outputs:
|
|
backend: ${{ steps.names.outputs.backend }}
|
|
backend-run: ${{ steps.backend.outputs.run-id }}
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
|
|
# artifacts are named after the sources they are built from, so a run uses one that an earlier run uploaded
|
|
- name: Artifact names
|
|
id: names
|
|
run: echo "backend=desktop-linux-backend-${{ hashFiles('src/**', 'apps/simplex-chat/**', 'apps/simplex-directory-service/Main.hs', 'apps/simplex-directory-service/src/**', 'cabal.project', 'simplex-chat.cabal', 'scripts/ci/desktop-linux/build-backend.sh') }}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Find backend
|
|
id: backend
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: scripts/ci/desktop-linux/earlier-run.sh "${{ steps.names.outputs.backend }}"
|
|
|
|
backend:
|
|
name: test backend for the app
|
|
needs: plan
|
|
if: needs.plan.outputs.backend-run == ''
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 150
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Show runner
|
|
run: |
|
|
lsb_release -ds
|
|
nproc
|
|
free -h
|
|
df -h /
|
|
|
|
- name: Setup Haskell
|
|
id: haskell
|
|
uses: simplex-chat/setup-haskell-action@v2
|
|
with:
|
|
ghc-version: ${{ env.GHC_VER }}
|
|
cabal-version: ${{ env.CABAL_VER }}
|
|
|
|
# before the first backend store exists, the core library's has most of the same Hackage dependencies
|
|
- name: Restore cabal store
|
|
id: cabal-cache
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: |
|
|
${{ steps.haskell.outputs.cabal-store }}
|
|
dist-newstyle
|
|
key: desktop-linux-backend-store-ghc${{ env.GHC_VER }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}-${{ github.run_id }}
|
|
restore-keys: |
|
|
desktop-linux-backend-store-ghc${{ env.GHC_VER }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}-
|
|
desktop-linux-backend-store-ghc${{ env.GHC_VER }}-
|
|
desktop-linux-core-store-ghc${{ env.GHC_VER }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}-
|
|
|
|
- name: Install libraries
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y libffi-dev libgmp-dev libnuma-dev libssl-dev zlib1g-dev
|
|
|
|
- name: Build backend
|
|
run: scripts/ci/desktop-linux/build-backend.sh ~/backend/${{ env.BACKEND_PKG }}
|
|
|
|
- name: Save cabal store
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/cache/save@v5
|
|
with:
|
|
path: |
|
|
${{ steps.haskell.outputs.cabal-store }}
|
|
dist-newstyle
|
|
key: ${{ steps.cabal-cache.outputs.cache-primary-key }}
|
|
|
|
- name: Upload backend
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: ${{ needs.plan.outputs.backend }}
|
|
path: ~/backend/${{ env.BACKEND_PKG }}
|
|
compression-level: 0
|
|
retention-days: 30
|
|
if-no-files-found: error
|
|
|
|
core:
|
|
name: core library for Linux
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 150
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Show runner
|
|
run: |
|
|
lsb_release -ds
|
|
nproc
|
|
free -h
|
|
df -h /
|
|
|
|
- name: Restore core library
|
|
id: core-cache
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: ~/core/${{ env.CORE_PKG }}
|
|
key: desktop-linux-core-${{ hashFiles('src/**', 'cabal.project', 'simplex-chat.cabal', 'PRIVACY.md', 'scripts/desktop/build-lib-linux.sh', 'scripts/ci/desktop-linux/build-core.sh') }}
|
|
|
|
- name: Setup Haskell
|
|
id: haskell
|
|
if: steps.core-cache.outputs.cache-hit != 'true'
|
|
uses: simplex-chat/setup-haskell-action@v2
|
|
with:
|
|
ghc-version: ${{ env.GHC_VER }}
|
|
cabal-version: ${{ env.CABAL_VER }}
|
|
|
|
- name: Restore cabal store
|
|
id: cabal-cache
|
|
if: steps.core-cache.outputs.cache-hit != 'true'
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: |
|
|
${{ steps.haskell.outputs.cabal-store }}
|
|
dist-newstyle
|
|
key: desktop-linux-core-store-ghc${{ env.GHC_VER }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}-${{ github.run_id }}
|
|
restore-keys: |
|
|
desktop-linux-core-store-ghc${{ env.GHC_VER }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}-
|
|
desktop-linux-core-store-ghc${{ env.GHC_VER }}-
|
|
|
|
- name: Install libraries
|
|
if: steps.core-cache.outputs.cache-hit != 'true'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y libffi-dev libgmp-dev libnuma-dev libssl-dev zlib1g-dev patchelf
|
|
|
|
- name: Build core library
|
|
if: steps.core-cache.outputs.cache-hit != 'true'
|
|
run: scripts/ci/desktop-linux/build-core.sh ~/core/${{ env.CORE_PKG }}
|
|
|
|
- name: Save cabal store
|
|
if: ${{ !cancelled() && steps.core-cache.outputs.cache-hit != 'true' }}
|
|
uses: actions/cache/save@v5
|
|
with:
|
|
path: |
|
|
${{ steps.haskell.outputs.cabal-store }}
|
|
dist-newstyle
|
|
key: ${{ steps.cabal-cache.outputs.cache-primary-key }}
|
|
|
|
- name: Save core library
|
|
if: steps.core-cache.outputs.cache-hit != 'true'
|
|
uses: actions/cache/save@v5
|
|
with:
|
|
path: ~/core/${{ env.CORE_PKG }}
|
|
key: ${{ steps.core-cache.outputs.cache-primary-key }}
|
|
|
|
- name: Upload core library
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: desktop-core-linux-x86_64
|
|
path: ~/core/${{ env.CORE_PKG }}
|
|
compression-level: 0
|
|
retention-days: 3
|
|
if-no-files-found: error
|
|
|
|
app:
|
|
name: desktop app under Xvfb
|
|
needs: [plan, core, backend]
|
|
# backend is skipped when an earlier run built it
|
|
if: ${{ !cancelled() && !failure() }}
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 60
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
with:
|
|
submodules: recursive
|
|
|
|
- name: Show runner
|
|
run: |
|
|
lsb_release -ds
|
|
nproc
|
|
free -h
|
|
df -h /
|
|
echo "ANDROID_HOME=$ANDROID_HOME"
|
|
ls "$ANDROID_HOME/platforms" "$ANDROID_HOME/build-tools" || true
|
|
|
|
- name: Install tools
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y xvfb xdotool openbox imagemagick tesseract-ocr sqlite3
|
|
dpkg-query -W xvfb xdotool openbox imagemagick tesseract-ocr sqlite3
|
|
|
|
- name: Download core library
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: desktop-core-linux-x86_64
|
|
path: ~/core
|
|
|
|
- name: Install core library
|
|
id: core
|
|
run: scripts/ci/desktop-linux/install-core.sh ~/core/${{ env.CORE_PKG }}
|
|
|
|
- name: Download backend
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: ${{ needs.plan.outputs.backend }}
|
|
path: ~/backend
|
|
run-id: ${{ needs.plan.outputs.backend-run || github.run_id }}
|
|
github-token: ${{ github.token }}
|
|
|
|
- name: Start test backend
|
|
timeout-minutes: 30
|
|
run: scripts/ci/desktop-linux/backend.sh start ~/backend/${{ env.BACKEND_PKG }} "$RUNNER_TEMP/backend"
|
|
|
|
- name: Point the app at the test directory
|
|
id: link
|
|
run: |
|
|
link=$(cat "$RUNNER_TEMP/backend/directory-link")
|
|
file=apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt
|
|
sed -i "s|^const val DIRECTORY_SERVICE_LINK = \".*\"$|const val DIRECTORY_SERVICE_LINK = \"$link\"|" "$file"
|
|
grep -F "const val DIRECTORY_SERVICE_LINK = \"$link\"" "$file"
|
|
|
|
# the app and its tests are still built when the backend fails
|
|
- name: Setup Java
|
|
if: ${{ !cancelled() && steps.core.outcome == 'success' }}
|
|
uses: actions/setup-java@v5
|
|
with:
|
|
distribution: corretto
|
|
java-version: ${{ env.JAVA_VER }}
|
|
cache: gradle
|
|
|
|
- name: Build app
|
|
id: build
|
|
if: ${{ !cancelled() && steps.core.outcome == 'success' }}
|
|
working-directory: apps/multiplatform
|
|
run: |
|
|
./gradlew --no-daemon --console=plain -Psimplex.assets.dir=../../assets :desktop:createDistributable
|
|
app=release/main/app/simplex
|
|
ls -la "$app/bin" "$app/lib/app/resources"
|
|
du -sh "$app"
|
|
LD_LIBRARY_PATH="$app/lib/app/resources" ldd "$app/lib/app/resources/libapp-lib.so"
|
|
|
|
- name: Run desktop tests
|
|
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
|
|
working-directory: apps/multiplatform
|
|
run: ./gradlew --no-daemon --console=plain -Psimplex.assets.dir=../../assets :common:desktopTest
|
|
|
|
- name: Report desktop tests
|
|
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
|
|
run: scripts/ci/desktop-linux/test-summary.py apps/multiplatform/common/build/test-results/desktopTest
|
|
|
|
- name: Run smoke test
|
|
if: ${{ !cancelled() && steps.build.outcome == 'success' && steps.link.outcome == 'success' }}
|
|
run: scripts/ci/desktop-linux/smoke-test.sh apps/multiplatform/release/main/app/simplex "$RUNNER_TEMP/smoke-test"
|
|
|
|
- name: Stop test backend
|
|
if: ${{ !cancelled() }}
|
|
run: scripts/ci/desktop-linux/backend.sh stop "$RUNNER_TEMP/backend"
|
|
|
|
- name: Upload smoke test results
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: desktop-smoke-test
|
|
path: ${{ runner.temp }}/smoke-test
|
|
retention-days: 3
|
|
if-no-files-found: warn
|
|
|
|
- name: Upload desktop test report
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: desktop-test-report
|
|
path: |
|
|
apps/multiplatform/common/build/reports/tests/desktopTest
|
|
apps/multiplatform/common/build/test-results/desktopTest
|
|
retention-days: 3
|
|
if-no-files-found: warn
|
|
|
|
- name: Upload backend logs
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: desktop-backend-logs
|
|
path: ${{ runner.temp }}/backend/*.log
|
|
retention-days: 3
|
|
if-no-files-found: warn
|
|
|
|
# Claude Code repeats the smoke test's directory search with the ui helper. Each run spends the Claude
|
|
# subscription's usage limits, so it runs only when dispatched or asked for in the head commit's message.
|
|
claude:
|
|
name: Claude searches the directory in the desktop app
|
|
needs: [plan, core, backend]
|
|
if: ${{ !cancelled() && !failure() && (github.event_name == 'workflow_dispatch' || contains(github.event.head_commit.message, '[claude-test]')) }}
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 60
|
|
environment:
|
|
name: app-sim-claude
|
|
deployment: false
|
|
concurrency:
|
|
group: desktop-linux-claude-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
env:
|
|
CLAUDE_CODE_VERSION: 2.1.292
|
|
CLAUDE_MODEL: claude-sonnet-5-5
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
with:
|
|
submodules: recursive
|
|
persist-credentials: false
|
|
|
|
- name: Install tools
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y xvfb xdotool openbox imagemagick tesseract-ocr sqlite3
|
|
dpkg-query -W xvfb xdotool openbox imagemagick tesseract-ocr sqlite3
|
|
|
|
- name: Download core library
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: desktop-core-linux-x86_64
|
|
path: ~/core
|
|
|
|
- name: Install core library
|
|
run: scripts/ci/desktop-linux/install-core.sh ~/core/${{ env.CORE_PKG }}
|
|
|
|
- name: Download backend
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: ${{ needs.plan.outputs.backend }}
|
|
path: ~/backend
|
|
run-id: ${{ needs.plan.outputs.backend-run || github.run_id }}
|
|
github-token: ${{ github.token }}
|
|
|
|
- name: Start test backend
|
|
timeout-minutes: 30
|
|
run: scripts/ci/desktop-linux/backend.sh start ~/backend/${{ env.BACKEND_PKG }} "$RUNNER_TEMP/backend"
|
|
|
|
- name: Point the app at the test directory
|
|
run: |
|
|
link=$(cat "$RUNNER_TEMP/backend/directory-link")
|
|
file=apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/DirectorySearch.kt
|
|
sed -i "s|^const val DIRECTORY_SERVICE_LINK = \".*\"$|const val DIRECTORY_SERVICE_LINK = \"$link\"|" "$file"
|
|
grep -F "const val DIRECTORY_SERVICE_LINK = \"$link\"" "$file"
|
|
|
|
- name: Setup Java
|
|
uses: actions/setup-java@v5
|
|
with:
|
|
distribution: corretto
|
|
java-version: ${{ env.JAVA_VER }}
|
|
cache: gradle
|
|
|
|
- name: Build app
|
|
working-directory: apps/multiplatform
|
|
run: ./gradlew --no-daemon --console=plain -Psimplex.assets.dir=../../assets :desktop:createDistributable
|
|
|
|
# ui goes on every shell's PATH, where Claude's Bash tool finds it whatever environment it builds
|
|
- name: Install Claude Code and the ui command
|
|
run: |
|
|
npm install --prefix "$RUNNER_TEMP/claude-code" "@anthropic-ai/claude-code@$CLAUDE_CODE_VERSION"
|
|
echo "$RUNNER_TEMP/claude-code/node_modules/.bin" >> "$GITHUB_PATH"
|
|
printf '#!/bin/sh\nexec %s/scripts/ci/desktop-linux/ui "$@"\n' "$PWD" | sudo tee /usr/local/bin/ui
|
|
sudo chmod +x /usr/local/bin/ui
|
|
|
|
- name: Start the app at the chat list
|
|
run: scripts/ci/desktop-linux/smoke-test.sh apps/multiplatform/release/main/app/simplex "$RUNNER_TEMP/claude-test/screens" onboarding
|
|
|
|
# Claude's Bash is limited to single ui commands by the guard hook in claude-test.sh. Claude Code's credential
|
|
# scrub stays off: on Linux it forces its whole Bash sandbox (bubblewrap, socat), which ui isn't set up for.
|
|
- name: Run Claude
|
|
id: claude
|
|
env:
|
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: 1
|
|
run: scripts/ci/desktop-linux/claude-test.sh "$RUNNER_TEMP/claude-test" "$RUNNER_TEMP/claude-transcript.jsonl"
|
|
|
|
- name: Report Claude usage
|
|
if: ${{ !cancelled() && steps.claude.outcome != 'skipped' }}
|
|
run: scripts/ci/desktop-linux/claude-usage.py "$RUNNER_TEMP/claude-transcript.jsonl" "$RUNNER_TEMP/claude-usage.json"
|
|
|
|
- name: Stop test backend
|
|
if: ${{ !cancelled() }}
|
|
run: scripts/ci/desktop-linux/backend.sh stop "$RUNNER_TEMP/backend"
|
|
|
|
- name: Upload Claude test results
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: desktop-claude-test
|
|
path: |
|
|
${{ runner.temp }}/claude-test
|
|
${{ runner.temp }}/claude-transcript.jsonl
|
|
${{ runner.temp }}/claude-transcript.jsonl.guard.log
|
|
${{ runner.temp }}/claude-usage.json
|
|
retention-days: 7
|
|
if-no-files-found: warn
|
|
|
|
- name: Upload backend logs
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: desktop-claude-backend-logs
|
|
path: ${{ runner.temp }}/backend/*.log
|
|
retention-days: 3
|
|
if-no-files-found: warn
|
|
|
|
release:
|
|
name: released app under Xvfb
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 30
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
with:
|
|
fetch-depth: 0
|
|
filter: tree:0
|
|
|
|
- name: Download released app
|
|
run: scripts/ci/desktop-linux/release-app.sh "$RUNNER_TEMP/release"
|
|
|
|
- name: Install UI tools
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y xvfb xdotool openbox imagemagick tesseract-ocr
|
|
dpkg-query -W xvfb xdotool openbox imagemagick tesseract-ocr
|
|
|
|
- name: Run smoke test
|
|
run: scripts/ci/desktop-linux/smoke-test.sh "$RUNNER_TEMP/release/app/opt/simplex" "$RUNNER_TEMP/smoke-test" onboarding
|
|
|
|
- name: Upload smoke test results
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: desktop-release-smoke-test
|
|
path: ${{ runner.temp }}/smoke-test
|
|
retention-days: 3
|
|
if-no-files-found: warn
|