diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c764606b60a..06e4167d6f9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1237,11 +1237,6 @@ jobs: # cores for roughly the same billed core-minutes. runs-on: ${{ vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' && 'ubuntu-24.04' || (vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' && github.run_attempt > 1) && 'ubuntu-24.04' || github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'openclaw/openclaw') && 'blacksmith-32vcpu-ubuntu-2404' || 'ubuntu-24.04') }} timeout-minutes: ${{ (vars.OPENCLAW_CI_RUNNER_BACKEND == 'github' || (vars.OPENCLAW_CI_RUNNER_BACKEND == 'hybrid' && github.run_attempt > 1) || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository)) && 35 || 20 }} - outputs: - channels-result: ${{ steps.built_artifact_checks.outputs['channels-result'] }} - core-support-boundary-result: ${{ steps.built_artifact_checks.outputs['core-support-boundary-result'] }} - gateway-watch-result: ${{ steps.built_artifact_checks.outputs['gateway-watch-result'] }} - tui-pty-result: ${{ steps.built_artifact_checks.outputs['tui-pty-result'] }} steps: - *linux_node_checkout_step - name: Ensure secrets base commit (PR fast path) @@ -4172,60 +4167,3 @@ jobs: done <<< "$SELECTED_RESULTS" exit "$failures" - - ci-timings-summary: - permissions: - actions: read - contents: read - name: ci-timings-summary - needs: - - preflight - - security-fast - - pnpm-store-warmup - - build-artifacts - - checks-ui - - checks-ui-e2e - - checks-ui-e2e-real-gateway - - control-ui-i18n - - checks-fast-core - - checks-fast-plugin-contracts-shard - - checks-fast-channel-contracts-shard - - checks-node-compat - - checks-node-core-test-nondist-shard - - check-shard - - check-lint-hosted-core-shard - - check-additional-shard - - check-docs - - skills-python - - checks-windows - - macos-node - - macos-swift - - ios-build - - android - # TODO(2026-10-01): Re-enable or delete the CI timing-summary job after the next timing-optimization review. - if: ${{ false && !cancelled() && always() && github.event_name != 'push' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) }} - runs-on: ubuntu-24.04 - timeout-minutes: 5 - steps: - - name: Checkout timing summary helper - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || needs.preflight.outputs.checkout_revision || github.sha }} - fetch-depth: 1 - fetch-tags: false - persist-credentials: false - submodules: false - - - name: Write CI timing summary - env: - GH_TOKEN: ${{ github.token }} - run: | - node scripts/ci-run-timings.mjs "$GITHUB_RUN_ID" --limit 25 > ci-timings-summary.txt - cat ci-timings-summary.txt >> "$GITHUB_STEP_SUMMARY" - - - name: Upload CI timing summary - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 - with: - name: ci-timings-summary - path: ci-timings-summary.txt - retention-days: 14 diff --git a/docs/ci.md b/docs/ci.md index 526e6de22dae..6026190c668f 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -80,7 +80,7 @@ GitHub may mark superseded pull-request jobs as `cancelled` when a newer head la Use `pnpm ci:timings`, `pnpm ci:timings:recent`, or `node scripts/ci-run-timings.mjs ` to summarize wall time, start delay, slowest jobs, failures, and the `pnpm-store-warmup` fanout barrier from GitHub Actions. Use `pnpm ci:timings:trend` for a 72-hour baseline and a latest-12-hours versus prior-12-hours comparison. Trend mode includes every main push outcome, cancellation/pass rates, and successful-run wall time, then loads a balanced latest/prior sample of at most 100 successful runs by default. Its detailed sample separates workflow admission, job dependency/gate delay (`job.created_at` minus the first job's creation), runner queue/start latency (`job.started_at` minus `job.created_at`), and execution; it also reports critical-path ownership and the actual GitHub API request count. Reruns use attempt-specific jobs and are excluded from run-level wall/admission distributions because GitHub retains the original workflow creation time. Raise or lower the detailed-run selection cap with `--detail-runs` (a run with more than 100 jobs requires multiple requests), emit JSON to stdout with `--json`, or save the same report with `--output .artifacts/ci-timings/trend.json`; missing output directories are created automatically. The baseline must cover at least two comparison windows. -The in-workflow `ci-timings-summary` job exists in `ci.yml` but is currently disabled (`if: false`); run the timing helper locally instead. For build timing, check the `build-artifacts` job's `Build dist` step: `pnpm build:ci-artifacts` prints `[build-all] phase timings:` and includes `ui:build`; the job also uploads the `startup-memory` artifact. +Run the timing helper locally; there is no in-workflow timing-summary job (a permanently disabled one was removed once the local helper became the tool everyone actually used). For build timing, check the `build-artifacts` job's `Build dist` step: `pnpm build:ci-artifacts` prints `[build-all] phase timings:` and includes `ui:build`; the job also uploads the `startup-memory` artifact. ## PR context and evidence diff --git a/test/scripts/ci-workflow-guards.test.ts b/test/scripts/ci-workflow-guards.test.ts index b34356edcab1..9c0e7c963560 100644 --- a/test/scripts/ci-workflow-guards.test.ts +++ b/test/scripts/ci-workflow-guards.test.ts @@ -7054,9 +7054,10 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}" expect(workflow.on.pull_request).not.toHaveProperty("paths-ignore"); expect(gate.name).toBe("openclaw/ci-gate"); expect(gate.needs).toEqual([...requiredJobs, ...selectedJobs]); + // Every job in the file is gated; a new lane cannot slip in ungated. expect(gate.needs.toSorted()).toEqual( Object.keys(workflow.jobs) - .filter((job) => job !== "ci-gate" && job !== "ci-timings-summary") + .filter((job) => job !== "ci-gate") .toSorted(), ); expect(gate.if).toBe(