

























@@ -0,0 +1,274 @@
1+name: Test Performance Agent
2+3+on:
4+workflow_run: # zizmor: ignore[dangerous-triggers] main-only test optimization after trusted CI; job gates repository, event, branch, actor, conclusion, current main SHA, and daily cadence before using write token
5+workflows:
6+ - CI
7+types:
8+ - completed
9+workflow_dispatch:
10+11+permissions:
12+actions: read
13+contents: write
14+15+concurrency:
16+group: test-performance-agent-main
17+cancel-in-progress: false
18+19+env:
20+FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
21+TEST_PERF_BEFORE: .artifacts/test-perf/baseline-before.json
22+TEST_PERF_AFTER: .artifacts/test-perf/after-agent.json
23+TEST_PERF_COMPARE: .artifacts/test-perf/agent-compare.json
24+25+jobs:
26+optimize-tests:
27+if: >
28+ github.repository == 'openclaw/openclaw' &&
29+ (github.event_name == 'workflow_dispatch' ||
30+ (github.event.workflow_run.conclusion == 'success' &&
31+ github.event.workflow_run.event == 'push' &&
32+ github.event.workflow_run.head_branch == 'main' &&
33+ !endsWith(github.event.workflow_run.actor.login, '[bot]')))
34+ runs-on: blacksmith-32vcpu-ubuntu-2404
35+timeout-minutes: 240
36+steps:
37+ - name: Checkout
38+uses: actions/checkout@v6
39+with:
40+ref: main
41+fetch-depth: 0
42+persist-credentials: false
43+submodules: false
44+45+ - name: Gate trusted main activity and daily cadence
46+id: gate
47+env:
48+EVENT_NAME: ${{ github.event_name }}
49+GH_TOKEN: ${{ github.token }}
50+WORKFLOW_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
51+run: |
52+ set -euo pipefail
53+54+ if [ "$EVENT_NAME" != "workflow_run" ]; then
55+ echo "run_agent=true" >> "$GITHUB_OUTPUT"
56+ echo "base_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
57+ exit 0
58+ fi
59+60+ for attempt in 1 2 3 4 5; do
61+ if git fetch --no-tags origin main; then
62+ break
63+ fi
64+ if [ "$attempt" = "5" ]; then
65+ echo "Failed to fetch main after retries." >&2
66+ exit 1
67+ fi
68+ echo "Fetch attempt ${attempt} failed; retrying."
69+ sleep $((attempt * 2))
70+ done
71+72+ remote_main="$(git rev-parse origin/main)"
73+ if [ "$remote_main" != "$WORKFLOW_HEAD_SHA" ]; then
74+ echo "CI run is superseded by ${remote_main}; skipping test performance agent for ${WORKFLOW_HEAD_SHA}."
75+ echo "run_agent=false" >> "$GITHUB_OUTPUT"
76+ exit 0
77+ fi
78+79+ day_start="$(date -u +%Y-%m-%dT00:00:00Z)"
80+ runs_json="$RUNNER_TEMP/test-performance-agent-runs.json"
81+ gh api "repos/${GITHUB_REPOSITORY}/actions/workflows/test-performance-agent.yml/runs" \
82+ -f branch=main \
83+ -f event=workflow_run \
84+ -f per_page=50 > "$runs_json"
85+86+ prior_runs="$(
87+ jq -r \
88+ --argjson current_run_id "$GITHUB_RUN_ID" \
89+ --arg day_start "$day_start" \
90+ '.workflow_runs[]
91+ | select(.database_id != $current_run_id)
92+ | select(.created_at >= $day_start)
93+ | select(.status != "cancelled")
94+ | select((.conclusion // "") != "skipped")
95+ | [.database_id, .status, (.conclusion // ""), .created_at, .head_sha]
96+ | @tsv' "$runs_json"
97+ )"
98+99+ if [ -n "$prior_runs" ]; then
100+ echo "Test performance agent already ran or is running today; skipping."
101+ printf '%s\n' "$prior_runs"
102+ echo "run_agent=false" >> "$GITHUB_OUTPUT"
103+ exit 0
104+ fi
105+106+ echo "run_agent=true" >> "$GITHUB_OUTPUT"
107+ echo "base_sha=${remote_main}" >> "$GITHUB_OUTPUT"
108+109+ - name: Setup Node environment
110+if: steps.gate.outputs.run_agent == 'true'
111+uses: ./.github/actions/setup-node-env
112+with:
113+install-bun: "false"
114+115+ - name: Ensure test performance agent key exists
116+if: steps.gate.outputs.run_agent == 'true'
117+env:
118+OPENAI_API_KEY: ${{ secrets.OPENCLAW_TEST_PERF_AGENT_OPENAI_API_KEY || secrets.OPENAI_API_KEY }}
119+run: |
120+ set -euo pipefail
121+ if [ -z "${OPENAI_API_KEY:-}" ]; then
122+ echo "Missing OPENCLAW_TEST_PERF_AGENT_OPENAI_API_KEY or OPENAI_API_KEY secret." >&2
123+ exit 1
124+ fi
125+126+ - name: Build baseline full-suite performance report
127+if: steps.gate.outputs.run_agent == 'true'
128+run: pnpm test:perf:groups --full-suite --allow-failures --output "$TEST_PERF_BEFORE" --limit 20 --top-files 40
129+130+ - name: Run Codex test performance agent
131+if: steps.gate.outputs.run_agent == 'true'
132+uses: openai/codex-action@v1
133+with:
134+openai-api-key: ${{ secrets.OPENCLAW_TEST_PERF_AGENT_OPENAI_API_KEY || secrets.OPENAI_API_KEY }}
135+prompt-file: .github/codex/prompts/test-performance-agent.md
136+model: gpt-5.4
137+effort: high
138+sandbox: workspace-write
139+safety-strategy: drop-sudo
140+codex-args: '["--full-auto"]'
141+142+ - name: Enforce focused test performance patch
143+if: steps.gate.outputs.run_agent == 'true'
144+id: patch
145+run: |
146+ set -euo pipefail
147+148+ untracked="$(git ls-files --others --exclude-standard)"
149+ if [ -n "$untracked" ]; then
150+ echo "Test performance agent created untracked files; forbidden:"
151+ printf '%s\n' "$untracked"
152+ exit 1
153+ fi
154+155+ added_deleted_or_renamed="$(git diff --name-status --diff-filter=ADR)"
156+ if [ -n "$added_deleted_or_renamed" ]; then
157+ echo "Test performance agent added, deleted, or renamed tracked files; forbidden:"
158+ printf '%s\n' "$added_deleted_or_renamed"
159+ exit 1
160+ fi
161+162+ bad_paths="$(
163+ git diff --name-only | while IFS= read -r path; do
164+ case "$path" in
165+ apps/*|extensions/*|packages/*|scripts/*|src/*|Swabble/*|test/*|ui/*) ;;
166+ *) printf '%s\n' "$path" ;;
167+ esac
168+ done
169+ )"
170+ if [ -n "$bad_paths" ]; then
171+ echo "Test performance agent touched forbidden paths:"
172+ printf '%s\n' "$bad_paths"
173+ exit 1
174+ fi
175+176+ if git diff --quiet; then
177+ echo "has_changes=false" >> "$GITHUB_OUTPUT"
178+ else
179+ echo "has_changes=true" >> "$GITHUB_OUTPUT"
180+ fi
181+182+ - name: Restore Node 24 path
183+if: steps.gate.outputs.run_agent == 'true' && steps.patch.outputs.has_changes == 'true'
184+run: |
185+ set -euo pipefail
186+ export PATH="${NODE_BIN}:${PATH}"
187+ echo "${NODE_BIN}" >> "$GITHUB_PATH"
188+ node -v
189+ corepack enable
190+ pnpm -v
191+192+ - name: Run full-suite performance report after agent changes
193+if: steps.gate.outputs.run_agent == 'true' && steps.patch.outputs.has_changes == 'true'
194+run: pnpm test:perf:groups --full-suite --output "$TEST_PERF_AFTER" --limit 20 --top-files 40
195+196+ - name: Compare test performance reports
197+if: steps.gate.outputs.run_agent == 'true' && steps.patch.outputs.has_changes == 'true'
198+run: pnpm test:perf:groups:compare "$TEST_PERF_BEFORE" "$TEST_PERF_AFTER" --output "$TEST_PERF_COMPARE" --limit 20 --top-files 40
199+200+ - name: Enforce coverage-preserving test count
201+if: steps.gate.outputs.run_agent == 'true' && steps.patch.outputs.has_changes == 'true'
202+run: |
203+ set -euo pipefail
204+ node <<'NODE'
205+ const fs = require("node:fs");
206+ const before = JSON.parse(fs.readFileSync(process.env.TEST_PERF_BEFORE, "utf8"));
207+ const after = JSON.parse(fs.readFileSync(process.env.TEST_PERF_AFTER, "utf8"));
208+209+ if (before.failed) {
210+ console.log("Baseline had failing configs; skipping total test-count comparison against partial report.");
211+ process.exit(0);
212+ }
213+214+ const beforeTests = before.totals?.testCount ?? 0;
215+ const afterTests = after.totals?.testCount ?? 0;
216+ if (afterTests < beforeTests) {
217+ console.error(`Test count decreased from ${beforeTests} to ${afterTests}; refusing coverage-reducing patch.`);
218+ process.exit(1);
219+ }
220+ console.log(`Test count preserved: ${beforeTests} -> ${afterTests}.`);
221+ NODE
222+223+ - name: Check changed lanes
224+if: steps.gate.outputs.run_agent == 'true' && steps.patch.outputs.has_changes == 'true'
225+run: pnpm check:changed
226+227+ - name: Commit test performance updates
228+if: steps.gate.outputs.run_agent == 'true' && steps.patch.outputs.has_changes == 'true'
229+env:
230+BASE_SHA: ${{ steps.gate.outputs.base_sha }}
231+GITHUB_TOKEN: ${{ github.token }}
232+TARGET_BRANCH: main
233+run: |
234+ set -euo pipefail
235+236+ if git diff --quiet; then
237+ echo "No test performance changes."
238+ exit 0
239+ fi
240+241+ git config user.name "openclaw-test-performance-agent[bot]"
242+ git config user.email "openclaw-test-performance-agent[bot]@users.noreply.github.com"
243+ git add apps extensions packages scripts src Swabble test ui
244+ git commit --no-verify -m "test: optimize slow tests"
245+246+ for attempt in 1 2 3 4 5; do
247+ if ! git fetch --no-tags origin "${TARGET_BRANCH}"; then
248+ echo "Fetch attempt ${attempt} failed; retrying."
249+ sleep $((attempt * 2))
250+ continue
251+ fi
252+ if git push "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:"${TARGET_BRANCH}"; then
253+ exit 0
254+ fi
255+ remote_main="$(git rev-parse "origin/${TARGET_BRANCH}")"
256+ if [ "$remote_main" != "$BASE_SHA" ]; then
257+ echo "main advanced from ${BASE_SHA} to ${remote_main}; skipping stale test performance update."
258+ exit 0
259+ fi
260+ echo "Test performance update attempt ${attempt} failed; retrying."
261+ sleep $((attempt * 2))
262+ done
263+264+ echo "Failed to push test performance updates after retries." >&2
265+ exit 1
266+267+ - name: Upload test performance artifacts
268+if: steps.gate.outputs.run_agent == 'true' && always()
269+uses: actions/upload-artifact@v7
270+with:
271+name: test-performance-agent-${{ github.run_id }}
272+path: .artifacts/test-perf/
273+if-no-files-found: ignore
274+retention-days: 14
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。