openpencil/.github/workflows/ci.yml
Danila Poyarkov af18eec514
fix(ai): polish chat history and stabilize chat tests (#923)
* fix(ai): confirm deleting a conversation in the standard dialog

Deleting a conversation asked inside the chat panel's header, unlike every other destructive confirmation, which uses AppConfirmationDialog. It now opens that dialog, which names the conversation; Cancel keeps it and Delete removes it.

* feat(ai): dim a reverted reply's content

A reverted reply still showed its tool cards with their success marks and its text as if the edits were there; only the line below said otherwise. Its content is now dimmed, while the Changes reverted or Restore changes line and the other actions stay at full strength.

* ci: typecheck the test suites in Code quality

bun run check runs check:test-types, but PR CI did not, so tests that no longer type-checked could merge and break bun run check on master, as the grid fixtures from #866 did. It runs as a step of Code quality, which already builds the package declarations it needs; the check takes about two seconds, less than a separate job's setup.

* test(ai): time the reasoning auto-close with the fake clock

The test slept 1200 ms of real time past the 1000 ms auto-close to show that a manually opened block stays open, which was timing-dependent under load. It now advances Playwright's clock past the timer. With manual opening ignored, it fails.

* test(ai): skip file identity on Playwright's bundled Chromium on macOS

There, reading a FileSystemFileHandle back from IndexedDB closes the page before any app code runs, so the test always failed locally on a Mac. Installed Google Chrome on the same machine reads and compares the handle correctly, and the app's resolveFile returns one ID for twelve concurrent calls there. The skip is limited to the bundled build on macOS.

* fix(settings): keep a model picker open while the editor slides in

The models panel focused the editor's first field after its enter transition, even when focus had already moved into the editor, closing a picker opened during the animation. It now leaves focus alone once it has moved since the editor opened.

* test: serve a fixed model catalog and wait for the editor before Settings shortcuts

Chat tests fetched the live models.dev catalog, so the model list changed under them; they now get a small fixed one. The credentials test pressed the Settings shortcut before the editor was ready.

* fix(settings): move drill-in focus with Reka's FocusScope

The drill-in focused the detail's first field after its slide-in ended, found by querying the DOM, and restored focus by hand, so a picker opened during the slide closed. FocusScope now focuses the detail as it mounts and returns focus to what opened it. A caller can prevent open-auto-focus to focus its own field, which the models panel does for the profile name through the editor's exposed focus().

* test(ai): answer the Vision model's request in chat tests

A message with images is analyzed by the Vision model, which calls OpenRouter directly rather than the mocked chat transport. With the test key the request failed, and since #916 a failure before dispatch takes the message back, so the WebKit history test never got a reply. The chat fixture now answers that request with fixed findings.

* test(ai): check that deletion persists and reverted replies dim

The delete test passed even if the conversation stayed in storage, since the panel clears its messages before the store removes it; it now reloads and checks the history. The revert test checked only data-reverted; it now checks the rendered opacity too.
2026-10-07 09:53:05 +00:00

272 lines
8.3 KiB
YAML

name: CI
on:
# Every base, so stacked pull requests are checked against the PR below them.
pull_request:
# The merge queue checks each queued change on top of the ones ahead of it.
merge_group:
permissions:
contents: read
packages: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
changes:
name: Classify changes
runs-on: ubuntu-latest
timeout-minutes: 3
outputs:
scope: ${{ steps.classify.outputs.scope }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
- name: Fetch comparison base
env:
CI_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
run: git fetch --no-tags --depth=1 origin "$CI_BASE_SHA"
- name: Select validation scope
id: classify
env:
CI_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
run: bun tools/ci/policy/src/classify.ts
commit-messages:
name: Commit messages
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
persist-credentials: false
- uses: ./.github/actions/setup-bun
- name: Validate PR commit messages
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }}
run: |
if ! bun run check:commits --from "$BASE_SHA" --to "$HEAD_SHA" --verbose; then
echo '::error title=Commit messages::Check the messages listed above. Use type(scope): description, for example fix: preserve selection. See CONTRIBUTING.md#commit-messages.'
exit 1
fi
documentation:
name: Documentation
needs: changes
if: needs.changes.outputs.scope == 'docs'
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: ./.github/actions/setup-bun
- name: Build documentation type dependencies
run: bun run build:packages
- name: Validate documentation and generated references
run: bun run check:docs
- name: Build documentation and check examples
run: bun run docs:build
source-quality:
name: Code quality
needs: changes
if: needs.changes.outputs.scope == 'code'
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: ./.github/actions/setup-bun
- name: Generate package declarations
run: bun run build:packages
- name: Verify formatting
run: bun run format:check
- name: Lint TypeScript and Vue
run: bun run lint
- name: Typecheck application and SDKs
run: bun run typecheck
- name: Typecheck test suites
run: bun run check:test-types
- name: Enforce architecture and type-shape boundaries
run: bun run check:arch && bun run check:test-homes && bun run test:type-shapes
package-quality:
name: Package integrity
needs: changes
if: needs.changes.outputs.scope == 'code'
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: ./.github/actions/setup-bun
- name: Cache npm consumer downloads
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: ~/.npm
key: npm-consumers-${{ runner.os }}-${{ hashFiles('bun.lock') }}
restore-keys: npm-consumers-${{ runner.os }}-
- name: Build publishable packages
run: bun run build:packages
- name: Validate installed package artifacts with Node and Bun
run: bun run test:packages
- name: Detect unused dependencies and files
run: bun run check:deps
- name: Validate workspace dependency policy
run: bun run check:monorepo
repository-quality:
name: Repository hygiene
needs: changes
if: needs.changes.outputs.scope == 'code'
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: ./.github/actions/setup-bun
- name: Validate generated brand assets
run: bun run check:icons
- name: Validate translations
run: bun run check:i18n
- name: Validate documentation links and structure
run: bun run check:docs
- name: Audit critical dependency vulnerabilities
run: bun run check:audit
- name: Scan for committed secrets
run: bun run check:secrets
- name: Test repository tooling
run: bun run test:tools
- name: Detect duplicated product code
run: bun run test:dupes
storybook:
name: Component workshop
needs: changes
if: needs.changes.outputs.scope == 'code'
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: ./.github/actions/setup-bun
- name: Build component dependencies
run: bun run build:packages
- name: Build static Storybook
run: bun run build-storybook
native-test-contracts:
name: Native app contracts
needs: changes
if: needs.changes.outputs.scope == 'code'
timeout-minutes: 8
runs-on: ubuntu-24.04
container:
image: ghcr.io/open-pencil/native-contracts-ci@sha256:64e6b1b50a988c1cefe2b69ac9d58076fd743b18391fa2dbd1d153eba2be9521
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: desktop -> target
key: native-test
- uses: ./.github/actions/setup-bun
- name: Typecheck native interaction tests
run: bun run check:native-test
- name: Generate native brand assets
run: bun run generate:icons --target desktop
- name: Compile native-test Tauri feature
run: cargo check --manifest-path desktop/Cargo.toml --features native-test
unit-tests:
needs: changes
if: needs.changes.outputs.scope == 'code'
timeout-minutes: 10
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
group: [app, cli, core, dom, fig, mcp, render, scene-graph, vue]
name: Unit tests — ${{ matrix.group }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: ./.github/actions/setup-bun
with:
lfs: 'true'
# Quick shards read only the small fixtures; heavy-tests.yml pulls the large .fig corpora.
lfs-include: tests/fixtures/gold-preview.fig,tests/fixtures/circle-text.fig,tests/fixtures/slots.fig,tests/fixtures/variable-override-precedence.fig,tests/fixtures/fonts/*
- name: Build shared Core test dependency
run: bun --filter @open-pencil/core build
- name: Run ${{ matrix.group }} unit tests
run: bun tools/dev/unit-tests/src/run.ts "${{ matrix.group }}"
result:
name: CI result
needs: [changes, commit-messages, documentation, source-quality, package-quality, repository-quality, storybook, native-test-contracts, unit-tests]
if: always()
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
- name: Require successful checks for the selected scope
env:
CI_NEEDS: ${{ toJSON(needs) }}
run: bun tools/ci/policy/src/gate.ts