mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-14 13:22:37 +00:00
uploads.github.com returned HTTP 500 five times during the v0.9.0-beta.1 release, each costing a ~50 minute rebuild. The upload step passed every artifact to one gh invocation, so a single failed asset discarded the rest and failed the job. Upload each asset separately with bounded retries, shared by the macOS, Linux and Windows jobs.
493 lines
16 KiB
YAML
493 lines
16 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
branches: [main]
|
|
merge_group:
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
|
|
env:
|
|
# CI does not use the CUDA execution provider, and the onnxruntime-node
|
|
# postinstall download from NuGet is large enough to make npm ci flaky.
|
|
ONNXRUNTIME_NODE_INSTALL: skip
|
|
|
|
jobs:
|
|
changes:
|
|
name: changes
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
outputs:
|
|
full: ${{ github.event_name != 'pull_request' || steps.filter.outputs.routing != 'false' || steps.filter.outputs.workspace != 'false' || steps.filter.outputs.ci != 'false' }}
|
|
format: ${{ steps.filter.outputs.format }}
|
|
quality: ${{ steps.filter.outputs.quality }}
|
|
hub: ${{ steps.filter.outputs.hub }}
|
|
server: ${{ steps.filter.outputs.server }}
|
|
desktop: ${{ steps.filter.outputs.desktop }}
|
|
app: ${{ steps.filter.outputs.app }}
|
|
sdk: ${{ steps.filter.outputs.sdk }}
|
|
browser: ${{ steps.filter.outputs.browser }}
|
|
relay: ${{ steps.filter.outputs.relay }}
|
|
cli: ${{ steps.filter.outputs.cli }}
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
|
|
- name: Detect affected CI contracts
|
|
id: filter
|
|
uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3.0.3
|
|
with:
|
|
filters: .github/ci-paths.yml
|
|
|
|
- name: Validate CI contracts
|
|
run: node --test scripts/ci-workflow.test.mjs scripts/daemon-launch-contract.test.mjs scripts/sync-fdroid-changelogs.test.mjs scripts/upload-release-assets.test.mjs
|
|
|
|
format:
|
|
name: format
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.format != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
cache: "npm"
|
|
|
|
- name: Install dependencies
|
|
run: node scripts/npm-retry.mjs ci
|
|
|
|
- name: Check formatting
|
|
run: npm run format:check
|
|
|
|
lint:
|
|
name: lint
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.quality != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
cache: "npm"
|
|
|
|
- name: Install dependencies
|
|
run: node scripts/npm-retry.mjs ci
|
|
|
|
- name: Lint lockfile
|
|
run: npx --yes lockfile-lint --path package-lock.json --type npm --allowed-hosts npm --validate-https --validate-integrity
|
|
|
|
- name: Verify dependency signatures
|
|
run: npm audit signatures
|
|
|
|
- name: Lint
|
|
run: npm run lint
|
|
|
|
typecheck:
|
|
name: typecheck
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.quality != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
cache: "npm"
|
|
|
|
- name: Install dependencies
|
|
run: node scripts/npm-retry.mjs ci
|
|
|
|
- name: Build server stack
|
|
run: npm run build:server
|
|
|
|
- name: Typecheck all packages
|
|
run: npm run typecheck
|
|
|
|
- name: Install website test browsers
|
|
run: npx playwright install --with-deps chromium webkit
|
|
|
|
- name: Verify website mockups
|
|
run: npm run test:e2e --workspace=@getpaseo/website
|
|
|
|
- name: Verify public package contents
|
|
run: |
|
|
npm pack --dry-run --ignore-scripts --workspace=@getpaseo/protocol
|
|
npm pack --dry-run --ignore-scripts --workspace=@getpaseo/client
|
|
npm pack --dry-run --ignore-scripts --workspace=@getpaseo/server
|
|
|
|
server-tests-ubuntu:
|
|
name: server-tests (ubuntu-latest)
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.server != 'false' || needs.changes.outputs.hub != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
steps: &server_test_steps
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
cache: "npm"
|
|
|
|
- name: Fetch origin/main (worktree tests)
|
|
if: ${{ needs.changes.outputs.full != 'false' || needs.changes.outputs.server != 'false' }}
|
|
run: git fetch --no-tags origin main:refs/remotes/origin/main
|
|
|
|
- name: Install dependencies
|
|
run: node scripts/npm-retry.mjs ci
|
|
|
|
- name: Install agent CLIs for provider tests
|
|
if: ${{ needs.changes.outputs.full != 'false' || needs.changes.outputs.server != 'false' }}
|
|
run: node scripts/npm-retry.mjs install -g @anthropic-ai/claude-code opencode-ai
|
|
|
|
- name: Build server dependencies
|
|
run: npm run build:server-deps
|
|
|
|
- name: Run server tests
|
|
if: ${{ needs.changes.outputs.full != 'false' || needs.changes.outputs.server != 'false' }}
|
|
run: npm run test --workspace=@getpaseo/server
|
|
env:
|
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
|
|
|
- name: Run Hub CLI/daemon contract
|
|
if: ${{ needs.changes.outputs.full == 'false' && needs.changes.outputs.server == 'false' && needs.changes.outputs.hub != 'false' }}
|
|
run: npm run test:hub-cli-contract --workspace=@getpaseo/server
|
|
|
|
server-tests-windows:
|
|
name: server-tests (windows-latest)
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.server != 'false' || needs.changes.outputs.hub != 'false') }}
|
|
runs-on: windows-latest
|
|
steps: *server_test_steps
|
|
|
|
desktop-tests-ubuntu:
|
|
name: desktop-tests (ubuntu-latest)
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.desktop != 'false') }}
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 30
|
|
permissions:
|
|
contents: read
|
|
steps: &desktop_test_steps
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
cache: "npm"
|
|
|
|
- name: Install dependencies with retry
|
|
run: node scripts/npm-retry.mjs ci
|
|
|
|
- name: Build server stack
|
|
run: npm run build:server
|
|
|
|
- name: Run desktop tests
|
|
run: npm run test --workspace=@getpaseo/desktop
|
|
|
|
- name: Install virtual display
|
|
if: ${{ runner.os == 'Linux' }}
|
|
run: sudo apt-get update && sudo apt-get install -y xvfb xauth xdotool
|
|
|
|
- name: Install Playwright browser
|
|
if: ${{ runner.os == 'Linux' }}
|
|
run: npx playwright install chromium
|
|
|
|
- name: Verify Windows daemon process control
|
|
if: ${{ runner.os == 'Windows' }}
|
|
run: npx vitest run packages/cli/src/commands/daemon/lifecycle.e2e.test.ts --bail=1 --maxWorkers=1 -t "Windows unbound|managed two-home"
|
|
|
|
- name: Run desktop lifecycle E2E (Linux)
|
|
if: ${{ runner.os == 'Linux' }}
|
|
run: xvfb-run -a --server-args="-screen 0 1280x800x24" npm run test:e2e:lifecycle --workspace=@getpaseo/desktop
|
|
env:
|
|
PASEO_DESKTOP_LIFECYCLE_ARTIFACT_DIR: ${{ runner.temp }}/desktop-lifecycle
|
|
|
|
- name: Upload desktop lifecycle screenshots
|
|
uses: actions/upload-artifact@v4
|
|
if: ${{ !cancelled() && runner.os == 'Linux' }}
|
|
with:
|
|
name: desktop-lifecycle
|
|
path: ${{ runner.temp }}/desktop-lifecycle
|
|
if-no-files-found: ignore
|
|
retention-days: 7
|
|
|
|
- name: Run desktop lifecycle E2E (Windows)
|
|
if: ${{ runner.os == 'Windows' }}
|
|
run: npm run test:e2e:lifecycle --workspace=@getpaseo/desktop
|
|
|
|
- name: Run desktop renderer E2E
|
|
if: ${{ runner.os == 'Linux' }}
|
|
run: npm run test:e2e:renderer --workspace=@getpaseo/desktop
|
|
|
|
- name: Run desktop browser E2E
|
|
if: ${{ runner.os == 'Linux' }}
|
|
run: npm run test:e2e:browser-tabs --workspace=@getpaseo/desktop
|
|
env:
|
|
PASEO_DESKTOP_BROWSER_E2E_ARTIFACT_DIR: ${{ runner.temp }}/desktop-browser-e2e
|
|
|
|
- name: Upload desktop browser diagnostics
|
|
uses: actions/upload-artifact@v4
|
|
if: ${{ failure() && runner.os == 'Linux' }}
|
|
with:
|
|
name: desktop-browser-e2e
|
|
path: ${{ runner.temp }}/desktop-browser-e2e
|
|
if-no-files-found: ignore
|
|
retention-days: 7
|
|
|
|
desktop-tests-windows:
|
|
name: desktop-tests (windows-latest)
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.desktop != 'false') }}
|
|
runs-on: windows-latest
|
|
timeout-minutes: 30
|
|
permissions:
|
|
contents: read
|
|
steps: *desktop_test_steps
|
|
|
|
app-tests:
|
|
name: app-tests
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.app != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
cache: "npm"
|
|
|
|
- name: Install dependencies with retry
|
|
run: node scripts/npm-retry.mjs ci
|
|
|
|
- name: Install Playwright browsers
|
|
timeout-minutes: 10
|
|
run: npx playwright install chromium
|
|
|
|
- name: Build app dependencies
|
|
run: npm run build:app-deps
|
|
|
|
- name: Run app unit tests
|
|
run: npm run test --workspace=@getpaseo/app
|
|
|
|
sdk-tests:
|
|
name: sdk-tests
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.sdk != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
cache: "npm"
|
|
|
|
- name: Install dependencies
|
|
run: node scripts/npm-retry.mjs ci
|
|
|
|
- name: Build client dependencies
|
|
run: npm run build:relay && npm run build:client
|
|
|
|
- name: Build plugin SDK
|
|
run: npm run build:plugin
|
|
|
|
- name: Run plugin SDK tests
|
|
run: npm run test --workspace=@getpaseo/plugin
|
|
|
|
- name: Run protocol tests
|
|
run: npm run test --workspace=@getpaseo/protocol
|
|
|
|
- name: Run client tests
|
|
run: npm run test --workspace=@getpaseo/client
|
|
|
|
- name: Run highlight tests
|
|
run: npm run test --workspace=@getpaseo/highlight
|
|
|
|
- name: Typecheck client examples
|
|
run: npm run typecheck:examples --workspace=@getpaseo/client
|
|
|
|
playwright-1:
|
|
name: playwright (shard 1/4)
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.browser != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
PLAYWRIGHT_SHARD: "1/4"
|
|
PLAYWRIGHT_ARTIFACT: "1"
|
|
steps: &playwright_test_steps
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
cache: "npm"
|
|
|
|
- name: Install dependencies with retry
|
|
run: node scripts/npm-retry.mjs ci
|
|
|
|
- name: Install Playwright browsers
|
|
timeout-minutes: 10
|
|
run: npx playwright install chromium
|
|
|
|
- name: Build server stack
|
|
run: npm run build:server
|
|
|
|
- name: Build native app dependency
|
|
run: npm run build --workspace=@getpaseo/expo-two-way-audio
|
|
|
|
- name: Install agent CLIs for provider tests
|
|
run: node scripts/npm-retry.mjs install -g @anthropic-ai/claude-code @openai/codex@0.105.0 opencode-ai
|
|
|
|
- name: Run Playwright E2E tests
|
|
run: npm run test:e2e --workspace=@getpaseo/app -- --shard=${{ env.PLAYWRIGHT_SHARD }}
|
|
env:
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
# Weight file groups by observed duration; raw test counts overload
|
|
# early agent/Changes specs. Required check names remain unchanged.
|
|
PWTEST_SHARD_WEIGHTS: "111:172:137:160"
|
|
|
|
- name: Upload test artifacts
|
|
uses: actions/upload-artifact@v4
|
|
if: failure()
|
|
with:
|
|
name: playwright-results-${{ env.PLAYWRIGHT_ARTIFACT }}
|
|
path: |
|
|
packages/app/test-results/
|
|
packages/app/playwright-report/
|
|
retention-days: 7
|
|
|
|
playwright-2:
|
|
name: playwright (shard 2/4)
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.browser != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
PLAYWRIGHT_SHARD: "2/4"
|
|
PLAYWRIGHT_ARTIFACT: "2"
|
|
steps: *playwright_test_steps
|
|
|
|
playwright-3:
|
|
name: playwright (shard 3/4)
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.browser != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
PLAYWRIGHT_SHARD: "3/4"
|
|
PLAYWRIGHT_ARTIFACT: "3"
|
|
steps: *playwright_test_steps
|
|
|
|
playwright-4:
|
|
name: playwright (shard 4/4)
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.browser != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
PLAYWRIGHT_SHARD: "4/4"
|
|
PLAYWRIGHT_ARTIFACT: "4"
|
|
steps: *playwright_test_steps
|
|
|
|
relay-tests:
|
|
name: relay-tests
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.relay != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
cache: "npm"
|
|
|
|
- name: Install dependencies
|
|
run: node scripts/npm-retry.mjs ci
|
|
|
|
- name: Build relay
|
|
run: npm run build:relay
|
|
|
|
- name: Run relay tests
|
|
run: npm run test --workspace=@getpaseo/relay
|
|
|
|
cli-tests-1:
|
|
name: cli-tests (shard 1/3)
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.cli != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
PASEO_LOCAL_SPEECH_AUTO_DOWNLOAD: "0"
|
|
PASEO_DICTATION_ENABLED: "0"
|
|
PASEO_VOICE_MODE_ENABLED: "0"
|
|
PASEO_CLI_TEST_SHARD: "1"
|
|
PASEO_CLI_TEST_SHARD_TOTAL: "3"
|
|
steps: &cli_test_steps
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
cache: "npm"
|
|
|
|
- name: Install dependencies
|
|
run: node scripts/npm-retry.mjs ci
|
|
|
|
- name: Install agent CLIs for provider tests
|
|
run: node scripts/npm-retry.mjs install -g @anthropic-ai/claude-code @openai/codex@0.105.0 opencode-ai
|
|
|
|
# The local runner builds its dependencies and shards its own E2E files.
|
|
- name: Run CLI E2E tests
|
|
run: npm run test:local --workspace=@getpaseo/cli
|
|
|
|
# Source tests do not read PASEO_CLI_TEST_SHARD. Run them once, on the
|
|
# shortest E2E shard, after its runner has built the server stack.
|
|
- name: Run CLI source tests
|
|
if: ${{ env.PASEO_CLI_TEST_SHARD == '3' }}
|
|
run: npm run test:unit --workspace=@getpaseo/cli
|
|
|
|
cli-tests-2:
|
|
name: cli-tests (shard 2/3)
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.cli != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
PASEO_LOCAL_SPEECH_AUTO_DOWNLOAD: "0"
|
|
PASEO_DICTATION_ENABLED: "0"
|
|
PASEO_VOICE_MODE_ENABLED: "0"
|
|
PASEO_CLI_TEST_SHARD: "2"
|
|
PASEO_CLI_TEST_SHARD_TOTAL: "3"
|
|
steps: *cli_test_steps
|
|
|
|
cli-tests-3:
|
|
name: cli-tests (shard 3/3)
|
|
needs: changes
|
|
if: ${{ !cancelled() && (needs.changes.outputs.full != 'false' || needs.changes.outputs.cli != 'false') }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
PASEO_LOCAL_SPEECH_AUTO_DOWNLOAD: "0"
|
|
PASEO_DICTATION_ENABLED: "0"
|
|
PASEO_VOICE_MODE_ENABLED: "0"
|
|
PASEO_CLI_TEST_SHARD: "3"
|
|
PASEO_CLI_TEST_SHARD_TOTAL: "3"
|
|
steps: *cli_test_steps
|