diff --git a/.github/actions/setup-pnpm-store-cache/action.yml b/.github/actions/setup-pnpm-store-cache/action.yml new file mode 100644 index 0000000000..c866393ee4 --- /dev/null +++ b/.github/actions/setup-pnpm-store-cache/action.yml @@ -0,0 +1,41 @@ +name: Setup pnpm + store cache +description: Prepare pnpm via corepack and restore pnpm store cache. +inputs: + pnpm-version: + description: pnpm version to activate via corepack. + required: false + default: "10.23.0" + cache-key-suffix: + description: Suffix appended to the cache key. + required: false + default: "node22" +runs: + using: composite + steps: + - name: Setup pnpm (corepack retry) + shell: bash + run: | + set -euo pipefail + corepack enable + for attempt in 1 2 3; do + if corepack prepare "pnpm@${{ inputs.pnpm-version }}" --activate; then + pnpm -v + exit 0 + fi + echo "corepack prepare failed (attempt $attempt/3). Retrying..." + sleep $((attempt * 10)) + done + exit 1 + + - name: Resolve pnpm store path + id: pnpm-store + shell: bash + run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT" + + - name: Restore pnpm store cache + uses: actions/cache@v4 + with: + path: ${{ steps.pnpm-store.outputs.path }} + key: ${{ runner.os }}-pnpm-store-${{ inputs.cache-key-suffix }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store-${{ inputs.cache-key-suffix }}- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c12a9e09a6..8126492909 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,9 +27,101 @@ jobs: id: check uses: ./.github/actions/detect-docs-only - install-check: + # Detect which heavy areas are touched so PRs can skip unrelated expensive jobs. + # Push to main keeps broad coverage. + changed-scope: needs: [docs-scope] if: needs.docs-scope.outputs.docs_only != 'true' + runs-on: ubuntu-latest + outputs: + run_node: ${{ steps.scope.outputs.run_node }} + run_macos: ${{ steps.scope.outputs.run_macos }} + run_android: ${{ steps.scope.outputs.run_android }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: false + + - name: Detect changed scopes + id: scope + shell: bash + run: | + set -euo pipefail + + if [ "${{ github.event_name }}" = "push" ]; then + BASE="${{ github.event.before }}" + else + BASE="${{ github.event.pull_request.base.sha }}" + fi + + CHANGED="$(git diff --name-only "$BASE" HEAD 2>/dev/null || echo "UNKNOWN")" + if [ "$CHANGED" = "UNKNOWN" ] || [ -z "$CHANGED" ]; then + # Fail-safe: run broad checks if detection fails. + echo "run_node=true" >> "$GITHUB_OUTPUT" + echo "run_macos=true" >> "$GITHUB_OUTPUT" + echo "run_android=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + run_node=false + run_macos=false + run_android=false + has_non_docs=false + has_non_native_non_docs=false + + while IFS= read -r path; do + [ -z "$path" ] && continue + case "$path" in + docs/*|*.md|*.mdx) + continue + ;; + *) + has_non_docs=true + ;; + esac + + case "$path" in + apps/macos/*|apps/ios/*|apps/shared/*|Swabble/*) + run_macos=true + ;; + esac + + case "$path" in + apps/android/*|apps/shared/*) + run_android=true + ;; + esac + + case "$path" in + src/*|test/*|extensions/*|packages/*|scripts/*|ui/*|.github/*|openclaw.mjs|package.json|pnpm-lock.yaml|pnpm-workspace.yaml|tsconfig*.json|vitest*.ts|tsdown.config.ts|.oxlintrc.json|.oxfmtrc.jsonc) + run_node=true + ;; + esac + + case "$path" in + apps/android/*|apps/ios/*|apps/macos/*|apps/shared/*|Swabble/*|appcast.xml) + ;; + *) + has_non_native_non_docs=true + ;; + esac + done <<< "$CHANGED" + + # If there are non-doc files outside native app trees, keep Node checks enabled. + if [ "$run_node" = false ] && [ "$has_non_docs" = true ] && [ "$has_non_native_non_docs" = true ]; then + run_node=true + fi + + echo "run_node=${run_node}" >> "$GITHUB_OUTPUT" + echo "run_macos=${run_macos}" >> "$GITHUB_OUTPUT" + echo "run_android=${run_android}" >> "$GITHUB_OUTPUT" + + # Build dist once for Node-relevant changes and share it with downstream jobs. + build-artifacts: + needs: [docs-scope, changed-scope] + if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true') runs-on: blacksmith-4vcpu-ubuntu-2404 steps: - name: Checkout @@ -56,20 +148,76 @@ jobs: node-version: 22.x check-latest: true - - name: Setup pnpm (corepack retry) + - name: Setup pnpm + cache store + uses: ./.github/actions/setup-pnpm-store-cache + with: + pnpm-version: "10.23.0" + cache-key-suffix: "node22" + + - name: Runtime versions + run: | + node -v + npm -v + pnpm -v + + - name: Capture node path + run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV" + + - name: Install dependencies (frozen) + env: + CI: true + run: | + export PATH="$NODE_BIN:$PATH" + which node + node -v + pnpm -v + pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true + + - name: Build dist + run: pnpm build + + - name: Upload dist artifact + uses: actions/upload-artifact@v4 + with: + name: dist-build + path: dist/ + retention-days: 1 + + install-check: + needs: [docs-scope, changed-scope] + if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true') + runs-on: blacksmith-4vcpu-ubuntu-2404 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: false + + - name: Checkout submodules (retry) run: | set -euo pipefail - corepack enable - for attempt in 1 2 3; do - if corepack prepare pnpm@10.23.0 --activate; then - pnpm -v + git submodule sync --recursive + for attempt in 1 2 3 4 5; do + if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then exit 0 fi - echo "corepack prepare failed (attempt $attempt/3). Retrying..." + echo "Submodule update failed (attempt $attempt/5). Retrying…" sleep $((attempt * 10)) done exit 1 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.x + check-latest: true + + - name: Setup pnpm + cache store + uses: ./.github/actions/setup-pnpm-store-cache + with: + pnpm-version: "10.23.0" + cache-key-suffix: "node22" + - name: Runtime versions run: | node -v @@ -90,8 +238,8 @@ jobs: pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true checks: - needs: [docs-scope] - if: needs.docs-scope.outputs.docs_only != 'true' + needs: [docs-scope, changed-scope] + if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true') runs-on: blacksmith-4vcpu-ubuntu-2404 strategy: fail-fast: false @@ -108,7 +256,7 @@ jobs: command: pnpm protocol:check - runtime: bun task: test - command: pnpm canvas:a2ui:bundle && bunx vitest run + command: pnpm canvas:a2ui:bundle && bunx vitest run --config vitest.unit.config.ts steps: - name: Checkout uses: actions/checkout@v4 @@ -134,19 +282,11 @@ jobs: node-version: 22.x check-latest: true - - name: Setup pnpm (corepack retry) - run: | - set -euo pipefail - corepack enable - for attempt in 1 2 3; do - if corepack prepare pnpm@10.23.0 --activate; then - pnpm -v - exit 0 - fi - echo "corepack prepare failed (attempt $attempt/3). Retrying..." - sleep $((attempt * 10)) - done - exit 1 + - name: Setup pnpm + cache store + uses: ./.github/actions/setup-pnpm-store-cache + with: + pnpm-version: "10.23.0" + cache-key-suffix: "node22" - name: Setup Bun uses: oven-sh/setup-bun@v2 @@ -184,7 +324,7 @@ jobs: matrix: include: - task: lint - command: pnpm build && pnpm lint + command: pnpm lint - task: format command: pnpm format steps: @@ -212,19 +352,11 @@ jobs: node-version: 22.x check-latest: true - - name: Setup pnpm (corepack retry) - run: | - set -euo pipefail - corepack enable - for attempt in 1 2 3; do - if corepack prepare pnpm@10.23.0 --activate; then - pnpm -v - exit 0 - fi - echo "corepack prepare failed (attempt $attempt/3). Retrying..." - sleep $((attempt * 10)) - done - exit 1 + - name: Setup pnpm + cache store + uses: ./.github/actions/setup-pnpm-store-cache + with: + pnpm-version: "10.23.0" + cache-key-suffix: "node22" - name: Setup Bun uses: oven-sh/setup-bun@v2 @@ -280,8 +412,8 @@ jobs: fi checks-windows: - needs: [docs-scope] - if: needs.docs-scope.outputs.docs_only != 'true' + needs: [docs-scope, changed-scope, build-artifacts] + if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true') runs-on: blacksmith-4vcpu-windows-2025 env: NODE_OPTIONS: --max-old-space-size=4096 @@ -296,8 +428,8 @@ jobs: matrix: include: - runtime: node - task: build & lint - command: pnpm build && pnpm lint + task: lint + command: pnpm lint - runtime: node task: test command: pnpm canvas:a2ui:bundle && pnpm test @@ -342,25 +474,31 @@ jobs: done exit 1 + - name: Download dist artifact (lint lane) + if: matrix.task == 'lint' + uses: actions/download-artifact@v4 + with: + name: dist-build + path: dist/ + + - name: Verify dist artifact (lint lane) + if: matrix.task == 'lint' + run: | + set -euo pipefail + test -s dist/index.js + test -s dist/plugin-sdk/index.js + - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 22.x check-latest: true - - name: Setup pnpm (corepack retry) - run: | - set -euo pipefail - corepack enable - for attempt in 1 2 3; do - if corepack prepare pnpm@10.23.0 --activate; then - pnpm -v - exit 0 - fi - echo "corepack prepare failed (attempt $attempt/3). Retrying..." - sleep $((attempt * 10)) - done - exit 1 + - name: Setup pnpm + cache store + uses: ./.github/actions/setup-pnpm-store-cache + with: + pnpm-version: "10.23.0" + cache-key-suffix: "node22" - name: Setup Bun uses: oven-sh/setup-bun@v2 @@ -395,8 +533,8 @@ jobs: # running 4 separate jobs per PR (as before) starved the queue. One job # per PR allows 5 PRs to run macOS checks simultaneously. macos: - needs: [docs-scope] - if: github.event_name == 'pull_request' && needs.docs-scope.outputs.docs_only != 'true' + needs: [docs-scope, changed-scope] + if: github.event_name == 'pull_request' && needs.docs-scope.outputs.docs_only != 'true' && needs.changed-scope.outputs.run_macos == 'true' runs-on: macos-latest steps: - name: Checkout @@ -424,19 +562,11 @@ jobs: node-version: 22.x check-latest: true - - name: Setup pnpm (corepack retry) - run: | - set -euo pipefail - corepack enable - for attempt in 1 2 3; do - if corepack prepare pnpm@10.23.0 --activate; then - pnpm -v - exit 0 - fi - echo "corepack prepare failed (attempt $attempt/3). Retrying..." - sleep $((attempt * 10)) - done - exit 1 + - name: Setup pnpm + cache store + uses: ./.github/actions/setup-pnpm-store-cache + with: + pnpm-version: "10.23.0" + cache-key-suffix: "node22" - name: Runtime versions run: | @@ -681,8 +811,8 @@ jobs: PY android: - needs: [docs-scope] - if: needs.docs-scope.outputs.docs_only != 'true' + needs: [docs-scope, changed-scope] + if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_android == 'true') runs-on: blacksmith-4vcpu-ubuntu-2404 strategy: fail-fast: false