--- # Reusable E2E workflow — single source of truth for the desktop E2E recipe. # # Callers: # - `.github/workflows/e2e.yml` — PR/push, all-OS selected-spec gate. # - `.github/workflows/release-staging.yml` — pretest gate, all 3 OS, full suite. # - `.github/workflows/release-production.yml` — pretest gate, all 3 OS, full suite. # # Recipe per OS (identical across platforms — see e2e-run-session.sh): # 1. Build the CEF app via `pnpm --filter openhuman-app test:e2e:build`. # 2. Install Appium 3.x + the `chromium` driver. # 3. Run `app/scripts/e2e-run-session.sh` which: # - launches the built binary, # - waits for CDP on :19222, # - prunes the prewarm `about:blank` target, # - downloads a chromedriver matching CEF's Chromium version, # - starts Appium and runs WDIO against `wdio.conf.ts`. # # Linux runs inside the project CI container under Xvfb (no native display). # macOS / Windows run on GitHub-hosted runners. # # Caching: pnpm store, Swatinem rust-cache, and CEF runtime download # (~400MB) are cached on every job. The CEF cache key matches what # build-desktop.yml uses so cross-workflow hits are possible. name: E2E (reusable) on: workflow_call: inputs: ref: description: Git ref (tag or SHA) to test. Release workflows pass the freshly-pushed staging/production tag here so pretest validates the exact commit the build matrix will check out, not main HEAD at workflow_dispatch time. Defaults to empty (checkout uses its own default, i.e. the workflow's triggering ref). type: string default: "" run_linux: description: Run the Linux (Xvfb / container) E2E job. type: boolean default: true run_macos: description: Run the macOS E2E job. type: boolean default: false run_windows: description: Run the Windows E2E job. type: boolean default: false full: description: When true, run the entire spec suite via `e2e-run-session.sh` (no spec arg). When false, run the selected desktop spec. Releases set this to true. type: boolean default: false spec_path: description: Spec path to run when full is false. type: string default: test/e2e/specs/mega-flow.spec.ts spec_label: description: Log label for the selected spec when full is false. type: string default: mega-flow permissions: # `actions: read` lets scripts/ci-cancel-aware.sh poll the run status so # cancelled builds inside container jobs stop themselves (docker exec # swallows the runner's signals). actions: read contents: read packages: read env: # Consumed by scripts/ci-cancel-aware.sh's cancellation watchdog. GH_TOKEN: ${{ github.token }} jobs: # Selected-spec gate for PR/push (full=false). The full-suite path lives in # `e2e-linux-full` below, which fans out across 4 parallel shards via # `e2e-run-all-flows.sh --suite=`. Splitting the two prevents the # smoke job from paying matrix overhead for a 2-spec run. e2e-linux: if: inputs.run_linux && !inputs.full name: E2E (Linux / Appium Chromium) runs-on: ubuntu-22.04 container: image: ghcr.io/tinyhumansai/openhuman_ci:latest timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v5 with: ref: ${{ inputs.ref }} fetch-depth: 1 persist-credentials: false submodules: recursive - name: Cache pnpm store uses: actions/cache@v5 with: path: ~/.local/share/pnpm/store key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} restore-keys: | pnpm-store-${{ runner.os }}- - name: Cache Rust build artifacts uses: Swatinem/rust-cache@v2 with: workspaces: | . -> target app/src-tauri -> target cache-on-failure: true key: e2e-linux-unified # CEF runtime download cache — matches the key shape used by # build-desktop.yml so a release build and an E2E run on the same # commit can share a single download. - name: Cache CEF binary distribution uses: actions/cache@v5 with: path: | ~/.cache/tauri-cef key: cef-x86_64-unknown-linux-gnu-${{ hashFiles('app/src-tauri/Cargo.toml') }} restore-keys: | cef-x86_64-unknown-linux-gnu- - name: Cache Appium global install uses: actions/cache@v5 with: path: | ~/.appium /usr/local/lib/node_modules/appium key: appium3-chromium-${{ runner.os }}-v2 - name: Install JS dependencies run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile - name: Ensure .env exists for E2E build run: | touch .env touch app/.env - name: Install Appium and chromium driver run: | if ! command -v appium >/dev/null 2>&1; then bash scripts/ci-cancel-aware.sh npm install -g appium@3 fi # appium-chromium-driver is loaded from ~/.appium and imports the # appium package from that tree at runtime. bash scripts/ci-cancel-aware.sh npm install --prefix "$HOME/.appium" appium@3 # `appium driver list --installed` can miss cached installs on some # Appium builds; install idempotently and ignore "already installed". bash scripts/ci-cancel-aware.sh appium driver install --source=npm appium-chromium-driver >/dev/null 2>&1 || true - name: Build E2E app run: bash scripts/ci-cancel-aware.sh pnpm --filter openhuman-app test:e2e:build - name: Run E2E (${{ inputs.spec_label }}) if: ${{ !inputs.full }} run: | bash scripts/ci-cancel-aware.sh \ xvfb-run -a --server-args="-screen 0 1280x960x24" \ bash app/scripts/e2e-run-session.sh "${{ inputs.spec_path }}" "${{ inputs.spec_label }}" - name: Upload E2E failure artifacts if: failure() uses: actions/upload-artifact@v5 with: name: e2e-failure-logs-${{ runner.os }}-${{ github.run_id }} path: | ${{ runner.temp }}/openhuman-e2e-app-*.log /tmp/openhuman-e2e-app-*.log app/test/e2e/artifacts/ retention-days: 7 if-no-files-found: ignore - name: Write job summary if: always() run: | echo "## E2E Results (${{ runner.os }})" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY if [ -f /tmp/e2e-summary.txt ]; then cat /tmp/e2e-summary.txt >> $GITHUB_STEP_SUMMARY else echo "No summary file found." >> $GITHUB_STEP_SUMMARY fi # Full-suite Linux is now build-once-then-fanout: one `build-linux-full` # job produces the binary + frontend dist + CEF runtime as a single # workflow artifact, and the 4 shard test jobs `needs:` that build and # download the artifact. This eliminates the parallel-shard cache race # (only the first shard would otherwise populate the binary/CEF caches, # the others would lose the race and rebuild) and guarantees the binary # and its libcef.so are always packaged together. build-linux-full: if: inputs.run_linux && inputs.full name: Build (Linux full) runs-on: ubuntu-22.04 container: image: ghcr.io/tinyhumansai/openhuman_ci:latest timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v5 with: ref: ${{ inputs.ref }} fetch-depth: 1 submodules: recursive - name: Cache pnpm store uses: actions/cache@v5 with: path: ~/.local/share/pnpm/store key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} restore-keys: | pnpm-store-${{ runner.os }}- - name: Cache Rust build artifacts uses: Swatinem/rust-cache@v2 with: workspaces: | . -> target app/src-tauri -> target cache-on-failure: true key: e2e-linux-unified - name: Cache CEF binary distribution uses: actions/cache@v5 with: # cef-dll-sys downloads into $CEF_PATH; ensure-tauri-cli.sh + # e2e-build.sh pin that to $HOME/Library/Caches/tauri-cef on # every OS, so the cache key/path live there too. path: | ~/Library/Caches/tauri-cef key: cef-x86_64-unknown-linux-gnu-v2-${{ hashFiles('app/src-tauri/Cargo.toml') }} restore-keys: | cef-x86_64-unknown-linux-gnu-v2- - name: Install JS dependencies run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile - name: Ensure .env exists for E2E build run: | touch .env touch app/.env - name: Build E2E app run: bash scripts/ci-cancel-aware.sh pnpm --filter openhuman-app test:e2e:build - name: Package build artifact run: | # Stage everything the test shards need at the layout they expect # under a single directory, so the consumer can extract straight # into the workspace + $HOME. STAGE="$(mktemp -d)" mkdir -p "$STAGE/repo/app/src-tauri/target/debug" mkdir -p "$STAGE/repo/app/dist" mkdir -p "$STAGE/home/Library/Caches" cp -a app/src-tauri/target/debug/OpenHuman "$STAGE/repo/app/src-tauri/target/debug/" cp -a app/dist/. "$STAGE/repo/app/dist/" cp -a "$HOME/Library/Caches/tauri-cef" "$STAGE/home/Library/Caches/tauri-cef" tar -czf e2e-build-linux.tar.gz -C "$STAGE" repo home ls -lh e2e-build-linux.tar.gz - name: Upload build artifact uses: actions/upload-artifact@v5 with: name: e2e-build-linux-${{ github.run_id }} path: e2e-build-linux.tar.gz retention-days: 1 if-no-files-found: error e2e-linux-full: if: inputs.run_linux && inputs.full needs: build-linux-full name: E2E (Linux full / ${{ matrix.shard.name }}) runs-on: ubuntu-22.04 container: image: ghcr.io/tinyhumansai/openhuman_ci:latest timeout-minutes: 30 strategy: fail-fast: false matrix: shard: - { name: foundation, suites: "auth,navigation,system" } - { name: chat, suites: "chat,skills,journeys" } - { name: providers, suites: "providers,notifications" } - { name: webhooks, suites: "webhooks" } - { name: connectors, suites: "connectors" } - { name: commerce, suites: "payments,settings" } steps: - name: Checkout code uses: actions/checkout@v5 with: ref: ${{ inputs.ref }} fetch-depth: 1 submodules: recursive - name: Cache pnpm store uses: actions/cache@v5 with: path: ~/.local/share/pnpm/store key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} restore-keys: | pnpm-store-${{ runner.os }}- - name: Cache Appium global install uses: actions/cache@v5 with: path: | ~/.appium /usr/local/lib/node_modules/appium key: appium3-chromium-${{ runner.os }}-v2 - name: Install JS dependencies (for test harness only) run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile - name: Install Appium and chromium driver run: | if ! command -v appium >/dev/null 2>&1; then bash scripts/ci-cancel-aware.sh npm install -g appium@3 fi bash scripts/ci-cancel-aware.sh npm install --prefix "$HOME/.appium" appium@3 bash scripts/ci-cancel-aware.sh appium driver install --source=npm appium-chromium-driver >/dev/null 2>&1 || true - name: Download build artifact uses: actions/download-artifact@v5 with: name: e2e-build-linux-${{ github.run_id }} path: . - name: Restore build artifact into workspace + $HOME run: | tar -xzf e2e-build-linux.tar.gz # The artifact contains: repo/{app/src-tauri/target/debug/OpenHuman, app/dist/...} # and home/Library/Caches/tauri-cef/... mkdir -p app/src-tauri/target/debug app/dist "$HOME/Library/Caches" cp -a repo/app/src-tauri/target/debug/OpenHuman app/src-tauri/target/debug/ cp -a repo/app/dist/. app/dist/ cp -a home/Library/Caches/tauri-cef "$HOME/Library/Caches/" rm -rf repo home e2e-build-linux.tar.gz chmod +x app/src-tauri/target/debug/OpenHuman ls -la app/src-tauri/target/debug/OpenHuman app/dist | head ls -la "$HOME/Library/Caches/tauri-cef" | head - name: Run E2E shard (${{ matrix.shard.name }} — suites=${{ matrix.shard.suites }}) env: E2E_BAIL_ON_FAILURE: ${{ vars.E2E_BAIL_ON_FAILURE || '' }} run: | export CEF_PATH="$HOME/Library/Caches/tauri-cef" BAIL_FLAG="" if [[ "${E2E_BAIL_ON_FAILURE:-}" == "1" ]]; then BAIL_FLAG="--bail" fi bash scripts/ci-cancel-aware.sh \ xvfb-run -a --server-args="-screen 0 1280x960x24" \ bash app/scripts/e2e-run-all-flows.sh --skip-preflight \ --suite=${{ matrix.shard.suites }} $BAIL_FLAG - name: Upload E2E failure artifacts if: failure() uses: actions/upload-artifact@v5 with: name: e2e-failure-logs-${{ runner.os }}-${{ matrix.shard.name }}-${{ github.run_id }} path: | ${{ runner.temp }}/openhuman-e2e-app-*.log /tmp/openhuman-e2e-app-*.log app/test/e2e/artifacts/ retention-days: 7 if-no-files-found: ignore - name: Write job summary if: always() run: | echo "## E2E Results (${{ runner.os }} / ${{ matrix.shard.name }})" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY if [ -f /tmp/e2e-summary.txt ]; then cat /tmp/e2e-summary.txt >> $GITHUB_STEP_SUMMARY else echo "No summary file found." >> $GITHUB_STEP_SUMMARY fi # Rust-side E2E counterpart to the Tauri runs above. Same Linux-only # scope (CI does not run this on macOS or Windows — the Rust core is # platform-independent, so one OS is enough signal). Boots the same # `scripts/mock-api-server.mjs` the Tauri specs hit, then runs every # `tests/*_e2e.rs` suite against it. rust-e2e-linux: if: inputs.run_linux name: E2E (Linux / Rust integration suite) runs-on: ubuntu-22.04 container: image: ghcr.io/tinyhumansai/openhuman_ci:latest timeout-minutes: 30 env: CARGO_INCREMENTAL: "0" RUST_BACKTRACE: "1" steps: - name: Checkout code uses: actions/checkout@v5 with: ref: ${{ inputs.ref }} fetch-depth: 1 persist-credentials: false submodules: recursive - name: Cache pnpm store uses: actions/cache@v5 with: path: ~/.local/share/pnpm/store key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} restore-keys: | pnpm-store-${{ runner.os }}- - name: Cache Rust build artifacts uses: Swatinem/rust-cache@v2 with: workspaces: . -> target cache-on-failure: true key: rust-e2e-linux - name: Install JS dependencies run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile - name: Ensure .env exists for tests run: | touch .env touch app/.env - name: Run Rust E2E suite (tests/*_e2e.rs vs mock backend) run: bash scripts/ci-cancel-aware.sh pnpm test:rust:e2e # No artifact uploads here either — same release-workflow reuse # concern as the Tauri job above. Mock-backend log lives at # /tmp/openhuman-rust-e2e-mock.log for local docker repro. e2e-macos: if: inputs.run_macos && !inputs.full name: E2E (macOS / Appium Chromium) runs-on: macos-latest timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v5 with: ref: ${{ inputs.ref }} fetch-depth: 1 persist-credentials: false submodules: recursive - name: Install pnpm uses: pnpm/action-setup@v4 - name: Setup Node.js 24.x uses: actions/setup-node@v5 with: node-version: 24.x cache: pnpm - name: Install Rust (rust-toolchain.toml) uses: dtolnay/rust-toolchain@1.93.0 with: # macos-latest is arm64, but the vendored tauri-cli's build.rs # compiles a CEF helper for x86_64-apple-darwin (universal binary), # so the x86_64 libstd must be installed too. targets: x86_64-apple-darwin - name: Verify cargo resolves to real toolchain run: | rustup default 1.93.0 || true which cargo # `cargo --version` in the repo root materializes the # rust-toolchain.toml override toolchain (1.96.1), which supersedes the # dtolnay-installed 1.93.0. The action's `targets:` input added # x86_64-apple-darwin to 1.93.0, NOT the override toolchain the CEF # universal-helper build actually uses — so add it to the active # (override) toolchain here or the cross-compile fails with E0463 # "can't find crate for `core`". cargo --version rustup target add x86_64-apple-darwin - name: Cache Rust build artifacts uses: Swatinem/rust-cache@v2 with: workspaces: | . -> target app/src-tauri -> target cache-on-failure: true key: e2e-macos-unified-v2 - name: Cache CEF binary distribution uses: actions/cache@v5 with: path: | ~/Library/Caches/tauri-cef key: cef-aarch64-apple-darwin-${{ hashFiles('app/src-tauri/Cargo.toml') }} restore-keys: | cef-aarch64-apple-darwin- - name: Cache Appium global install uses: actions/cache@v5 with: path: | ~/.appium key: appium3-chromium-${{ runner.os }}-v2 - name: Install JS dependencies run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile - name: Ensure .env exists for E2E build run: | touch .env touch app/.env - name: Install Appium and chromium driver run: | if ! command -v appium >/dev/null 2>&1; then bash scripts/ci-cancel-aware.sh npm install -g appium@3 fi # appium-chromium-driver is loaded from ~/.appium and imports the # appium package from that tree at runtime. bash scripts/ci-cancel-aware.sh npm install --prefix "$HOME/.appium" appium@3 # `appium driver list --installed` can miss cached installs on some # Appium builds; install idempotently and ignore "already installed". bash scripts/ci-cancel-aware.sh appium driver install --source=npm appium-chromium-driver >/dev/null 2>&1 || true - name: Build E2E app run: bash scripts/ci-cancel-aware.sh pnpm --filter openhuman-app test:e2e:build # macOS rejects dynamic-framework loads from unsigned bundles — adhoc # signing satisfies the loader without a real developer-ID cert. - name: Adhoc-sign the .app bundle run: | codesign --force --deep --sign - \ app/src-tauri/target/debug/bundle/macos/OpenHuman.app codesign --verify --deep --verbose=2 \ app/src-tauri/target/debug/bundle/macos/OpenHuman.app - name: Run E2E (${{ inputs.spec_label }}) run: | bash scripts/ci-cancel-aware.sh bash app/scripts/e2e-run-session.sh "${{ inputs.spec_path }}" "${{ inputs.spec_label }}" # Artifact uploads intentionally omitted — see e2e-linux for the # reusable-workflow-is-also-used-by-releases rationale. e2e-windows: if: inputs.run_windows && !inputs.full name: E2E (Windows / Appium Chromium) # Pin to 2022 for the CEF/Appium harness. As of June 2026, # windows-latest resolves to Windows Server 2025 / VS 2026, where # whisper-rs-sys' CMake build exits with 0xc0000142 before the harness can # launch. runs-on: windows-2022 timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v5 with: ref: ${{ inputs.ref }} fetch-depth: 1 persist-credentials: false submodules: recursive - name: Install pnpm uses: pnpm/action-setup@v4 - name: Setup Node.js 24.x uses: actions/setup-node@v5 with: node-version: 24.x cache: pnpm - name: Install Rust (rust-toolchain.toml) uses: dtolnay/rust-toolchain@1.93.0 - name: Cache Rust build artifacts uses: Swatinem/rust-cache@v2 with: workspaces: | . -> target app/src-tauri -> target cache-on-failure: true key: e2e-windows-unified - name: Cache CEF binary distribution uses: actions/cache@v5 with: path: | ~/AppData/Local/tauri-cef key: cef-x86_64-pc-windows-msvc-${{ hashFiles('app/src-tauri/Cargo.toml') }} restore-keys: | cef-x86_64-pc-windows-msvc- - name: Cache Appium global install uses: actions/cache@v5 with: path: | ~/.appium key: appium3-chromium-${{ runner.os }}-v2 - name: Install JS dependencies run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile - name: Ensure .env exists for E2E build shell: bash run: | touch .env touch app/.env - name: Install Appium and chromium driver shell: bash run: | if ! command -v appium >/dev/null 2>&1; then bash scripts/ci-cancel-aware.sh npm install -g appium@3 fi # appium-chromium-driver is loaded from ~/.appium and imports the # appium package from that tree at runtime. bash scripts/ci-cancel-aware.sh npm install --prefix "$HOME/.appium" appium@3 # `appium driver list --installed` can miss cached installs on some # Appium builds; install idempotently and ignore "already installed". bash scripts/ci-cancel-aware.sh appium driver install --source=npm appium-chromium-driver >/dev/null 2>&1 || true - name: Build E2E app run: bash scripts/ci-cancel-aware.sh pnpm --filter openhuman-app test:e2e:build - name: Run E2E (${{ inputs.spec_label }}) shell: bash run: | bash scripts/ci-cancel-aware.sh bash app/scripts/e2e-run-session.sh "${{ inputs.spec_path }}" "${{ inputs.spec_label }}" # Artifact uploads intentionally omitted — see e2e-linux for the # reusable-workflow-is-also-used-by-releases rationale. # --------------------------------------------------------------------------- # Full-suite macOS — build-once-then-fanout, mirroring build-linux-full. # # Previously e2e-macos-full was a 6-shard matrix where each shard restored an # `actions/cache` of the .app bundle and rebuilt the full Tauri app on a cold # cache. On a cold cache that means up to 6 parallel full Tauri builds (only # the first shard to finish would populate the cache; the rest lose the race). # `build-macos-full` now builds the .app once and uploads it as a per-run # workflow artifact; the shards `needs:` it, download, adhoc-sign, and run. # --------------------------------------------------------------------------- build-macos-full: if: inputs.run_macos && inputs.full name: Build (macOS full) runs-on: macos-latest timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v5 with: ref: ${{ inputs.ref }} fetch-depth: 1 submodules: recursive - name: Install pnpm uses: pnpm/action-setup@v4 - name: Setup Node.js 24.x uses: actions/setup-node@v5 with: node-version: 24.x cache: pnpm - name: Install Rust (rust-toolchain.toml) uses: dtolnay/rust-toolchain@1.93.0 with: # macos-latest is arm64, but the vendored tauri-cli's build.rs # compiles a CEF helper for x86_64-apple-darwin (universal binary), # so the x86_64 libstd must be installed too. Without this the # build fails with E0463 "can't find crate for `core`". targets: x86_64-apple-darwin - name: Verify cargo resolves to real toolchain run: | rustup default 1.93.0 || true which cargo # `cargo --version` in the repo root materializes the # rust-toolchain.toml override toolchain (1.96.1), which supersedes the # dtolnay-installed 1.93.0. The action's `targets:` input added # x86_64-apple-darwin to 1.93.0, NOT the override toolchain the CEF # universal-helper build actually uses — so add it to the active # (override) toolchain here or the cross-compile fails with E0463 # "can't find crate for `core`". cargo --version rustup target add x86_64-apple-darwin - name: Cache Rust build artifacts uses: Swatinem/rust-cache@v2 with: workspaces: | . -> target app/src-tauri -> target cache-on-failure: true key: e2e-macos-unified-v2 - name: Cache CEF binary distribution uses: actions/cache@v5 with: path: | ~/Library/Caches/tauri-cef key: cef-aarch64-apple-darwin-${{ hashFiles('app/src-tauri/Cargo.toml') }} restore-keys: | cef-aarch64-apple-darwin- - name: Install JS dependencies run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile - name: Ensure .env exists for E2E build run: | touch .env touch app/.env - name: Build E2E app run: bash scripts/ci-cancel-aware.sh pnpm --filter openhuman-app test:e2e:build - name: Package build artifact run: | # Stage the same runner-visible layout Linux/Windows use. The .app is # self-contained for runtime, but e2e-run-session.sh also validates the # built frontend bundle before launching so shards need app/dist too. STAGE="$(mktemp -d)" mkdir -p "$STAGE/repo/app/src-tauri/target/debug/bundle/macos" mkdir -p "$STAGE/repo/app/dist" cp -a app/src-tauri/target/debug/bundle/macos/OpenHuman.app \ "$STAGE/repo/app/src-tauri/target/debug/bundle/macos/" cp -a app/dist/. "$STAGE/repo/app/dist/" tar -czf e2e-build-macos.tar.gz -C "$STAGE" repo ls -lh e2e-build-macos.tar.gz - name: Upload build artifact uses: actions/upload-artifact@v5 with: name: e2e-build-macos-${{ github.run_id }} path: e2e-build-macos.tar.gz retention-days: 1 if-no-files-found: error e2e-macos-full: if: inputs.run_macos && inputs.full needs: build-macos-full name: E2E (macOS full / ${{ matrix.shard.name }}) runs-on: macos-latest timeout-minutes: 30 strategy: fail-fast: false matrix: shard: - { name: foundation, suites: "auth,navigation,system" } - { name: chat, suites: "chat,skills,journeys" } - { name: providers, suites: "providers,notifications" } - { name: webhooks, suites: "webhooks" } - { name: connectors, suites: "connectors" } - { name: commerce, suites: "payments,settings" } steps: - name: Checkout code uses: actions/checkout@v5 with: ref: ${{ inputs.ref }} fetch-depth: 1 submodules: recursive - name: Install pnpm uses: pnpm/action-setup@v4 - name: Setup Node.js 24.x uses: actions/setup-node@v5 with: node-version: 24.x cache: pnpm - name: Cache Appium global install uses: actions/cache@v5 with: path: | ~/.appium key: appium3-chromium-${{ runner.os }}-v2 - name: Install JS dependencies (for test harness only) run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile - name: Install Appium and chromium driver run: | if ! command -v appium >/dev/null 2>&1; then bash scripts/ci-cancel-aware.sh npm install -g appium@3 fi bash scripts/ci-cancel-aware.sh npm install --prefix "$HOME/.appium" appium@3 bash scripts/ci-cancel-aware.sh appium driver install --source=npm appium-chromium-driver >/dev/null 2>&1 || true - name: Download build artifact uses: actions/download-artifact@v5 with: name: e2e-build-macos-${{ github.run_id }} path: . - name: Restore .app bundle into workspace run: | tar -xzf e2e-build-macos.tar.gz mkdir -p app/src-tauri/target/debug/bundle/macos mkdir -p app/dist cp -a repo/app/src-tauri/target/debug/bundle/macos/OpenHuman.app \ app/src-tauri/target/debug/bundle/macos/ cp -a repo/app/dist/. app/dist/ rm -rf repo e2e-build-macos.tar.gz ls -la app/src-tauri/target/debug/bundle/macos/OpenHuman.app | head ls -la app/dist | head # macOS rejects dynamic-framework loads from unsigned bundles — adhoc # signing satisfies the loader without a real developer-ID cert. A # bundle restored from the build artifact also needs (re-)signing on this # runner; codesign is idempotent. - name: Adhoc-sign the .app bundle run: | codesign --force --deep --sign - \ app/src-tauri/target/debug/bundle/macos/OpenHuman.app codesign --verify --deep --verbose=2 \ app/src-tauri/target/debug/bundle/macos/OpenHuman.app - name: Run E2E shard (${{ matrix.shard.name }} — suites=${{ matrix.shard.suites }}) env: E2E_BAIL_ON_FAILURE: ${{ vars.E2E_BAIL_ON_FAILURE || '' }} run: | BAIL_FLAG="" if [[ "${E2E_BAIL_ON_FAILURE:-}" == "1" ]]; then BAIL_FLAG="--bail" fi bash scripts/ci-cancel-aware.sh bash app/scripts/e2e-run-all-flows.sh --skip-preflight \ --suite=${{ matrix.shard.suites }} $BAIL_FLAG - name: Upload E2E failure artifacts if: failure() uses: actions/upload-artifact@v5 with: name: e2e-failure-logs-${{ runner.os }}-${{ matrix.shard.name }}-${{ github.run_id }} path: | ${{ runner.temp }}/openhuman-e2e-app-*.log /tmp/openhuman-e2e-app-*.log app/test/e2e/artifacts/ retention-days: 7 if-no-files-found: ignore - name: Write job summary if: always() run: | echo "## E2E Results (${{ runner.os }} / ${{ matrix.shard.name }})" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY if [ -f /tmp/e2e-summary.txt ]; then cat /tmp/e2e-summary.txt >> $GITHUB_STEP_SUMMARY else echo "No summary file found." >> $GITHUB_STEP_SUMMARY fi # --------------------------------------------------------------------------- # Full-suite Windows — build-once-then-fanout, mirroring build-linux-full. # `build-windows-full` builds the .exe + frontend dist + CEF runtime once and # uploads them as a per-run workflow artifact; the shards `needs:` it and # download, instead of each shard rebuilding on a cold binary cache. # --------------------------------------------------------------------------- build-windows-full: if: inputs.run_windows && inputs.full name: Build (Windows full) # Keep the full-suite build on the same stable image as the smoke lane. runs-on: windows-2022 timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v5 with: ref: ${{ inputs.ref }} fetch-depth: 1 submodules: recursive - name: Install pnpm uses: pnpm/action-setup@v4 - name: Setup Node.js 24.x uses: actions/setup-node@v5 with: node-version: 24.x cache: pnpm - name: Install Rust (rust-toolchain.toml) uses: dtolnay/rust-toolchain@1.93.0 - name: Cache Rust build artifacts uses: Swatinem/rust-cache@v2 with: workspaces: | . -> target app/src-tauri -> target cache-on-failure: true key: e2e-windows-unified - name: Cache CEF binary distribution uses: actions/cache@v5 with: # ensure-tauri-cli.sh + e2e-build.sh export # CEF_PATH=$HOME/Library/Caches/tauri-cef regardless of OS; under Git # Bash on Windows that resolves under the user profile. path: | ~/Library/Caches/tauri-cef key: cef-x86_64-pc-windows-msvc-v2-${{ hashFiles('app/src-tauri/Cargo.toml') }} restore-keys: | cef-x86_64-pc-windows-msvc-v2- - name: Install JS dependencies run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile - name: Ensure .env exists for E2E build shell: bash run: | touch .env touch app/.env - name: Build E2E app run: bash scripts/ci-cancel-aware.sh pnpm --filter openhuman-app test:e2e:build - name: Package build artifact shell: bash run: | # Windows is NOT self-contained: the shard needs the .exe, the # frontend dist, and the CEF runtime (exported via CEF_PATH at run # time). Stage all three at the layout the shard restores into. STAGE="$(mktemp -d)" mkdir -p "$STAGE/repo/app/src-tauri/target/debug" mkdir -p "$STAGE/repo/app/dist" mkdir -p "$STAGE/home/Library/Caches" cp -a app/src-tauri/target/debug/OpenHuman.exe "$STAGE/repo/app/src-tauri/target/debug/" cp -a app/dist/. "$STAGE/repo/app/dist/" cp -a "$HOME/Library/Caches/tauri-cef" "$STAGE/home/Library/Caches/tauri-cef" tar -czf e2e-build-windows.tar.gz -C "$STAGE" repo home ls -lh e2e-build-windows.tar.gz - name: Upload build artifact uses: actions/upload-artifact@v5 with: name: e2e-build-windows-${{ github.run_id }} path: e2e-build-windows.tar.gz retention-days: 1 if-no-files-found: error e2e-windows-full: if: inputs.run_windows && inputs.full needs: build-windows-full name: E2E (Windows full / ${{ matrix.shard.name }}) # Keep full-suite shards on the same stable image as the build job. runs-on: windows-2022 timeout-minutes: 30 strategy: fail-fast: false matrix: shard: - { name: foundation, suites: "auth,navigation,system" } - { name: chat, suites: "chat,skills,journeys" } - { name: providers, suites: "providers,notifications" } - { name: webhooks, suites: "webhooks" } - { name: connectors, suites: "connectors" } - { name: commerce, suites: "payments,settings" } steps: - name: Checkout code uses: actions/checkout@v5 with: ref: ${{ inputs.ref }} fetch-depth: 1 submodules: recursive - name: Install pnpm uses: pnpm/action-setup@v4 - name: Setup Node.js 24.x uses: actions/setup-node@v5 with: node-version: 24.x cache: pnpm - name: Cache Appium global install uses: actions/cache@v5 with: path: | ~/.appium key: appium3-chromium-${{ runner.os }}-v2 - name: Install JS dependencies (for test harness only) run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile - name: Install Appium and chromium driver shell: bash run: | if ! command -v appium >/dev/null 2>&1; then bash scripts/ci-cancel-aware.sh npm install -g appium@3 fi bash scripts/ci-cancel-aware.sh npm install --prefix "$HOME/.appium" appium@3 bash scripts/ci-cancel-aware.sh appium driver install --source=npm appium-chromium-driver >/dev/null 2>&1 || true - name: Download build artifact uses: actions/download-artifact@v5 with: name: e2e-build-windows-${{ github.run_id }} path: . - name: Restore build artifact into workspace + $HOME shell: bash run: | tar -xzf e2e-build-windows.tar.gz mkdir -p app/src-tauri/target/debug app/dist "$HOME/Library/Caches" cp -a repo/app/src-tauri/target/debug/OpenHuman.exe app/src-tauri/target/debug/ cp -a repo/app/dist/. app/dist/ cp -a home/Library/Caches/tauri-cef "$HOME/Library/Caches/" rm -rf repo home e2e-build-windows.tar.gz ls -la app/src-tauri/target/debug/OpenHuman.exe app/dist | head - name: Run E2E shard (${{ matrix.shard.name }} — suites=${{ matrix.shard.suites }}) shell: bash env: E2E_BAIL_ON_FAILURE: ${{ vars.E2E_BAIL_ON_FAILURE || '' }} run: | # e2e-build.sh + ensure-tauri-cli.sh always download CEF to # $HOME/Library/Caches/tauri-cef regardless of OS. export CEF_PATH="$HOME/Library/Caches/tauri-cef" BAIL_FLAG="" if [[ "${E2E_BAIL_ON_FAILURE:-}" == "1" ]]; then BAIL_FLAG="--bail" fi bash scripts/ci-cancel-aware.sh bash app/scripts/e2e-run-all-flows.sh --skip-preflight \ --suite=${{ matrix.shard.suites }} $BAIL_FLAG - name: Upload E2E failure artifacts if: failure() uses: actions/upload-artifact@v5 with: name: e2e-failure-logs-${{ runner.os }}-${{ matrix.shard.name }}-${{ github.run_id }} # e2e-run-session.sh writes its app log to `${RUNNER_TEMP:-${TMPDIR:-/tmp}}`. # On Windows runners RUNNER_TEMP resolves to D:\a\_temp, not /tmp. path: | ${{ runner.temp }}/openhuman-e2e-app-*.log app/test/e2e/artifacts/ retention-days: 7 if-no-files-found: ignore - name: Write job summary if: always() shell: bash run: | echo "## E2E Results (${{ runner.os }} / ${{ matrix.shard.name }})" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY if [ -f /tmp/e2e-summary.txt ]; then cat /tmp/e2e-summary.txt >> $GITHUB_STEP_SUMMARY else echo "No summary file found." >> $GITHUB_STEP_SUMMARY fi