Repository navigation
fix(mobile): give the PR Review paste control a visible result on every tap #22977
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: kilo-app CI | |
| # The mobile suite jobs are required checks on main, so this workflow runs on | |
| # every PR and every main push (no path filters) and the `mobile-changes` job | |
| # gates the suite work. An unrelated PR or push still reports the suite checks | |
| # as skipped (a skipped job satisfies a required check) instead of leaving them | |
| # "Expected". The release workflow re-runs the suite through `workflow_call`. | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| merge_group: | |
| workflow_call: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| # Always runs: the required suite checks must report on every PR and every | |
| # main push. The detection step diffs against the PR's base sha or, on a main | |
| # push, `github.event.before`; when neither is available (workflow_dispatch | |
| # release), the suite runs (a release must validate). | |
| mobile-changes: | |
| runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }} | |
| timeout-minutes: 5 | |
| outputs: | |
| mobile: ${{ steps.detect.outputs.mobile }} | |
| steps: | |
| # Full history so the change-detection step can diff against the PR's | |
| # base sha or the pushed commit's parent. | |
| - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 | |
| - name: Setup Node | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version-file: '.nvmrc' | |
| # The suite jobs always report (required checks); only the suite's own | |
| # paths justify the heavy work. The patterns are pinned by | |
| # scripts/stacked-ci.test.mjs, which ci.yml's `ci-wiring` job runs. | |
| # A lockfile change matches the patterns first, so the detector below | |
| # never parses the lockfile and needs no installed dependencies. | |
| - name: Detect mobile-relevant changes | |
| id: detect | |
| run: | | |
| set -euo pipefail | |
| # On a PR, diff against the base sha; on a main push (or a | |
| # workflow_call riding a push event), diff against | |
| # `github.event.before`. If neither object is available, run the | |
| # suite rather than skip it: a false "no changes" would break the | |
| # required checks' promise, while a redundant run is harmless. | |
| # A merge queue entry diffs against the group's base: the PRs in the | |
| # group passed the suite on their own branches. | |
| base="${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}" | |
| if ! git rev-parse --verify "${base}^{commit}" > /dev/null 2>&1; then | |
| base="${{ github.event.before }}" | |
| fi | |
| if ! git rev-parse --verify "${base}^{commit}" > /dev/null 2>&1; then | |
| echo "mobile=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if git diff --name-only "$base" HEAD \ | |
| | grep -Eq '^apps/mobile/|^packages/trpc/|^apps/web/src/routers/|^packages/web-shared/src/routers/|^packages/app-shared/|^packages/kilo-chat/|^packages/kilo-chat-hooks/|^packages/event-service/|^packages/notifications/|^packages/cloud-agent-sdk/|^pnpm-lock\.yaml$|^\.github/workflows/kilo-app-ci\.yml$|^\.github/workflows/ci\.yml$|^scripts/stacked-ci\.test\.mjs$'; then | |
| echo "mobile=true" >> "$GITHUB_OUTPUT" | |
| # The app also reads every workspace it depends on, directly or | |
| # through a relative import, and its tests may read other | |
| # workspaces' files as data. A detection error runs the suite. | |
| elif ! affected=$(node scripts/affected-workspaces.mjs "$(git rev-parse "${base}^{commit}")") \ | |
| || ! selected=$(jq -r '.full or (.packages + .test_only_packages | index("kilo-app") != null)' <<< "$affected") \ | |
| || [ "$selected" != false ]; then | |
| echo "mobile=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "mobile=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| typecheck: | |
| needs: mobile-changes | |
| if: needs.mobile-changes.outputs.mobile == 'true' | |
| runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }} | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 | |
| - name: Mount pnpm store | |
| uses: ./.github/actions/pnpm-store | |
| - name: Setup Node | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version-file: '.nvmrc' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Cache compiler plugins | |
| uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 | |
| with: | |
| path: node_modules/.cache/ttsc/plugins | |
| key: ${{ runner.os }}-${{ runner.arch }}-ttsc-plugins-${{ hashFiles('pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-${{ runner.arch }}-ttsc-plugins- | |
| - name: Restore or build tRPC declarations | |
| uses: ./.github/actions/trpc-declarations | |
| - name: Build harness SDK | |
| run: pnpm --filter @kilocode/harness-sdk run build | |
| - name: Typecheck | |
| run: pnpm --filter kilo-app run typecheck | |
| # The release preflight is the only other job that evaluates app.config.ts, | |
| # and it runs nightly. Read the config here so a broken import fails the PR. | |
| - name: Read Expo config | |
| run: pnpm --filter kilo-app exec expo config --type public > /dev/null | |
| lint: | |
| needs: mobile-changes | |
| if: needs.mobile-changes.outputs.mobile == 'true' | |
| runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }} | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 | |
| - name: Mount pnpm store | |
| uses: ./.github/actions/pnpm-store | |
| - name: Setup Node | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version-file: '.nvmrc' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Cache compiler plugins | |
| uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 | |
| with: | |
| path: node_modules/.cache/ttsc/plugins | |
| key: ${{ runner.os }}-${{ runner.arch }}-ttsc-plugins-${{ hashFiles('pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-${{ runner.arch }}-ttsc-plugins- | |
| - name: Restore or build tRPC declarations | |
| uses: ./.github/actions/trpc-declarations | |
| - name: Build harness SDK | |
| run: pnpm --filter @kilocode/harness-sdk run build | |
| - name: Lint | |
| run: pnpm --filter kilo-app run lint | |
| i18n-leftover: | |
| needs: mobile-changes | |
| if: needs.mobile-changes.outputs.mobile == 'true' | |
| runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }} | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 | |
| - name: Mount pnpm store | |
| uses: ./.github/actions/pnpm-store | |
| - name: Setup Node | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version-file: '.nvmrc' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # Neither check reads types or built packages: the i18n oxlint config | |
| # is not type-aware. | |
| - name: Check i18n catalogs and leftover copy | |
| run: pnpm --filter kilo-app run check:i18n | |
| - name: Check for silently-dropped NativeWind classes | |
| run: pnpm --filter kilo-app run check:classes | |
| - name: Test the NativeWind class checker | |
| run: node --test tools/nativewind/check-classes.test.mjs | |
| - name: Test the i18n catalog checker | |
| run: node --test tools/i18n/check-catalogs.test.mjs | |
| format-check: | |
| needs: mobile-changes | |
| if: needs.mobile-changes.outputs.mobile == 'true' | |
| runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }} | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 | |
| - name: Mount pnpm store | |
| uses: ./.github/actions/pnpm-store | |
| - name: Setup Node | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version-file: '.nvmrc' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Format check | |
| run: | | |
| pnpm --filter kilo-app run format:check || { | |
| echo "::error::oxfmt formatting check failed. Run 'cd apps/mobile && pnpm format' locally and commit the changes." | |
| exit 1 | |
| } | |
| check-unused: | |
| needs: mobile-changes | |
| if: needs.mobile-changes.outputs.mobile == 'true' | |
| runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }} | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 | |
| - name: Mount pnpm store | |
| uses: ./.github/actions/pnpm-store | |
| - name: Setup Node | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version-file: '.nvmrc' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Cache compiler plugins | |
| uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 | |
| with: | |
| path: node_modules/.cache/ttsc/plugins | |
| key: ${{ runner.os }}-${{ runner.arch }}-ttsc-plugins-${{ hashFiles('pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-${{ runner.arch }}-ttsc-plugins- | |
| - name: Build harness SDK | |
| run: pnpm --filter @kilocode/harness-sdk run build | |
| - name: Check unused exports | |
| run: pnpm --filter kilo-app run check:unused | |
| # Eight shards run the mobile Vitest suite in parallel; the `test` job below | |
| # reports the combined result under the required check name. | |
| # | |
| # The shard's `Test` step is CPU-bound and the jobs start together, so the | |
| # runner size sets the wall clock of the whole row: measured on a work VM the | |
| # step drops from 84 s to 36 s per shard (2 vCPU -> 4 vCPU), against a ~15 s | |
| # fixed setup that does not change. Four vCPU pays for itself: the mobile row | |
| # gets shorter while its vCPU-seconds rise by about 2%. | |
| # | |
| # The label is `vars.RUNNER_MEDIUM_LABEL` (blacksmith-4vcpu-ubuntu-2404, 4 vCPU | |
| # and 16 GB; a shard peaks at 0.6 GB). Repo variables are not exposed to fork | |
| # pull requests, so the job falls back to `ubuntu-latest` there. | |
| mobile-test: | |
| needs: mobile-changes | |
| if: needs.mobile-changes.outputs.mobile == 'true' | |
| name: mobile-test (${{ matrix.shard }}/8) | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2, 3, 4, 5, 6, 7, 8] | |
| runs-on: ${{ vars.RUNNER_MEDIUM_LABEL || 'ubuntu-latest' }} | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 | |
| - name: Mount pnpm store | |
| uses: ./.github/actions/pnpm-store | |
| - name: Setup Node | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version-file: '.nvmrc' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Cache compiler plugins | |
| uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 | |
| with: | |
| path: node_modules/.cache/ttsc/plugins | |
| key: ${{ runner.os }}-${{ runner.arch }}-ttsc-plugins-${{ hashFiles('pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-${{ runner.arch }}-ttsc-plugins- | |
| # The tests import the harness SDK build at run time. The tRPC build | |
| # holds only type declarations, which Vitest does not read. | |
| - name: Build harness SDK | |
| run: pnpm --filter @kilocode/harness-sdk run build | |
| - name: Test | |
| run: pnpm --filter kilo-app run test --shard=${{ matrix.shard }}/8 | |
| # Required check. Skipped shards (no mobile change) pass; a failed or | |
| # cancelled shard fails this job. | |
| test: | |
| needs: [mobile-changes, mobile-test] | |
| if: ${{ !cancelled() }} | |
| runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }} | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Check test results | |
| env: | |
| RESULTS: ${{ toJSON(needs.*.result) }} | |
| run: | | |
| echo "$RESULTS" | |
| if jq -e 'any(.[]; . == "failure" or . == "cancelled")' <<< "$RESULTS" > /dev/null; then | |
| exit 1 | |
| fi |