feat(ui): group tool calls into trow summaries #2511
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: ci | |
| on: | |
| push: | |
| branches: [dev] | |
| pull_request: | |
| branches: [dev] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.ref == 'refs/heads/dev' && format('ci-{0}', github.run_id) || format('ci-{0}', github.event.pull_request.number || github.ref) }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| changes: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| docs_only: ${{ steps.filter.outputs.docs_only }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - id: filter | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }} | |
| HEAD_SHA: ${{ github.sha }} | |
| run: | | |
| set -euo pipefail | |
| is_docs_path() { | |
| case "$1" in | |
| README.md|README_CN.md|assets/readme/*|.github/ISSUE_TEMPLATE/*|.github/pull_request_template.md|packages/*/README.md|packages/opencode/specs/*) | |
| return 0 | |
| ;; | |
| *) | |
| return 1 | |
| ;; | |
| esac | |
| } | |
| docs_only=false | |
| if [ "$EVENT_NAME" = "workflow_dispatch" ]; then | |
| echo "docs_only=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then | |
| BASE_SHA="$(git rev-list --max-parents=0 HEAD | tail -n 1)" | |
| fi | |
| mapfile -t changes < <(git diff --name-status --find-renames --find-copies "$BASE_SHA" "$HEAD_SHA" --) | |
| if [ "${#changes[@]}" -gt 0 ]; then | |
| docs_only=true | |
| for change in "${changes[@]}"; do | |
| IFS=$'\t' read -r status path1 path2 <<< "$change" | |
| case "$status" in | |
| A*|M*|T*|D*) | |
| if ! is_docs_path "$path1"; then | |
| docs_only=false | |
| break | |
| fi | |
| ;; | |
| R*|C*) | |
| if ! is_docs_path "$path1" || ! is_docs_path "$path2"; then | |
| docs_only=false | |
| break | |
| fi | |
| ;; | |
| *) | |
| docs_only=false | |
| break | |
| ;; | |
| esac | |
| done | |
| fi | |
| echo "docs_only=$docs_only" >> "$GITHUB_OUTPUT" | |
| typecheck: | |
| needs: changes | |
| if: needs.changes.outputs.docs_only != 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| # Load-bearing: `bun install` runs `trustedDependencies` | |
| # postinstalls from the root package.json (electron, node-pty, | |
| # esbuild, tree-sitter at time of writing), which call `node` | |
| # explicitly. Do not drop without first patching those scripts. | |
| # Source of truth: grep `trustedDependencies` in package.json. (#70) | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # actions/setup-node@v6.4.0 | |
| with: | |
| node-version: "24" | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.13" | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} | |
| restore-keys: | | |
| bun-${{ runner.os }}- | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 | |
| with: | |
| path: .turbo/cache | |
| key: turbo-${{ runner.os }}-typecheck-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}-${{ github.sha }} | |
| restore-keys: | | |
| turbo-${{ runner.os }}-typecheck-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}- | |
| turbo-${{ runner.os }}-typecheck- | |
| - run: bun install --frozen-lockfile | |
| - name: typecheck | |
| run: bun turbo typecheck | |
| lint: | |
| needs: changes | |
| if: needs.changes.outputs.docs_only != 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| # Load-bearing: `bun install` runs `trustedDependencies` | |
| # postinstalls from the root package.json (electron, node-pty, | |
| # esbuild, tree-sitter at time of writing), which call `node` | |
| # explicitly. Do not drop without first patching those scripts. | |
| # Source of truth: grep `trustedDependencies` in package.json. (#70) | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # actions/setup-node@v6.4.0 | |
| with: | |
| node-version: "24" | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.13" | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} | |
| restore-keys: | | |
| bun-${{ runner.os }}- | |
| - run: bun install --frozen-lockfile | |
| - name: lint | |
| run: bun run lint:ci | |
| frontend-architecture: | |
| needs: changes | |
| if: needs.changes.outputs.docs_only != 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| # Load-bearing: `bun install` runs `trustedDependencies` | |
| # postinstalls from the root package.json (electron, node-pty, | |
| # esbuild, tree-sitter at time of writing), which call `node` | |
| # explicitly. Do not drop without first patching those scripts. | |
| # Source of truth: grep `trustedDependencies` in package.json. (#70) | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # actions/setup-node@v6.4.0 | |
| with: | |
| node-version: "24" | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.13" | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} | |
| restore-keys: | | |
| bun-${{ runner.os }}- | |
| - run: bun install --frozen-lockfile | |
| - name: frontend inventory | |
| run: | | |
| mkdir -p .artifacts/frontend-architecture | |
| node script/frontend-inventory.mjs --format json > .artifacts/frontend-architecture/frontend-inventory.json | |
| - name: LOC ratchet warnings | |
| env: | |
| BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }} | |
| HEAD_SHA: ${{ github.sha }} | |
| run: node script/frontend-inventory.mjs --check-baseline --base "$BASE_SHA" --head "$HEAD_SHA" | |
| unit-app: | |
| needs: changes | |
| if: needs.changes.outputs.docs_only != 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: read | |
| checks: write | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| # Load-bearing: `bun install` runs `trustedDependencies` | |
| # postinstalls from the root package.json (electron, node-pty, | |
| # esbuild, tree-sitter at time of writing), which call `node` | |
| # explicitly. Do not drop without first patching those scripts. | |
| # Source of truth: grep `trustedDependencies` in package.json. (#70) | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # actions/setup-node@v6.4.0 | |
| with: | |
| node-version: "24" | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.13" | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} | |
| restore-keys: | | |
| bun-${{ runner.os }}- | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 | |
| with: | |
| path: .turbo/cache | |
| key: turbo-${{ runner.os }}-unit-app-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}-${{ github.sha }} | |
| restore-keys: | | |
| turbo-${{ runner.os }}-unit-app-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}- | |
| turbo-${{ runner.os }}-unit-app- | |
| - run: bun install --frozen-lockfile | |
| - name: unit | |
| run: bun turbo test:ci --filter=@opencode-ai/app | |
| - name: Publish unit reports | |
| if: > | |
| always() && | |
| ( | |
| github.event_name != 'pull_request' || | |
| github.event.pull_request.head.repo.full_name == github.repository | |
| ) | |
| uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # mikepenz/action-junit-report@v6 | |
| with: | |
| report_paths: packages/app/.artifacts/unit/junit.xml | |
| check_name: unit results (app) | |
| detailed_summary: true | |
| include_time_in_summary: true | |
| fail_on_failure: false | |
| - name: Upload unit artifacts | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # actions/upload-artifact@v7 | |
| with: | |
| name: unit-app-${{ github.run_attempt }} | |
| include-hidden-files: true | |
| if-no-files-found: ignore | |
| retention-days: 7 | |
| path: packages/app/.artifacts/unit/junit.xml | |
| unit-ui: | |
| needs: changes | |
| if: needs.changes.outputs.docs_only != 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: read | |
| checks: write | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| # Load-bearing: `bun install` runs `trustedDependencies` | |
| # postinstalls from the root package.json (electron, node-pty, | |
| # esbuild, tree-sitter at time of writing), which call `node` | |
| # explicitly. Do not drop without first patching those scripts. | |
| # Source of truth: grep `trustedDependencies` in package.json. (#70) | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # actions/setup-node@v6.4.0 | |
| with: | |
| node-version: "24" | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.13" | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} | |
| restore-keys: | | |
| bun-${{ runner.os }}- | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 | |
| with: | |
| path: .turbo/cache | |
| key: turbo-${{ runner.os }}-unit-ui-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}-${{ github.sha }} | |
| restore-keys: | | |
| turbo-${{ runner.os }}-unit-ui-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}- | |
| turbo-${{ runner.os }}-unit-ui- | |
| - run: bun install --frozen-lockfile | |
| - name: unit | |
| run: bun turbo test:ci --filter=@opencode-ai/ui | |
| - name: Publish unit reports | |
| if: > | |
| always() && | |
| ( | |
| github.event_name != 'pull_request' || | |
| github.event.pull_request.head.repo.full_name == github.repository | |
| ) | |
| uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # mikepenz/action-junit-report@v6 | |
| with: | |
| report_paths: packages/ui/.artifacts/unit/junit.xml | |
| check_name: unit results (ui) | |
| detailed_summary: true | |
| include_time_in_summary: true | |
| fail_on_failure: false | |
| - name: Upload unit artifacts | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # actions/upload-artifact@v7 | |
| with: | |
| name: unit-ui-${{ github.run_attempt }} | |
| include-hidden-files: true | |
| if-no-files-found: ignore | |
| retention-days: 7 | |
| path: packages/ui/.artifacts/unit/junit.xml | |
| unit-opencode: | |
| needs: changes | |
| if: needs.changes.outputs.docs_only != 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: read | |
| checks: write | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| # Load-bearing: `bun install` runs `trustedDependencies` | |
| # postinstalls from the root package.json (electron, node-pty, | |
| # esbuild, tree-sitter at time of writing), which call `node` | |
| # explicitly. Do not drop without first patching those scripts. | |
| # Source of truth: grep `trustedDependencies` in package.json. (#70) | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # actions/setup-node@v6.4.0 | |
| with: | |
| node-version: "24" | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.13" | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} | |
| restore-keys: | | |
| bun-${{ runner.os }}- | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 | |
| with: | |
| path: .turbo/cache | |
| key: turbo-${{ runner.os }}-unit-opencode-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}-${{ github.sha }} | |
| restore-keys: | | |
| turbo-${{ runner.os }}-unit-opencode-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}- | |
| turbo-${{ runner.os }}-unit-opencode- | |
| - run: bun install --frozen-lockfile | |
| - name: unit | |
| run: bun turbo test:ci --filter=opencode | |
| - name: Publish unit reports | |
| if: > | |
| always() && | |
| ( | |
| github.event_name != 'pull_request' || | |
| github.event.pull_request.head.repo.full_name == github.repository | |
| ) | |
| uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # mikepenz/action-junit-report@v6 | |
| with: | |
| report_paths: packages/opencode/.artifacts/unit/junit.xml | |
| check_name: unit results (opencode) | |
| detailed_summary: true | |
| include_time_in_summary: true | |
| fail_on_failure: false | |
| - name: Upload unit artifacts | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # actions/upload-artifact@v7 | |
| with: | |
| name: unit-opencode-${{ github.run_attempt }} | |
| include-hidden-files: true | |
| if-no-files-found: ignore | |
| retention-days: 7 | |
| path: packages/opencode/.artifacts/unit/junit.xml | |
| unit-desktop: | |
| needs: changes | |
| if: needs.changes.outputs.docs_only != 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: read | |
| checks: write | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| # Load-bearing: `bun install` runs `trustedDependencies` | |
| # postinstalls from the root package.json (electron, node-pty, | |
| # esbuild, tree-sitter at time of writing), which call `node` | |
| # explicitly. Do not drop without first patching those scripts. | |
| # Source of truth: grep `trustedDependencies` in package.json. (#70) | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # actions/setup-node@v6.4.0 | |
| with: | |
| node-version: "24" | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.13" | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} | |
| restore-keys: | | |
| bun-${{ runner.os }}- | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 | |
| with: | |
| path: .turbo/cache | |
| key: turbo-${{ runner.os }}-unit-desktop-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}-${{ github.sha }} | |
| restore-keys: | | |
| turbo-${{ runner.os }}-unit-desktop-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}- | |
| turbo-${{ runner.os }}-unit-desktop- | |
| - run: bun install --frozen-lockfile | |
| - name: unit | |
| run: bun turbo test:ci --filter=@opencode-ai/desktop-electron | |
| - name: Publish unit reports | |
| if: > | |
| always() && | |
| ( | |
| github.event_name != 'pull_request' || | |
| github.event.pull_request.head.repo.full_name == github.repository | |
| ) | |
| uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # mikepenz/action-junit-report@v6 | |
| with: | |
| report_paths: packages/desktop-electron/.artifacts/unit/junit.xml | |
| check_name: unit results (desktop) | |
| detailed_summary: true | |
| include_time_in_summary: true | |
| fail_on_failure: false | |
| - name: Upload unit artifacts | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # actions/upload-artifact@v7 | |
| with: | |
| name: unit-desktop-${{ github.run_attempt }} | |
| include-hidden-files: true | |
| if-no-files-found: ignore | |
| retention-days: 7 | |
| path: packages/desktop-electron/.artifacts/unit/junit.xml | |
| # Compatibility aggregator for branch protection setups that require | |
| # `ci / check`. The live dev ruleset currently requires the individual | |
| # blocking jobs directly, so every blocking job added above should still | |
| # be listed in `needs:` below. | |
| # If the blocking list grows past ~5 entries, consider switching to | |
| # `re-actors/alls-green` for dynamic aggregation (tracked in issue #54). | |
| check: | |
| if: always() | |
| needs: | |
| - changes | |
| - typecheck | |
| - frontend-architecture | |
| - unit-ui | |
| - unit-app | |
| - unit-opencode | |
| - unit-desktop | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Validate CI result | |
| env: | |
| DOCS_ONLY: ${{ needs.changes.outputs.docs_only }} | |
| TYPECHECK_RESULT: ${{ needs.typecheck.result }} | |
| FRONTEND_ARCHITECTURE_RESULT: ${{ needs['frontend-architecture'].result }} | |
| UNIT_UI_RESULT: ${{ needs['unit-ui'].result }} | |
| UNIT_APP_RESULT: ${{ needs['unit-app'].result }} | |
| UNIT_OPENCODE_RESULT: ${{ needs['unit-opencode'].result }} | |
| UNIT_DESKTOP_RESULT: ${{ needs['unit-desktop'].result }} | |
| run: | | |
| set -euo pipefail | |
| if [ "$DOCS_ONLY" = "true" ]; then | |
| echo "Docs-only change, daily CI skipped." | |
| exit 0 | |
| fi | |
| if [ "$TYPECHECK_RESULT" != "success" ] || | |
| [ "$FRONTEND_ARCHITECTURE_RESULT" != "success" ] || | |
| [ "$UNIT_UI_RESULT" != "success" ] || | |
| [ "$UNIT_APP_RESULT" != "success" ] || | |
| [ "$UNIT_OPENCODE_RESULT" != "success" ] || | |
| [ "$UNIT_DESKTOP_RESULT" != "success" ]; then | |
| echo "typecheck=$TYPECHECK_RESULT" | |
| echo "frontend-architecture=$FRONTEND_ARCHITECTURE_RESULT" | |
| echo "unit-ui=$UNIT_UI_RESULT" | |
| echo "unit-app=$UNIT_APP_RESULT" | |
| echo "unit-opencode=$UNIT_OPENCODE_RESULT" | |
| echo "unit-desktop=$UNIT_DESKTOP_RESULT" | |
| exit 1 | |
| fi |