name: CodeQL on: push: branches: [main] pull_request: workflow_dispatch: concurrency: group: codeql-${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" permissions: actions: read contents: read security-events: write jobs: # Detect docs-only changes to skip all analysis (same pattern as CI). docs-scope: runs-on: blacksmith-16vcpu-ubuntu-2404 outputs: docs_only: ${{ steps.check.outputs.docs_only }} steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 1 fetch-tags: false submodules: false - name: Ensure docs-scope base commit if: github.event_name != 'workflow_dispatch' uses: ./.github/actions/ensure-base-commit with: base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }} fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }} - name: Detect docs-only changes if: github.event_name != 'workflow_dispatch' id: check uses: ./.github/actions/detect-docs-changes # Detect which areas changed so each language only runs on relevant PRs. # Push to main and workflow_dispatch keep broad coverage. changed-scope: needs: [docs-scope] if: needs.docs-scope.outputs.docs_only != 'true' runs-on: blacksmith-16vcpu-ubuntu-2404 outputs: run_node: ${{ steps.scope.outputs.run_node }} run_macos: ${{ steps.scope.outputs.run_macos }} run_android: ${{ steps.scope.outputs.run_android }} run_skills_python: ${{ steps.scope.outputs.run_skills_python }} steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 1 fetch-tags: false submodules: false - name: Ensure changed-scope base commit if: github.event_name != 'workflow_dispatch' uses: ./.github/actions/ensure-base-commit with: base-sha: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }} fetch-ref: ${{ github.event_name == 'push' && github.ref_name || github.event.pull_request.base.ref }} - name: Detect changed scopes id: scope shell: bash run: | set -euo pipefail if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then # Manual runs analyze everything. echo "run_node=true" >> "$GITHUB_OUTPUT" echo "run_macos=true" >> "$GITHUB_OUTPUT" echo "run_android=true" >> "$GITHUB_OUTPUT" echo "run_skills_python=true" >> "$GITHUB_OUTPUT" exit 0 fi if [ "${{ github.event_name }}" = "push" ]; then BASE="${{ github.event.before }}" else BASE="${{ github.event.pull_request.base.sha }}" fi node scripts/ci-changed-scope.mjs --base "$BASE" --head HEAD analyze: name: Analyze (${{ matrix.language }}) needs: [docs-scope, changed-scope] if: needs.docs-scope.outputs.docs_only != 'true' && needs.changed-scope.result != 'failure' runs-on: ${{ matrix.runs_on }} strategy: fail-fast: false matrix: include: - language: javascript-typescript runs_on: blacksmith-16vcpu-ubuntu-2404 scope: run_node needs_node: true needs_python: false needs_java: false needs_swift_tools: false needs_manual_build: false needs_autobuild: false config_file: ./.github/codeql/codeql-javascript-typescript.yml - language: actions runs_on: blacksmith-16vcpu-ubuntu-2404 scope: run_node needs_node: false needs_python: false needs_java: false needs_swift_tools: false needs_manual_build: false needs_autobuild: false config_file: "" - language: python runs_on: blacksmith-16vcpu-ubuntu-2404 scope: run_skills_python needs_node: false needs_python: true needs_java: false needs_swift_tools: false needs_manual_build: false needs_autobuild: false config_file: "" - language: java-kotlin runs_on: blacksmith-16vcpu-ubuntu-2404 scope: run_android needs_node: false needs_python: false needs_java: true needs_swift_tools: false needs_manual_build: true needs_autobuild: false config_file: "" - language: swift runs_on: macos-latest scope: run_macos needs_node: false needs_python: false needs_java: false needs_swift_tools: true needs_manual_build: true needs_autobuild: false config_file: "" steps: # Skip this matrix entry when the relevant scope was not touched. # Push to main and workflow_dispatch set all scopes to true (run everything). - name: Check scope id: scope-check shell: bash env: EVENT_NAME: ${{ github.event_name }} RUN_NODE: ${{ needs.changed-scope.outputs.run_node }} RUN_MACOS: ${{ needs.changed-scope.outputs.run_macos }} RUN_ANDROID: ${{ needs.changed-scope.outputs.run_android }} RUN_SKILLS_PYTHON: ${{ needs.changed-scope.outputs.run_skills_python }} SCOPE_KEY: ${{ matrix.scope }} run: | # Map the matrix scope key to the corresponding output value. case "$SCOPE_KEY" in run_node) SHOULD_RUN="$RUN_NODE" ;; run_macos) SHOULD_RUN="$RUN_MACOS" ;; run_android) SHOULD_RUN="$RUN_ANDROID" ;; run_skills_python) SHOULD_RUN="$RUN_SKILLS_PYTHON" ;; *) SHOULD_RUN="true" ;; esac if [ "$SHOULD_RUN" = "true" ] || [ "$EVENT_NAME" = "push" ] || [ "$EVENT_NAME" = "workflow_dispatch" ]; then echo "skip=false" >> "$GITHUB_OUTPUT" else echo "skip=true" >> "$GITHUB_OUTPUT" fi - name: Checkout if: steps.scope-check.outputs.skip != 'true' uses: actions/checkout@v6 with: submodules: false - name: Setup Node environment if: steps.scope-check.outputs.skip != 'true' && matrix.needs_node uses: ./.github/actions/setup-node-env with: install-bun: "false" use-sticky-disk: "false" - name: Setup Python if: steps.scope-check.outputs.skip != 'true' && matrix.needs_python uses: actions/setup-python@v6 with: python-version: "3.12" - name: Setup Java if: steps.scope-check.outputs.skip != 'true' && matrix.needs_java uses: actions/setup-java@v5 with: distribution: temurin java-version: "21" - name: Setup Swift build tools if: steps.scope-check.outputs.skip != 'true' && matrix.needs_swift_tools run: | sudo xcode-select -s /Applications/Xcode_26.1.app xcodebuild -version brew install xcodegen swiftlint swiftformat swift --version - name: Initialize CodeQL if: steps.scope-check.outputs.skip != 'true' uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} queries: security-and-quality config-file: ${{ matrix.config_file || '' }} - name: Autobuild if: steps.scope-check.outputs.skip != 'true' && matrix.needs_autobuild uses: github/codeql-action/autobuild@v4 - name: Build Android for CodeQL if: steps.scope-check.outputs.skip != 'true' && matrix.language == 'java-kotlin' working-directory: apps/android run: ./gradlew --no-daemon :app:assemblePlayDebug - name: Build Swift for CodeQL if: steps.scope-check.outputs.skip != 'true' && matrix.language == 'swift' run: | set -euo pipefail swift build --package-path apps/macos --configuration release cd apps/ios xcodegen generate xcodebuild build \ -project OpenClaw.xcodeproj \ -scheme OpenClaw \ -destination "generic/platform=iOS Simulator" \ CODE_SIGNING_ALLOWED=NO - name: Analyze if: steps.scope-check.outputs.skip != 'true' uses: github/codeql-action/analyze@v4 with: category: "/language:${{ matrix.language }}"