mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-15 12:53:31 +08:00
Route required checks by changed surface, add offline provider and chat contracts, and keep fork PRs independent of live credentials. Layer agent guidance by subtree and enforce a compact instruction budget. Tested: bun run check:policy Confidence: high Scope-risk: broad
332 lines
11 KiB
YAML
332 lines
11 KiB
YAML
name: PR Quality
|
|
|
|
on:
|
|
pull_request:
|
|
types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]
|
|
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
|
|
concurrency:
|
|
group: pr-quality-${{ github.event.pull_request.number }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
scope-plan:
|
|
name: scope-plan
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
areas: ${{ steps.policy.outputs.areas }}
|
|
area_labels: ${{ steps.policy.outputs.area_labels }}
|
|
blocked: ${{ steps.policy.outputs.blocked }}
|
|
blocking_reasons: ${{ steps.policy.outputs.blocking_reasons }}
|
|
desktop_checks: ${{ steps.policy.outputs.desktop_checks }}
|
|
server_checks: ${{ steps.policy.outputs.server_checks }}
|
|
adapter_checks: ${{ steps.policy.outputs.adapter_checks }}
|
|
desktop_native_checks: ${{ steps.policy.outputs.desktop_native_checks }}
|
|
provider_contract_checks: ${{ steps.policy.outputs.provider_contract_checks }}
|
|
chat_contract_checks: ${{ steps.policy.outputs.chat_contract_checks }}
|
|
persistence_checks: ${{ steps.policy.outputs.persistence_checks }}
|
|
policy_checks: ${{ steps.policy.outputs.policy_checks }}
|
|
docs_checks: ${{ steps.policy.outputs.docs_checks }}
|
|
coverage_checks: ${{ steps.policy.outputs.coverage_checks }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Setup Bun
|
|
uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: 1.3.12
|
|
|
|
- name: Collect changed files
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
REPOSITORY: ${{ github.repository }}
|
|
run: |
|
|
gh api "repos/${REPOSITORY}/pulls/${PR_NUMBER}/files" --paginate --jq '.[].filename' > changed-files.txt
|
|
jq -r '.pull_request.labels[].name' "$GITHUB_EVENT_PATH" > labels.txt
|
|
|
|
- name: Build deterministic scope plan
|
|
id: policy
|
|
run: bun run scripts/pr/change-policy.ts --files changed-files.txt --labels-file labels.txt --plan-only
|
|
|
|
policy-enforcement:
|
|
name: policy-enforcement
|
|
needs: scope-plan
|
|
if: ${{ always() && needs.scope-plan.result == 'success' }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout policy sources
|
|
if: needs.scope-plan.outputs.policy_checks == 'true'
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Bun
|
|
if: needs.scope-plan.outputs.policy_checks == 'true'
|
|
uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: 1.3.12
|
|
|
|
- name: Install root dependencies
|
|
if: needs.scope-plan.outputs.policy_checks == 'true'
|
|
run: bun install --frozen-lockfile
|
|
|
|
- name: Run policy regression tests
|
|
if: needs.scope-plan.outputs.policy_checks == 'true'
|
|
run: bun run check:policy
|
|
|
|
- name: Enforce change policy
|
|
env:
|
|
BLOCKED: ${{ needs.scope-plan.outputs.blocked }}
|
|
BLOCKING_REASONS: ${{ needs.scope-plan.outputs.blocking_reasons }}
|
|
run: |
|
|
if [ "$BLOCKED" = "true" ]; then
|
|
echo "::error::${BLOCKING_REASONS:-Change policy blocked this pull request}"
|
|
exit 1
|
|
fi
|
|
echo "Change policy passed"
|
|
|
|
desktop-checks:
|
|
name: desktop-checks
|
|
needs: scope-plan
|
|
if: needs.scope-plan.outputs.desktop_checks == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: 1.3.12
|
|
- name: Install root dependencies
|
|
run: bun install --frozen-lockfile
|
|
- name: Install desktop dependencies
|
|
working-directory: desktop
|
|
run: bun install --frozen-lockfile
|
|
- name: Run desktop checks
|
|
run: bun run check:desktop
|
|
|
|
server-checks:
|
|
name: server-checks
|
|
needs: scope-plan
|
|
if: needs.scope-plan.outputs.server_checks == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: 1.3.12
|
|
- name: Install root dependencies
|
|
run: bun install --frozen-lockfile
|
|
- name: Run root runtime checks
|
|
run: bun run check:server
|
|
|
|
provider-contract-checks:
|
|
name: provider-contract-checks
|
|
needs: scope-plan
|
|
if: needs.scope-plan.outputs.provider_contract_checks == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: 1.3.12
|
|
- name: Install root dependencies
|
|
run: bun install --frozen-lockfile
|
|
- name: Run offline provider contracts
|
|
run: bun run check:provider-contract
|
|
|
|
chat-contract-checks:
|
|
name: chat-contract-checks
|
|
needs: scope-plan
|
|
if: needs.scope-plan.outputs.chat_contract_checks == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: 1.3.12
|
|
- name: Install root dependencies
|
|
run: bun install --frozen-lockfile
|
|
- name: Install desktop dependencies
|
|
working-directory: desktop
|
|
run: bun install --frozen-lockfile
|
|
- name: Run desktop-server chat contracts
|
|
run: bun run check:chat-contract
|
|
|
|
adapter-checks:
|
|
name: adapter-checks
|
|
needs: scope-plan
|
|
if: needs.scope-plan.outputs.adapter_checks == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: 1.3.12
|
|
- name: Install adapter dependencies
|
|
working-directory: adapters
|
|
run: bun install --frozen-lockfile
|
|
- name: Run adapter checks
|
|
run: bun run check:adapters
|
|
|
|
desktop-native-checks:
|
|
name: desktop-native-checks
|
|
needs: scope-plan
|
|
if: needs.scope-plan.outputs.desktop_native_checks == 'true'
|
|
runs-on: ubuntu-22.04
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Install Linux dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y \
|
|
build-essential curl wget file \
|
|
libxdo-dev libssl-dev patchelf \
|
|
libfuse2
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: 1.3.12
|
|
- name: Install root dependencies
|
|
run: bun install --frozen-lockfile
|
|
- name: Install desktop dependencies
|
|
working-directory: desktop
|
|
run: bun install --frozen-lockfile
|
|
- name: Run desktop native checks
|
|
run: bun run check:native
|
|
|
|
persistence-checks:
|
|
name: persistence-checks
|
|
needs: scope-plan
|
|
if: needs.scope-plan.outputs.persistence_checks == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: 1.3.12
|
|
- name: Install root dependencies
|
|
run: bun install --frozen-lockfile
|
|
- name: Install desktop dependencies
|
|
working-directory: desktop
|
|
run: bun install --frozen-lockfile
|
|
- name: Run persistence upgrade contracts
|
|
run: bun run check:persistence-upgrade
|
|
|
|
docs-checks:
|
|
name: docs-checks
|
|
needs: scope-plan
|
|
if: needs.scope-plan.outputs.docs_checks == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: npm
|
|
- name: Run docs checks
|
|
run: npm ci && npm run docs:build
|
|
|
|
coverage-checks:
|
|
name: coverage-checks
|
|
needs: scope-plan
|
|
if: needs.scope-plan.outputs.coverage_checks == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: 1.3.12
|
|
- name: Install root dependencies
|
|
run: bun install --frozen-lockfile
|
|
- name: Install desktop dependencies
|
|
working-directory: desktop
|
|
run: bun install --frozen-lockfile
|
|
- name: Install adapter dependencies
|
|
working-directory: adapters
|
|
run: bun install --frozen-lockfile
|
|
- name: Run coverage checks
|
|
env:
|
|
COVERAGE_BASE_REF: origin/${{ github.base_ref }}
|
|
run: bun run check:coverage
|
|
- name: Summarize coverage report
|
|
if: always()
|
|
run: |
|
|
latest_report="$(find artifacts/coverage -name coverage-report.md -print | sort | tail -n 1)"
|
|
if [ -n "$latest_report" ]; then
|
|
cat "$latest_report" >> "$GITHUB_STEP_SUMMARY"
|
|
fi
|
|
- name: Upload coverage report
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: coverage-report
|
|
path: artifacts/coverage/
|
|
retention-days: 14
|
|
|
|
pr-quality-gate:
|
|
name: pr-quality-gate
|
|
needs:
|
|
- scope-plan
|
|
- policy-enforcement
|
|
- desktop-checks
|
|
- server-checks
|
|
- provider-contract-checks
|
|
- chat-contract-checks
|
|
- adapter-checks
|
|
- desktop-native-checks
|
|
- persistence-checks
|
|
- docs-checks
|
|
- coverage-checks
|
|
if: always()
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Require every selected quality job to pass
|
|
run: |
|
|
failures=0
|
|
|
|
require_success() {
|
|
local name="$1"
|
|
local result="$2"
|
|
if [ "$result" != "success" ]; then
|
|
echo "::error::$name ended with result: $result"
|
|
failures=$((failures + 1))
|
|
else
|
|
echo "$name: success"
|
|
fi
|
|
}
|
|
|
|
require_selected() {
|
|
local name="$1"
|
|
local expected="$2"
|
|
local result="$3"
|
|
if [ "$expected" = "true" ]; then
|
|
require_success "$name" "$result"
|
|
elif [ "$result" != "skipped" ]; then
|
|
echo "::error::$name was not selected but ended with result: $result"
|
|
failures=$((failures + 1))
|
|
else
|
|
echo "$name: skipped by scope plan"
|
|
fi
|
|
}
|
|
|
|
require_success "scope-plan" "${{ needs.scope-plan.result }}"
|
|
require_success "policy-enforcement" "${{ needs.policy-enforcement.result }}"
|
|
require_selected "desktop-checks" "${{ needs.scope-plan.outputs.desktop_checks }}" "${{ needs.desktop-checks.result }}"
|
|
require_selected "server-checks" "${{ needs.scope-plan.outputs.server_checks }}" "${{ needs.server-checks.result }}"
|
|
require_selected "provider-contract-checks" "${{ needs.scope-plan.outputs.provider_contract_checks }}" "${{ needs.provider-contract-checks.result }}"
|
|
require_selected "chat-contract-checks" "${{ needs.scope-plan.outputs.chat_contract_checks }}" "${{ needs.chat-contract-checks.result }}"
|
|
require_selected "adapter-checks" "${{ needs.scope-plan.outputs.adapter_checks }}" "${{ needs.adapter-checks.result }}"
|
|
require_selected "desktop-native-checks" "${{ needs.scope-plan.outputs.desktop_native_checks }}" "${{ needs.desktop-native-checks.result }}"
|
|
require_selected "persistence-checks" "${{ needs.scope-plan.outputs.persistence_checks }}" "${{ needs.persistence-checks.result }}"
|
|
require_selected "docs-checks" "${{ needs.scope-plan.outputs.docs_checks }}" "${{ needs.docs-checks.result }}"
|
|
require_selected "coverage-checks" "${{ needs.scope-plan.outputs.coverage_checks }}" "${{ needs.coverage-checks.result }}"
|
|
|
|
if [ "$failures" -gt 0 ]; then
|
|
exit 1
|
|
fi
|