feat: add RuntimeAdapter interface and OpenClawGatewayAdapter (Phase 1) #549
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: PR Check | |
| on: | |
| pull_request: | |
| branches: [main] | |
| concurrency: | |
| group: pr-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| changes: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| pull-requests: read | |
| outputs: | |
| quality: ${{ steps.plan.outputs.quality }} | |
| architecture: ${{ steps.plan.outputs.architecture }} | |
| ui_contract: ${{ steps.plan.outputs.ui_contract }} | |
| renderer_copy: ${{ steps.plan.outputs.renderer_copy }} | |
| i18n: ${{ steps.plan.outputs.i18n }} | |
| dead_code: ${{ steps.plan.outputs.dead_code }} | |
| typecheck_shared: ${{ steps.plan.outputs.typecheck_shared }} | |
| typecheck_core: ${{ steps.plan.outputs.typecheck_core }} | |
| typecheck_desktop: ${{ steps.plan.outputs.typecheck_desktop }} | |
| typecheck_pwa: ${{ steps.plan.outputs.typecheck_pwa }} | |
| typecheck_website: ${{ steps.plan.outputs.typecheck_website }} | |
| test_shared: ${{ steps.plan.outputs.test_shared }} | |
| test_core: ${{ steps.plan.outputs.test_core }} | |
| test_desktop: ${{ steps.plan.outputs.test_desktop }} | |
| test_pwa: ${{ steps.plan.outputs.test_pwa }} | |
| build_desktop: ${{ steps.plan.outputs.build_desktop }} | |
| test_job: ${{ steps.plan.outputs.test_job }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - id: filter | |
| uses: dorny/paths-filter@v4 | |
| with: | |
| filters: | | |
| global: | |
| - 'package.json' | |
| - 'pnpm-lock.yaml' | |
| - 'pnpm-workspace.yaml' | |
| - 'tsconfig.base.json' | |
| - 'eslint.config.mjs' | |
| - 'knip.json' | |
| - 'scripts/**' | |
| - '.github/workflows/pr-check.yml' | |
| - '.github/workflows/e2e.yml' | |
| shared: | |
| - 'packages/shared/**' | |
| core: | |
| - 'packages/core/**' | |
| desktop_renderer: | |
| - 'packages/desktop/src/renderer/**' | |
| - 'packages/desktop/test/**/*.tsx' | |
| desktop_main: | |
| - 'packages/desktop/src/main/**' | |
| - 'packages/desktop/src/preload/**' | |
| - 'packages/desktop/test/**/*.ts' | |
| desktop_package: | |
| - 'packages/desktop/package.json' | |
| - 'packages/desktop/electron.vite.config.ts' | |
| - 'packages/desktop/scripts/**' | |
| pwa: | |
| - 'packages/pwa/**' | |
| website: | |
| - 'website/**' | |
| - '.github/workflows/website.yml' | |
| keynote: | |
| - 'keynote/**' | |
| - id: plan | |
| env: | |
| GLOBAL: ${{ steps.filter.outputs.global }} | |
| SHARED: ${{ steps.filter.outputs.shared }} | |
| CORE: ${{ steps.filter.outputs.core }} | |
| DESKTOP_RENDERER: ${{ steps.filter.outputs.desktop_renderer }} | |
| DESKTOP_MAIN: ${{ steps.filter.outputs.desktop_main }} | |
| DESKTOP_PACKAGE: ${{ steps.filter.outputs.desktop_package }} | |
| PWA: ${{ steps.filter.outputs.pwa }} | |
| WEBSITE: ${{ steps.filter.outputs.website }} | |
| KEYNOTE: ${{ steps.filter.outputs.keynote }} | |
| run: | | |
| set_flag() { | |
| local name="$1" | |
| shift | |
| local value=false | |
| for item in "$@"; do | |
| if [ "$item" = "true" ]; then | |
| value=true | |
| break | |
| fi | |
| done | |
| echo "$name=$value" >> "$GITHUB_OUTPUT" | |
| } | |
| set_flag quality "$GLOBAL" "$SHARED" "$CORE" "$DESKTOP_RENDERER" "$DESKTOP_MAIN" "$DESKTOP_PACKAGE" "$PWA" "$WEBSITE" | |
| set_flag architecture "$GLOBAL" "$SHARED" "$CORE" "$DESKTOP_MAIN" | |
| set_flag ui_contract "$GLOBAL" "$DESKTOP_RENDERER" "$PWA" | |
| set_flag renderer_copy "$GLOBAL" "$DESKTOP_RENDERER" | |
| set_flag i18n "$GLOBAL" "$SHARED" "$CORE" "$DESKTOP_RENDERER" "$PWA" | |
| set_flag dead_code "$GLOBAL" "$SHARED" "$CORE" "$DESKTOP_RENDERER" "$DESKTOP_MAIN" "$DESKTOP_PACKAGE" "$PWA" "$WEBSITE" | |
| set_flag typecheck_shared "$GLOBAL" "$SHARED" | |
| set_flag typecheck_core "$GLOBAL" "$SHARED" "$CORE" | |
| set_flag typecheck_desktop "$GLOBAL" "$SHARED" "$CORE" "$DESKTOP_RENDERER" "$DESKTOP_MAIN" "$DESKTOP_PACKAGE" | |
| set_flag typecheck_pwa "$GLOBAL" "$SHARED" "$CORE" "$PWA" | |
| set_flag typecheck_website "$GLOBAL" "$WEBSITE" | |
| set_flag test_shared "$GLOBAL" "$SHARED" | |
| set_flag test_core "$GLOBAL" "$SHARED" "$CORE" | |
| set_flag test_desktop "$GLOBAL" "$SHARED" "$CORE" "$DESKTOP_RENDERER" "$DESKTOP_MAIN" "$DESKTOP_PACKAGE" | |
| set_flag test_pwa "$GLOBAL" "$SHARED" "$CORE" "$PWA" | |
| set_flag build_desktop "$GLOBAL" "$SHARED" "$CORE" "$DESKTOP_RENDERER" "$DESKTOP_MAIN" "$DESKTOP_PACKAGE" | |
| set_flag test_job "$GLOBAL" "$SHARED" "$CORE" "$DESKTOP_RENDERER" "$DESKTOP_MAIN" "$DESKTOP_PACKAGE" "$PWA" "$WEBSITE" | |
| quality: | |
| needs: [changes] | |
| if: needs.changes.outputs.quality == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v5 | |
| with: | |
| version: 10 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 20 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Lint | |
| run: pnpm lint | |
| - name: Format check | |
| run: pnpm format:check | |
| - name: Architecture guardrails | |
| if: needs.changes.outputs.architecture == 'true' | |
| run: pnpm check:architecture | |
| - name: UI contract | |
| if: needs.changes.outputs.ui_contract == 'true' | |
| run: pnpm check:ui-contract | |
| - name: Renderer copy contract | |
| if: needs.changes.outputs.renderer_copy == 'true' | |
| run: pnpm check:renderer-copy | |
| - name: i18n key parity | |
| if: needs.changes.outputs.i18n == 'true' | |
| run: pnpm check:i18n | |
| - name: Dead code detection | |
| if: needs.changes.outputs.dead_code == 'true' | |
| run: pnpm check:dead-code | |
| test: | |
| needs: [changes] | |
| if: needs.changes.outputs.test_job == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v5 | |
| with: | |
| version: 10 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 20 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Type check shared | |
| if: needs.changes.outputs.typecheck_shared == 'true' || needs.changes.outputs.typecheck_core == 'true' || needs.changes.outputs.typecheck_desktop == 'true' || needs.changes.outputs.typecheck_pwa == 'true' | |
| run: pnpm --filter @clawwork/shared exec tsc -b | |
| - name: Type check core | |
| if: needs.changes.outputs.typecheck_core == 'true' || needs.changes.outputs.typecheck_desktop == 'true' || needs.changes.outputs.typecheck_pwa == 'true' | |
| run: pnpm --filter @clawwork/core exec tsc -b | |
| - name: Type check desktop | |
| if: needs.changes.outputs.typecheck_desktop == 'true' | |
| run: pnpm --filter @clawwork/desktop exec tsc --noEmit | |
| - name: Type check PWA | |
| if: needs.changes.outputs.typecheck_pwa == 'true' | |
| run: pnpm --filter @clawwork/pwa exec tsc --noEmit | |
| - name: Type check website | |
| if: needs.changes.outputs.typecheck_website == 'true' | |
| run: pnpm --filter @clawwork/website exec tsc --noEmit | |
| - name: Type check shared tests | |
| if: needs.changes.outputs.test_shared == 'true' | |
| run: pnpm --filter @clawwork/shared exec tsc --noEmit -p tsconfig.test.json | |
| - name: Type check core tests | |
| if: needs.changes.outputs.test_core == 'true' | |
| run: pnpm --filter @clawwork/core exec tsc --noEmit -p tsconfig.test.json | |
| - name: Type check desktop tests | |
| if: needs.changes.outputs.test_desktop == 'true' | |
| run: pnpm --filter @clawwork/desktop exec tsc --noEmit -p tsconfig.test.json | |
| - name: Type check PWA tests | |
| if: needs.changes.outputs.test_pwa == 'true' | |
| run: pnpm --filter @clawwork/pwa exec tsc --noEmit -p tsconfig.test.json | |
| - name: Test shared | |
| if: needs.changes.outputs.test_shared == 'true' | |
| run: pnpm --filter @clawwork/shared test | |
| - name: Test core | |
| if: needs.changes.outputs.test_core == 'true' | |
| run: pnpm --filter @clawwork/core test | |
| - name: Test desktop | |
| if: needs.changes.outputs.test_desktop == 'true' | |
| run: pnpm --filter @clawwork/desktop test | |
| - name: Test PWA | |
| if: needs.changes.outputs.test_pwa == 'true' | |
| run: pnpm --filter @clawwork/pwa test | |
| build: | |
| needs: [changes, quality, test] | |
| if: needs.changes.outputs.build_desktop == 'true' && needs.quality.result != 'failure' && needs.test.result != 'failure' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v5 | |
| with: | |
| version: 10 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 20 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build Electron app | |
| run: pnpm --filter @clawwork/desktop build | |
| - name: Package Linux smoke build | |
| run: pnpm --filter @clawwork/desktop exec electron-builder --linux --x64 --publish never | |
| secrets-scan: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| env: | |
| GITLEAKS_VERSION: 8.30.1 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install gitleaks | |
| run: | | |
| curl -fsSL \ | |
| "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \ | |
| | tar -xz -C /usr/local/bin gitleaks | |
| gitleaks version | |
| - name: Scan PR diff | |
| env: | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: gitleaks detect --source . --log-opts "${BASE_SHA}..${HEAD_SHA}" --redact --verbose --no-banner | |
| ci-passed: | |
| needs: [changes, quality, test, build, secrets-scan] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Verify aggregate status | |
| env: | |
| NEEDS: ${{ toJson(needs) }} | |
| run: | | |
| echo "$NEEDS" | |
| failed=$(jq -r '[to_entries[] | select(.value.result == "failure" or .value.result == "cancelled") | .key] | join(", ")' <<< "$NEEDS") | |
| if [ -n "$failed" ]; then | |
| echo "::error::Required gates failed or cancelled: $failed" | |
| exit 1 | |
| fi | |
| echo "All required gates passed or skipped cleanly." |