From 0c75b9ce009b3f37eed557a170062fad246a11cd Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 20 Apr 2026 18:46:42 +0100 Subject: [PATCH] ci: speed up fast security checks --- .github/workflows/ci.yml | 77 +++++++++++++++++++++++++++++----------- docs/ci.md | 16 +++++---- 2 files changed, 66 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6c5d92ac47..f63ec92f86c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -319,14 +319,14 @@ jobs: # Run the fast security/SCM checks in parallel with scope detection so the # main Node jobs do not have to wait for Python/pre-commit setup. - security-fast: + security-scm-fast: permissions: contents: read if: github.event_name != 'pull_request' || !github.event.pull_request.draft runs-on: ${{ github.repository == 'openclaw/openclaw' && 'blacksmith-16vcpu-ubuntu-2404' || 'ubuntu-24.04' }} timeout-minutes: 20 env: - PRE_COMMIT_CACHE_KEY_SUFFIX: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.sha }} + PRE_COMMIT_HOME: .cache/pre-commit-security-fast steps: - name: Checkout uses: actions/checkout@v6 @@ -352,35 +352,22 @@ jobs: git show "${BASE_SHA}:.pre-commit-config.yaml" > "$trusted_config" echo "PRE_COMMIT_CONFIG_PATH=$trusted_config" >> "$GITHUB_ENV" - - name: Setup Node environment - uses: ./.github/actions/setup-node-env - with: - install-bun: "false" - install-deps: "false" - - name: Setup Python id: setup-python uses: actions/setup-python@v6 with: python-version: "3.12" - cache: "pip" - cache-dependency-path: | - pyproject.toml - .pre-commit-config.yaml - .github/workflows/ci.yml - name: Restore pre-commit cache uses: actions/cache@v5 with: - path: ~/.cache/pre-commit - key: pre-commit-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }}-${{ env.PRE_COMMIT_CACHE_KEY_SUFFIX }} + path: .cache/pre-commit-security-fast + key: pre-commit-security-fast-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }} restore-keys: | - pre-commit-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }}- + pre-commit-security-fast-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}- - name: Install pre-commit - run: | - python -m pip install --upgrade pip - python -m pip install pre-commit==4.2.0 + run: python -m pip install --disable-pip-version-check pre-commit==4.2.0 - name: Detect committed private keys run: pre-commit run --config "${PRE_COMMIT_CONFIG_PATH:-.pre-commit-config.yaml}" --all-files detect-private-key @@ -412,8 +399,58 @@ jobs: printf 'Auditing workflow files:\n%s\n' "${workflow_files[@]}" pre-commit run --config "${PRE_COMMIT_CONFIG_PATH:-.pre-commit-config.yaml}" zizmor --files "${workflow_files[@]}" + security-dependency-audit: + permissions: + contents: read + if: github.event_name != 'pull_request' || !github.event.pull_request.draft + runs-on: ${{ github.repository == 'openclaw/openclaw' && 'blacksmith-16vcpu-ubuntu-2404' || 'ubuntu-24.04' }} + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1 + fetch-tags: false + persist-credentials: false + submodules: false + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24.x" + check-latest: false + - name: Audit production dependencies - run: pre-commit run --config "${PRE_COMMIT_CONFIG_PATH:-.pre-commit-config.yaml}" --all-files pnpm-audit-prod + run: node scripts/pre-commit/pnpm-audit-prod.mjs --audit-level=high + + security-fast: + permissions: {} + needs: [security-scm-fast, security-dependency-audit] + if: always() && (github.event_name != 'pull_request' || !github.event.pull_request.draft) + runs-on: ${{ github.repository == 'openclaw/openclaw' && 'blacksmith-16vcpu-ubuntu-2404' || 'ubuntu-24.04' }} + timeout-minutes: 5 + steps: + - name: Verify fast security jobs + env: + DEPENDENCY_AUDIT_RESULT: ${{ needs.security-dependency-audit.result }} + SCM_RESULT: ${{ needs.security-scm-fast.result }} + run: | + set -euo pipefail + failed=0 + + for result in \ + "security-scm-fast=${SCM_RESULT}" \ + "security-dependency-audit=${DEPENDENCY_AUDIT_RESULT}" + do + job="${result%%=*}" + status="${result#*=}" + if [ "$status" != "success" ]; then + echo "::error::${job} ended with ${status}" + failed=1 + fi + done + + exit "$failed" # Build dist once for Node-relevant changes and share it with downstream jobs. # Keep this overlapping with the fast correctness lanes so green PRs get heavy diff --git a/docs/ci.md b/docs/ci.md index a73e61c1446..3b5e3951aac 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -15,7 +15,9 @@ The CI runs on every push to `main` and every pull request. It uses smart scopin | Job | Purpose | When it runs | | -------------------------------- | -------------------------------------------------------------------------------------------- | ----------------------------------- | | `preflight` | Detect docs-only changes, changed scopes, changed extensions, and build the CI manifest | Always on non-draft pushes and PRs | -| `security-fast` | Private key detection, workflow audit via `zizmor`, production dependency audit | Always on non-draft pushes and PRs | +| `security-scm-fast` | Private key detection and workflow audit via `zizmor` | Always on non-draft pushes and PRs | +| `security-dependency-audit` | Dependency-free production lockfile audit against npm advisories | Always on non-draft pushes and PRs | +| `security-fast` | Required aggregate for the fast security jobs | Always on non-draft pushes and PRs | | `build-artifacts` | Build `dist/` and the Control UI once, upload reusable artifacts for downstream jobs | Node-relevant changes | | `checks-fast-core` | Fast Linux correctness lanes such as bundled/plugin-contract/protocol checks | Node-relevant changes | | `checks-fast-contracts-channels` | Sharded channel contract checks with a stable aggregate check result | Node-relevant changes | @@ -38,7 +40,7 @@ The CI runs on every push to `main` and every pull request. It uses smart scopin Jobs are ordered so cheap checks fail before expensive ones run: 1. `preflight` decides which lanes exist at all. The `docs-scope` and `changed-scope` logic are steps inside this job, not standalone jobs. -2. `security-fast`, `check`, `check-additional`, `check-docs`, and `skills-python` fail quickly without waiting on the heavier artifact and platform matrix jobs. +2. `security-scm-fast`, `security-dependency-audit`, `security-fast`, `check`, `check-additional`, `check-docs`, and `skills-python` fail quickly without waiting on the heavier artifact and platform matrix jobs. 3. `build-artifacts` overlaps with the fast Linux lanes so downstream consumers can start as soon as the shared build is ready. 4. Heavier platform and runtime lanes fan out after that: `checks-fast-core`, `checks-fast-contracts-channels`, `checks-node-extensions`, `checks-node-core-test`, `extension-fast`, `checks`, `checks-windows`, `macos-node`, `macos-swift`, and `android`. @@ -53,11 +55,11 @@ The slowest Node test families are split into include-file shards so each job st ## Runners -| Runner | Jobs | -| -------------------------------- | ---------------------------------------------------------------------------------------------------- | -| `blacksmith-16vcpu-ubuntu-2404` | `preflight`, `security-fast`, `build-artifacts`, Linux checks, docs checks, Python skills, `android` | -| `blacksmith-32vcpu-windows-2025` | `checks-windows` | -| `macos-latest` | `macos-node`, `macos-swift` | +| Runner | Jobs | +| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `blacksmith-16vcpu-ubuntu-2404` | `preflight`, `security-scm-fast`, `security-dependency-audit`, `security-fast`, `build-artifacts`, Linux checks, docs checks, Python skills, `android` | +| `blacksmith-32vcpu-windows-2025` | `checks-windows` | +| `macos-latest` | `macos-node`, `macos-swift` | ## Local Equivalents