mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
chore(ci): reduce PR workflow container fanout (#4132)
This commit is contained in:
+164
-272
@@ -101,115 +101,101 @@ jobs:
|
||||
- 'app/scripts/e2e-web-*.sh'
|
||||
- 'scripts/ci-cancel-aware.sh'
|
||||
|
||||
frontend-quality:
|
||||
name: Frontend Quality (typecheck, lint, format)
|
||||
frontend-checks:
|
||||
name: Frontend Checks (quality, i18n, docs, coverage)
|
||||
needs: [changes]
|
||||
if: needs.changes.outputs.frontend == 'true'
|
||||
if: needs.changes.outputs.frontend == 'true' || needs.changes.outputs.i18n == 'true' || needs.changes.outputs.docs == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 15
|
||||
container:
|
||||
image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: pnpm-cache
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
pnpm-store-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Save pnpm store cache
|
||||
if: always() && steps.pnpm-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: ${{ steps.pnpm-cache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Type check TypeScript files
|
||||
run: pnpm --filter openhuman-app compile
|
||||
env:
|
||||
NODE_ENV: test
|
||||
|
||||
- name: Check Prettier formatting
|
||||
run: pnpm --filter openhuman-app format:check
|
||||
env:
|
||||
NODE_ENV: test
|
||||
|
||||
- name: Run ESLint
|
||||
run: pnpm --filter openhuman-app lint
|
||||
env:
|
||||
NODE_ENV: test
|
||||
|
||||
i18n-coverage:
|
||||
name: i18n Coverage (parity)
|
||||
needs: [changes]
|
||||
if: needs.changes.outputs.i18n == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
container:
|
||||
image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: pnpm-cache
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
pnpm-store-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Save pnpm store cache
|
||||
if: always() && steps.pnpm-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: ${{ steps.pnpm-cache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Verify i18n coverage (missing / extra / drifted keys across locales)
|
||||
run: pnpm i18n:check
|
||||
|
||||
docs-drift:
|
||||
name: Docs Drift (generated architecture docs)
|
||||
needs: [changes]
|
||||
if: needs.changes.outputs.docs == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 60
|
||||
container:
|
||||
image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0
|
||||
steps:
|
||||
- name: Checkout code
|
||||
# Pinned to the v5 commit SHA for supply-chain hardening (CodeRabbit
|
||||
# review on #3892). The other lanes still use the @v5 tag; migrating
|
||||
# them is a separate repo-wide change.
|
||||
# review on #3892). The combined frontend lane keeps that hardening
|
||||
# from the former docs-drift job.
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: pnpm-cache
|
||||
if: needs.changes.outputs.frontend == 'true' || needs.changes.outputs.i18n == 'true'
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
pnpm-store-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
if: needs.changes.outputs.frontend == 'true' || needs.changes.outputs.i18n == 'true'
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Save pnpm store cache
|
||||
if: always() && (needs.changes.outputs.frontend == 'true' || needs.changes.outputs.i18n == 'true') && steps.pnpm-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: ${{ steps.pnpm-cache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Type check TypeScript files
|
||||
if: needs.changes.outputs.frontend == 'true'
|
||||
run: pnpm --filter openhuman-app compile
|
||||
env:
|
||||
NODE_ENV: test
|
||||
|
||||
- name: Check Prettier formatting
|
||||
if: needs.changes.outputs.frontend == 'true'
|
||||
run: pnpm --filter openhuman-app format:check
|
||||
env:
|
||||
NODE_ENV: test
|
||||
|
||||
- name: Run ESLint
|
||||
if: needs.changes.outputs.frontend == 'true'
|
||||
run: pnpm --filter openhuman-app lint
|
||||
env:
|
||||
NODE_ENV: test
|
||||
|
||||
- name: Verify i18n coverage (missing / extra / drifted keys across locales)
|
||||
if: needs.changes.outputs.i18n == 'true'
|
||||
run: pnpm i18n:check
|
||||
|
||||
# The generator + its tests use only Node built-ins (no workspace deps),
|
||||
# so there is no `pnpm install` step — this lane stays fast.
|
||||
# so docs-only changes still skip `pnpm install`.
|
||||
- name: Generator unit tests
|
||||
if: needs.changes.outputs.docs == 'true'
|
||||
run: pnpm docs:test
|
||||
|
||||
- name: Verify generated architecture docs are not stale
|
||||
if: needs.changes.outputs.docs == 'true'
|
||||
run: pnpm docs:check
|
||||
|
||||
- name: Run Vitest with coverage
|
||||
if: needs.changes.outputs.frontend == 'true'
|
||||
run: pnpm test:coverage
|
||||
working-directory: app
|
||||
env:
|
||||
NODE_ENV: test
|
||||
|
||||
- name: Normalize lcov source paths to repo root
|
||||
if: needs.changes.outputs.frontend == 'true'
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -f app/coverage/lcov.info
|
||||
sed -i -E 's#^SF:(src/)#SF:app/\1#' app/coverage/lcov.info
|
||||
sed -i -E 's#^SF:(\./src/)#SF:app/src/#' app/coverage/lcov.info
|
||||
|
||||
- name: Upload frontend lcov
|
||||
if: needs.changes.outputs.frontend == 'true'
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: lcov-frontend
|
||||
path: app/coverage/lcov.info
|
||||
retention-days: 7
|
||||
if-no-files-found: error
|
||||
|
||||
rust-quality:
|
||||
name: Rust Quality (fmt, clippy)
|
||||
needs: [changes]
|
||||
@@ -266,7 +252,7 @@ jobs:
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: pnpm-cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
@@ -285,7 +271,7 @@ jobs:
|
||||
|
||||
- name: Restore cached Playwright E2E artifact
|
||||
id: playwright-artifact-cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: .ci/artifacts/e2e-playwright-linux.tar.gz
|
||||
key: e2e-playwright-linux-${{ hashFiles('src/**', 'Cargo.toml', 'Cargo.lock', 'rust-toolchain.toml', 'app/src/**', 'app/public/**', 'app/index.html', 'app/vite.config.*', 'app/tailwind.config.*', 'app/postcss.config.*', 'app/package.json', 'pnpm-lock.yaml', 'app/scripts/e2e-web-build.sh') }}
|
||||
@@ -296,7 +282,7 @@ jobs:
|
||||
|
||||
- name: Save pnpm store cache
|
||||
if: always() && steps.pnpm-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: ${{ steps.pnpm-cache.outputs.cache-primary-key }}
|
||||
@@ -332,73 +318,19 @@ jobs:
|
||||
|
||||
- name: Save Playwright E2E artifact cache
|
||||
if: always() && steps.playwright-artifact-cache.outputs.cache-hit != 'true' && hashFiles('.ci/artifacts/e2e-playwright-linux.tar.gz') != ''
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: .ci/artifacts/e2e-playwright-linux.tar.gz
|
||||
key: ${{ steps.playwright-artifact-cache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Upload Playwright E2E artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: e2e-playwright-linux-${{ github.run_id }}
|
||||
path: .ci/artifacts/e2e-playwright-linux.tar.gz
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
|
||||
frontend-coverage:
|
||||
name: Frontend Coverage (Vitest)
|
||||
needs: [changes, frontend-quality]
|
||||
if: always() && needs.changes.outputs.frontend == 'true' && needs['frontend-quality'].result == 'success'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
container:
|
||||
image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: pnpm-cache
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
pnpm-store-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Save pnpm store cache
|
||||
if: always() && steps.pnpm-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: ${{ steps.pnpm-cache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Run Vitest with coverage
|
||||
run: pnpm test:coverage
|
||||
working-directory: app
|
||||
env:
|
||||
NODE_ENV: test
|
||||
|
||||
- name: Normalize lcov source paths to repo root
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -f app/coverage/lcov.info
|
||||
sed -i -E 's#^SF:(src/)#SF:app/\1#' app/coverage/lcov.info
|
||||
sed -i -E 's#^SF:(\./src/)#SF:app/src/#' app/coverage/lcov.info
|
||||
|
||||
- name: Upload frontend lcov
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: lcov-frontend
|
||||
path: app/coverage/lcov.info
|
||||
retention-days: 7
|
||||
if-no-files-found: error
|
||||
|
||||
rust-core-coverage:
|
||||
name: Rust Core Coverage (cargo-llvm-cov)
|
||||
needs: [changes, rust-quality]
|
||||
@@ -453,7 +385,7 @@ jobs:
|
||||
CARGO_BUILD_JOBS: "1"
|
||||
|
||||
- name: Upload core lcov
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: lcov-rust-core
|
||||
path: lcov-core.info
|
||||
@@ -511,7 +443,7 @@ jobs:
|
||||
|
||||
- name: Cache CEF binary distribution
|
||||
id: cef-cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: |
|
||||
~/Library/Caches/tauri-cef
|
||||
@@ -533,7 +465,7 @@ jobs:
|
||||
|
||||
- name: Save CEF binary distribution cache
|
||||
if: always() && steps.cef-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: |
|
||||
~/Library/Caches/tauri-cef
|
||||
@@ -541,7 +473,7 @@ jobs:
|
||||
key: ${{ steps.cef-cache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Upload tauri lcov
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: lcov-rust-tauri
|
||||
path: lcov-tauri.info
|
||||
@@ -579,7 +511,7 @@ jobs:
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: pnpm-cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
@@ -600,7 +532,7 @@ jobs:
|
||||
|
||||
- name: Save pnpm store cache
|
||||
if: always() && steps.pnpm-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: ${{ steps.pnpm-cache.outputs.cache-primary-key }}
|
||||
@@ -616,30 +548,23 @@ jobs:
|
||||
RUST_BACKTRACE: "1"
|
||||
|
||||
playwright-e2e:
|
||||
name: E2E (Playwright / web lane ${{ matrix.shard }}/4)
|
||||
needs: [changes, frontend-quality, rust-quality, build-playwright-e2e-artifact]
|
||||
name: E2E (Playwright / web lane)
|
||||
needs: [changes, build-playwright-e2e-artifact]
|
||||
if: |
|
||||
always() &&
|
||||
needs.changes.outputs.playwright == 'true' &&
|
||||
needs['build-playwright-e2e-artifact'].result == 'success' &&
|
||||
contains(fromJSON('["success", "skipped"]'), needs['frontend-quality'].result) &&
|
||||
contains(fromJSON('["success", "skipped"]'), needs['rust-quality'].result)
|
||||
needs['build-playwright-e2e-artifact'].result == 'success'
|
||||
runs-on: ubuntu-22.04
|
||||
container:
|
||||
image: ghcr.io/tinyhumansai/openhuman_ci:latest
|
||||
timeout-minutes: 30
|
||||
# Sharded 4 ways: each shard is a separate runner that boots its OWN backend
|
||||
# stack (mock + core + web on fixed ports) and runs playwright --shard=i/4.
|
||||
# Because each shard is an isolated machine there is no shared mock-state
|
||||
# race, so this parallelises safely despite the suite's workers:1 design.
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [1, 2, 3, 4]
|
||||
# TODO(ci-flaky, #3615): shard 1/4 consistently hits toBeVisible timeouts
|
||||
# under CI resource contention — chat-tool-error-recovery, connector-gmail-
|
||||
# composio, connector-session-guard-matrix, chat-harness-send-stream,
|
||||
# chat-management-functional and ~7 others. Shards 2-4 pass reliably.
|
||||
timeout-minutes: 90
|
||||
# Run the Playwright suite in one container to reduce PR CI fanout and stay
|
||||
# under GitHub concurrency limits. This is intentionally slower than the
|
||||
# previous shard matrix but avoids four separate runner/container slots.
|
||||
# TODO(ci-flaky, #3615): several specs consistently hit toBeVisible
|
||||
# timeouts under CI resource contention — chat-tool-error-recovery,
|
||||
# connector-gmail-composio, connector-session-guard-matrix,
|
||||
# chat-harness-send-stream, chat-management-functional and others.
|
||||
# Playwright config bumped to 90s test / 15s expect timeout + 2 retries.
|
||||
# Run the lane but don't block merges until infra stabilises.
|
||||
continue-on-error: true
|
||||
@@ -653,7 +578,7 @@ jobs:
|
||||
|
||||
- name: Cache pnpm store
|
||||
id: pnpm-cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
@@ -665,13 +590,13 @@ jobs:
|
||||
|
||||
- name: Save pnpm store cache
|
||||
if: always() && steps.pnpm-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: .pnpm-store
|
||||
key: ${{ steps.pnpm-cache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Download Playwright E2E artifact
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||
with:
|
||||
name: e2e-playwright-linux-${{ github.run_id }}
|
||||
path: .
|
||||
@@ -689,81 +614,99 @@ jobs:
|
||||
- name: Install Playwright Chromium headless shell
|
||||
run: pnpm --filter openhuman-app exec playwright install chromium-headless-shell
|
||||
|
||||
- name: Run Playwright web E2E suite (shard ${{ matrix.shard }}/4)
|
||||
- name: Run Playwright web E2E suite
|
||||
env:
|
||||
OPENHUMAN_WORKSPACE: ${{ runner.temp }}/openhuman-playwright-workspace
|
||||
run: |
|
||||
mkdir -p "$OPENHUMAN_WORKSPACE"
|
||||
bash app/scripts/e2e-web-session.sh \
|
||||
--shard=${{ matrix.shard }}/4
|
||||
bash app/scripts/e2e-web-session.sh
|
||||
|
||||
- name: Upload Playwright E2E failure artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: e2e-playwright-failure-logs-${{ github.run_id }}-shard${{ matrix.shard }}
|
||||
name: e2e-playwright-failure-logs-${{ github.run_id }}
|
||||
path: |
|
||||
${{ runner.temp }}/openhuman-playwright-workspace/**
|
||||
retention-days: 7
|
||||
if-no-files-found: ignore
|
||||
|
||||
playwright-e2e-gate:
|
||||
name: Playwright E2E Gate
|
||||
# Fixed-name aggregator over the 4 dynamic shard checks. Branch protection
|
||||
# can require this single job instead of the per-shard matrix names (which
|
||||
# change if the shard count changes). if: always() so it runs even when a
|
||||
# shard is failed/skipped and can report the real aggregate result.
|
||||
needs: [playwright-e2e]
|
||||
pr-ci-gate:
|
||||
name: PR CI Gate
|
||||
needs:
|
||||
- changes
|
||||
- frontend-checks
|
||||
- rust-quality
|
||||
- build-playwright-e2e-artifact
|
||||
- rust-core-coverage
|
||||
- rust-tauri-coverage
|
||||
- rust-e2e
|
||||
- playwright-e2e
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Require all Playwright shards to pass
|
||||
- name: Require PR CI jobs to pass
|
||||
run: |
|
||||
result="${{ needs['playwright-e2e'].result }}"
|
||||
echo "Playwright web lane aggregate result: ${result}"
|
||||
# NOTE: the shards currently carry continue-on-error: true, so this
|
||||
# aggregate is 'success' even when a shard hit a known flake — the
|
||||
# gate passes today. Once continue-on-error is removed from the shards
|
||||
# (after the flakes are fixed) this same check enforces a real pass.
|
||||
if [ "${result}" = "success" ] || [ "${result}" = "skipped" ]; then
|
||||
echo "Playwright shards passed or were intentionally skipped."
|
||||
exit 0
|
||||
set -euo pipefail
|
||||
declare -A results=(
|
||||
["Detect Changed Areas"]="${{ needs.changes.result }}"
|
||||
["Frontend Checks"]="${{ needs['frontend-checks'].result }}"
|
||||
["Rust Quality"]="${{ needs['rust-quality'].result }}"
|
||||
["Build Playwright E2E Artifact"]="${{ needs['build-playwright-e2e-artifact'].result }}"
|
||||
["Rust Core Coverage"]="${{ needs['rust-core-coverage'].result }}"
|
||||
["Rust Tauri Coverage"]="${{ needs['rust-tauri-coverage'].result }}"
|
||||
["Rust E2E"]="${{ needs['rust-e2e'].result }}"
|
||||
["Playwright E2E"]="${{ needs['playwright-e2e'].result }}"
|
||||
)
|
||||
|
||||
failed=0
|
||||
for name in "${!results[@]}"; do
|
||||
result="${results[$name]}"
|
||||
echo "${name}: ${result}"
|
||||
if [ "${result}" != "success" ] && [ "${result}" != "skipped" ]; then
|
||||
echo "::error::${name} did not pass (result=${result})."
|
||||
failed=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${failed}" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "::error::Playwright web lane did not succeed (result=${result})."
|
||||
exit 1
|
||||
|
||||
coverage-gate:
|
||||
name: Coverage Gate (diff-cover >= 80%)
|
||||
needs: [changes, frontend-coverage, rust-core-coverage, rust-tauri-coverage]
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Verify coverage lanes completed
|
||||
run: |
|
||||
set -euo pipefail
|
||||
coverage="${{ needs.changes.outputs.coverage }}"
|
||||
frontend="${{ needs['frontend-coverage'].result }}"
|
||||
frontend_needed="${{ needs.changes.outputs.frontend }}"
|
||||
core_needed="${{ needs.changes.outputs['rust-core'] }}"
|
||||
tauri_needed="${{ needs.changes.outputs['rust-tauri'] }}"
|
||||
frontend="${{ needs['frontend-checks'].result }}"
|
||||
core="${{ needs['rust-core-coverage'].result }}"
|
||||
tauri="${{ needs['rust-tauri-coverage'].result }}"
|
||||
|
||||
echo "Coverage needed: ${coverage}"
|
||||
echo "Frontend coverage result: ${frontend}"
|
||||
echo "Rust core coverage result: ${core}"
|
||||
echo "Rust Tauri coverage result: ${tauri}"
|
||||
echo "Frontend changed: ${frontend_needed} (job=${frontend})"
|
||||
echo "Rust core changed: ${core_needed} (job=${core})"
|
||||
echo "Rust Tauri changed: ${tauri_needed} (job=${tauri})"
|
||||
|
||||
if [ "${coverage}" != "true" ]; then
|
||||
echo "No coverage-relevant changes detected; skipping diff-cover."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for result in "${frontend}" "${core}" "${tauri}"; do
|
||||
if [ "${result}" != "success" ] && [ "${result}" != "skipped" ]; then
|
||||
echo "::error::A required coverage lane did not succeed or skip cleanly (result=${result})."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
if [ "${frontend_needed}" = "true" ] && [ "${frontend}" != "success" ]; then
|
||||
echo "::error::Frontend coverage was required but Frontend Checks did not succeed (result=${frontend})."
|
||||
exit 1
|
||||
fi
|
||||
if [ "${core_needed}" = "true" ] && [ "${core}" != "success" ]; then
|
||||
echo "::error::Rust core coverage was required but the lane did not succeed (result=${core})."
|
||||
exit 1
|
||||
fi
|
||||
if [ "${tauri_needed}" = "true" ] && [ "${tauri}" != "success" ]; then
|
||||
echo "::error::Rust Tauri coverage was required but the lane did not succeed (result=${tauri})."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Checkout code
|
||||
if: needs.changes.outputs.coverage == 'true'
|
||||
@@ -797,7 +740,7 @@ jobs:
|
||||
|
||||
- name: Download all lcov artifacts
|
||||
if: needs.changes.outputs.coverage == 'true'
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||
with:
|
||||
path: lcov-artifacts
|
||||
pattern: lcov-*
|
||||
@@ -826,7 +769,7 @@ jobs:
|
||||
|
||||
- name: Upload diff-cover report
|
||||
if: always() && needs.changes.outputs.coverage == 'true'
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: diff-coverage-report
|
||||
path: |
|
||||
@@ -834,54 +777,3 @@ jobs:
|
||||
diff-coverage.md
|
||||
retention-days: 14
|
||||
if-no-files-found: warn
|
||||
|
||||
pr-ci-gate:
|
||||
name: PR CI Gate
|
||||
needs:
|
||||
- changes
|
||||
- frontend-quality
|
||||
- i18n-coverage
|
||||
- docs-drift
|
||||
- rust-quality
|
||||
- build-playwright-e2e-artifact
|
||||
- frontend-coverage
|
||||
- rust-core-coverage
|
||||
- rust-tauri-coverage
|
||||
- rust-e2e
|
||||
- playwright-e2e-gate
|
||||
- coverage-gate
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Require PR CI jobs to pass
|
||||
run: |
|
||||
set -euo pipefail
|
||||
declare -A results=(
|
||||
["Detect Changed Areas"]="${{ needs.changes.result }}"
|
||||
["Frontend Quality"]="${{ needs['frontend-quality'].result }}"
|
||||
["i18n Coverage"]="${{ needs['i18n-coverage'].result }}"
|
||||
["Docs Drift"]="${{ needs['docs-drift'].result }}"
|
||||
["Rust Quality"]="${{ needs['rust-quality'].result }}"
|
||||
["Build Playwright E2E Artifact"]="${{ needs['build-playwright-e2e-artifact'].result }}"
|
||||
["Frontend Coverage"]="${{ needs['frontend-coverage'].result }}"
|
||||
["Rust Core Coverage"]="${{ needs['rust-core-coverage'].result }}"
|
||||
["Rust Tauri Coverage"]="${{ needs['rust-tauri-coverage'].result }}"
|
||||
["Rust E2E"]="${{ needs['rust-e2e'].result }}"
|
||||
["Playwright E2E Gate"]="${{ needs['playwright-e2e-gate'].result }}"
|
||||
["Coverage Gate"]="${{ needs['coverage-gate'].result }}"
|
||||
)
|
||||
|
||||
failed=0
|
||||
for name in "${!results[@]}"; do
|
||||
result="${results[$name]}"
|
||||
echo "${name}: ${result}"
|
||||
if [ "${result}" != "success" ] && [ "${result}" != "skipped" ]; then
|
||||
echo "::error::${name} did not pass (result=${result})."
|
||||
failed=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${failed}" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user