Files
simplex-chat/.github/workflows/ios-sim.yml
T
spacedandy-agents-simplex f53eb6ae87 ci: point the iOS app at the test directory and restore the smoke test's search checks
With the test backend, the app is built against the test directory's address, and the smoke test goes on to search it: the Directory header above the results and their avatars in both layouts, the chat list's cards shown while typing and hidden under the results once the profile has a conversation, and "Show more" disabled while its page loads.
2026-10-16 21:01:22 +00:00

359 lines
13 KiB
YAML

name: ios-sim
on:
push:
branches:
- '**'
# a squash merge's message lists the merged commits' subjects, with their tags
- '!master'
- '!stable'
workflow_dispatch:
inputs:
claude:
description: Also run the Claude session
type: boolean
default: false
backend:
description: Start the test backend
type: boolean
default: false
permissions:
contents: read
actions: read
concurrency:
# a push that asks for no run must not cancel one that does
group: ios-sim-${{ github.ref }}-${{ (github.event_name == 'workflow_dispatch' || contains(github.event.head_commit.message, '[app-ci]') || contains(github.event.head_commit.message, '[app-ci:ios]') || contains(github.event.head_commit.message, '[claude-test]') || contains(github.event.head_commit.message, '[claude-test:ios]')) && 'requested' || github.run_id }}
cancel-in-progress: true
env:
GHC_VER: 9.6.3
CABAL_VER: 3.10.2.0
CORE_PKG: pkg-ios-aarch64-swift-json.zip
BACKEND_PKG: ios-sim-backend.zip
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
# newer images add Command Line Tools whose macOS SDK the default Xcode's linker cannot read
DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer
jobs:
plan:
name: find earlier builds
if: >-
github.event_name == 'workflow_dispatch' ||
contains(github.event.head_commit.message, '[app-ci]') ||
contains(github.event.head_commit.message, '[app-ci:ios]') ||
contains(github.event.head_commit.message, '[claude-test]') ||
contains(github.event.head_commit.message, '[claude-test:ios]')
runs-on: ubuntu-latest
timeout-minutes: 10
outputs:
core: ${{ steps.names.outputs.core }}
backend: ${{ steps.names.outputs.backend }}
core-run: ${{ steps.core.outputs.run-id }}
backend-run: ${{ steps.backend.outputs.run-id }}
claude: ${{ inputs.claude || contains(github.event.head_commit.message, '[claude-test]') || contains(github.event.head_commit.message, '[claude-test:ios]') }}
start-backend: ${{ inputs.backend || contains(github.event.head_commit.message, '[ci-backend]') }}
steps:
- name: Checkout code
uses: actions/checkout@v4
# 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 "core=ios-sim-core-${{ hashFiles('src/**', 'cabal.project', 'simplex-chat.cabal', 'scripts/ci/ios-sim/build-core.sh') }}" >> "$GITHUB_OUTPUT"
echo "backend=ios-sim-backend-${{ hashFiles('src/**', 'apps/simplex-chat/**', 'apps/simplex-directory-service/Main.hs', 'apps/simplex-directory-service/src/**', 'cabal.project', 'simplex-chat.cabal', 'scripts/ci/ios-sim/build-backend.sh') }}" >> "$GITHUB_OUTPUT"
- name: Find core library
id: core
env:
GH_TOKEN: ${{ github.token }}
run: scripts/ci/ios-sim/earlier-run.sh "${{ steps.names.outputs.core }}"
- name: Find backend
id: backend
env:
GH_TOKEN: ${{ github.token }}
run: scripts/ci/ios-sim/earlier-run.sh "${{ steps.names.outputs.backend }}"
core:
name: core library for the iOS simulator
needs: plan
if: needs.plan.outputs.core-run == ''
runs-on: macos-latest
timeout-minutes: 240
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Show runner
run: |
sw_vers
sysctl -n hw.ncpu hw.memsize
df -h /
xcodebuild -version
xcrun --show-sdk-path
ls /Library/Developer/CommandLineTools/SDKs || true
- name: Use the macOS 26 SDK
run: scripts/ci/ios-sim/use-macos26-sdk.sh
# without ~/.cabal, cabal 3.10 keeps its store in XDG directories, not where setup-haskell reports it
- name: Use ~/.cabal for cabal
run: echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV"
- name: Setup Haskell
id: haskell
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
uses: actions/cache/restore@v4
with:
path: |
${{ steps.haskell.outputs.cabal-store }}
dist-newstyle
key: ios-sim-core-store-ghc${{ env.GHC_VER }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}-${{ github.run_id }}
restore-keys: |
ios-sim-core-store-ghc${{ env.GHC_VER }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}-
ios-sim-core-store-ghc${{ env.GHC_VER }}-
- name: Install libraries
run: brew install openssl@3 libffi gmp
- name: Build core library
id: build
run: scripts/ci/ios-sim/build-core.sh ~/Downloads/${{ env.CORE_PKG }}
- name: Save cabal store
if: ${{ !cancelled() }}
uses: actions/cache/save@v4
with:
path: |
${{ steps.haskell.outputs.cabal-store }}
dist-newstyle
key: ${{ steps.cabal-cache.outputs.cache-primary-key }}
- name: Patch core library for the simulator
run: scripts/ci/ios-sim/prepare-libs.sh
- name: Upload core library
uses: actions/upload-artifact@v4
with:
name: ${{ needs.plan.outputs.core }}
path: ~/Downloads/${{ env.CORE_PKG }}
compression-level: 0
retention-days: 30
if-no-files-found: error
backend:
name: test backend for the app
needs: plan
if: needs.plan.outputs.start-backend == 'true' && needs.plan.outputs.backend-run == ''
runs-on: macos-latest
timeout-minutes: 240
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Show runner
run: |
sw_vers
xcodebuild -version
xcrun --show-sdk-path
ls /Library/Developer/CommandLineTools/SDKs || true
- name: Use the macOS 26 SDK
run: scripts/ci/ios-sim/use-macos26-sdk.sh
- name: Use ~/.cabal for cabal
run: echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV"
- name: Setup Haskell
id: haskell
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
uses: actions/cache/restore@v4
with:
path: |
${{ steps.haskell.outputs.cabal-store }}
dist-newstyle
key: ios-sim-backend-store-ghc${{ env.GHC_VER }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}-${{ github.run_id }}
restore-keys: |
ios-sim-backend-store-ghc${{ env.GHC_VER }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}-
ios-sim-backend-store-ghc${{ env.GHC_VER }}-
- name: Build backend
run: scripts/ci/ios-sim/build-backend.sh ~/Downloads/${{ env.BACKEND_PKG }}
- name: Save cabal store
if: ${{ !cancelled() }}
uses: actions/cache/save@v4
with:
path: |
${{ steps.haskell.outputs.cabal-store }}
dist-newstyle
key: ${{ steps.cabal-cache.outputs.cache-primary-key }}
- name: Upload backend
uses: actions/upload-artifact@v4
with:
name: ${{ needs.plan.outputs.backend }}
path: ~/Downloads/${{ env.BACKEND_PKG }}
compression-level: 0
retention-days: 30
if-no-files-found: error
app:
name: app in the iOS simulator
needs: [plan, core, backend]
# core and backend are skipped when an earlier run built them, and backend also when the run doesn't start it
if: ${{ !cancelled() && !failure() && needs.plan.result == 'success' }}
runs-on: macos-latest
environment:
name: ${{ needs.plan.outputs.claude == 'true' && 'app-sim-claude' || '' }}
deployment: false
timeout-minutes: 330
env:
CLAUDE_RUN: ${{ needs.plan.outputs.claude }}
START_BACKEND: ${{ needs.plan.outputs.start-backend }}
steps:
# the job later holds the Claude token, so the checkout's GitHub token is not left in .git/config
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Show runner
run: |
sw_vers
df -h /
xcodebuild -version
xcrun --show-sdk-path
ls /Library/Developer/CommandLineTools/SDKs || true
xcrun simctl list runtimes
- name: Use the macOS 26 SDK
run: scripts/ci/ios-sim/use-macos26-sdk.sh
- name: Download core library
uses: actions/download-artifact@v4
with:
name: ${{ needs.plan.outputs.core }}
path: ~/Downloads
run-id: ${{ needs.plan.outputs.core-run || github.run_id }}
github-token: ${{ github.token }}
- name: Prepare libraries
run: |
scripts/ci/ios-sim/prepare-libs.sh
scripts/ios/update-pbxproj.sh
- name: Download backend
if: env.START_BACKEND == 'true'
uses: actions/download-artifact@v4
with:
name: ${{ needs.plan.outputs.backend }}
path: ~/Downloads
run-id: ${{ needs.plan.outputs.backend-run || github.run_id }}
github-token: ${{ github.token }}
- name: Start test backend
if: env.START_BACKEND == 'true'
timeout-minutes: 30
run: scripts/ci/ios-sim/backend.sh start ~/Downloads/${{ env.BACKEND_PKG }} "$RUNNER_TEMP/backend"
- name: Point the app at the test directory
if: env.START_BACKEND == 'true'
run: |
link=$(cat "$RUNNER_TEMP/backend/directory-link")
file=apps/ios/Shared/Model/DirectorySearch.swift
sed -i '' "s|^let DIRECTORY_SERVICE_LINK = \".*\"$|let DIRECTORY_SERVICE_LINK = \"$link\"|" "$file"
grep -F "let DIRECTORY_SERVICE_LINK = \"$link\"" "$file"
- name: Build app
run: |
xcodebuild build -quiet \
-project apps/ios/SimpleX.xcodeproj \
-scheme "SimpleX (iOS)" \
-configuration Debug \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath "$RUNNER_TEMP/DerivedData" \
-onlyUsePackageVersionsFromResolvedFile \
-skipPackagePluginValidation \
-skipMacroValidation \
ARCHS=arm64 \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY=- \
DEVELOPMENT_TEAM= \
PROVISIONING_PROFILE_SPECIFIER=
- name: Install AXe
run: scripts/ci/ios-sim/install-axe.sh
- name: Run smoke test
env:
BACKEND_DIR: ${{ env.START_BACKEND == 'true' && format('{0}/backend', runner.temp) || '' }}
run: scripts/ci/ios-sim/smoke-test.sh "$RUNNER_TEMP/DerivedData/Build/Products/Debug-iphonesimulator/SimpleX.app" "$RUNNER_TEMP/smoke-test"
- name: Install Claude Code
if: env.CLAUDE_RUN == 'true'
run: |
npm install -g @anthropic-ai/claude-code@2.1.300
claude --version
- name: Run the device test plan with Claude
if: env.CLAUDE_RUN == 'true'
timeout-minutes: 240
env:
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
BACKEND_DIR: ${{ env.START_BACKEND == 'true' && format('{0}/backend', runner.temp) || '' }}
run: scripts/ci/ios-sim/claude-test.sh "$RUNNER_TEMP/claude-test"
- name: Report Claude usage
if: ${{ always() && env.CLAUDE_RUN == 'true' }}
run: scripts/ci/ios-sim/claude-usage.sh "$RUNNER_TEMP/claude-test"
- name: Stop test backend
if: ${{ !cancelled() && env.START_BACKEND == 'true' }}
run: scripts/ci/ios-sim/backend.sh stop "$RUNNER_TEMP/backend"
- name: Upload smoke test results
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: ios-sim-smoke-test
path: ${{ runner.temp }}/smoke-test
retention-days: 3
if-no-files-found: warn
- name: Upload backend logs
if: ${{ !cancelled() && env.START_BACKEND == 'true' }}
uses: actions/upload-artifact@v4
with:
name: ios-sim-backend-logs
path: |
${{ runner.temp }}/backend/*.log
${{ runner.temp }}/backend/seed.md
retention-days: 3
if-no-files-found: warn
- name: Upload Claude test results
if: ${{ always() && env.CLAUDE_RUN == 'true' }}
uses: actions/upload-artifact@v4
with:
name: ios-sim-claude-test
path: ${{ runner.temp }}/claude-test
retention-days: 14
if-no-files-found: warn