Skip to content

feat(ui): group tool calls into trow summaries #2511

feat(ui): group tool calls into trow summaries

feat(ui): group tool calls into trow summaries #2511

Workflow file for this run

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