diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 82d3e271..2fc10a90 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -48,6 +48,9 @@ jobs: integrations-llamaindex: ${{ steps.filter.outputs.integrations-llamaindex }} dev: ${{ steps.filter.outputs.dev }} ci: ${{ steps.filter.outputs.ci }} + # Secrets are available for internal PRs, pull_request_target, and workflow_dispatch. + # Fork PRs via pull_request event do NOT have access to secrets. + has_secrets: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} steps: - uses: actions/checkout@v6 with: @@ -446,11 +449,12 @@ jobs: test-rust-cli: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.cli == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest env: HINDSIGHT_API_LLM_PROVIDER: vertexai @@ -597,12 +601,13 @@ jobs: build-docker-images: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.docker == 'true' || needs.detect-changes.outputs.control-plane == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') name: Build Docker (${{ matrix.name }}) runs-on: ubuntu-latest strategy: @@ -690,10 +695,11 @@ jobs: test-api: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest env: HINDSIGHT_API_LLM_PROVIDER: vertexai @@ -766,11 +772,12 @@ jobs: test-python-client: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.clients-python == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest env: HINDSIGHT_API_LLM_PROVIDER: vertexai @@ -877,11 +884,12 @@ jobs: test-typescript-client: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.clients-ts == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest env: HINDSIGHT_API_LLM_PROVIDER: vertexai @@ -993,11 +1001,12 @@ jobs: test-typescript-client-deno: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.clients-ts == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest env: HINDSIGHT_API_LLM_PROVIDER: vertexai @@ -1147,11 +1156,12 @@ jobs: test-rust-client: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.clients-rust == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest env: HINDSIGHT_API_LLM_PROVIDER: vertexai @@ -1262,11 +1272,12 @@ jobs: test-go-client: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.clients-go == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest env: HINDSIGHT_API_LLM_PROVIDER: vertexai @@ -1375,12 +1386,13 @@ jobs: test-openclaw-integration: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.integrations-openclaw == 'true' || needs.detect-changes.outputs.embed == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest env: HINDSIGHT_API_LLM_PROVIDER: vertexai @@ -1492,11 +1504,12 @@ jobs: test-integration: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.integration-tests == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest env: HINDSIGHT_API_LLM_PROVIDER: vertexai @@ -1820,10 +1833,11 @@ jobs: test-pip-slim: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest env: HINDSIGHT_API_LLM_PROVIDER: vertexai @@ -1888,11 +1902,12 @@ jobs: test-embed: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.embed == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest env: HINDSIGHT_API_LLM_PROVIDER: vertexai @@ -1950,11 +1965,12 @@ jobs: test-hindsight-all: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.hindsight-all == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest env: HINDSIGHT_API_LLM_PROVIDER: vertexai @@ -2034,7 +2050,8 @@ jobs: test-doc-examples: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.clients-ts == 'true' || @@ -2042,7 +2059,7 @@ jobs: needs.detect-changes.outputs.clients-go == 'true' || needs.detect-changes.outputs.cli == 'true' || needs.detect-changes.outputs.docs == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest strategy: fail-fast: false @@ -2186,11 +2203,12 @@ jobs: test-upgrade: needs: [detect-changes] if: >- - (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || + needs.detect-changes.outputs.has_secrets == 'true' && + ((github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test') || github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.dev == 'true' || - needs.detect-changes.outputs.ci == 'true' + needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest env: HINDSIGHT_API_LLM_PROVIDER: vertexai @@ -2394,4 +2412,131 @@ jobs: # Check compatibility using our tool cd hindsight-dev - uv run check-openapi-compatibility /tmp/old-openapi.json ../hindsight-docs/static/openapi.json \ No newline at end of file + uv run check-openapi-compatibility /tmp/old-openapi.json ../hindsight-docs/static/openapi.json + + # Report CI status back to the PR for pull_request_target events. + # GitHub does not automatically link pull_request_target check runs to the PR, + # so we create a commit status on the PR head SHA explicitly. + report-pr-status: + if: github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-test' && always() + needs: + - detect-changes + - build-api-python-versions + - build-typescript-client + - build-openclaw-integration + - test-claude-code-integration + - test-codex-integration + - build-ai-sdk-integration + - test-ai-sdk-integration-deno + - build-chat-integration + - build-control-plane + - build-docs + - test-rust-cli + - lint-helm-chart + - build-docker-images + - test-api + - test-python-client + - test-typescript-client + - test-typescript-client-deno + - build-rust-cli-arm64 + - test-rust-client + - test-go-client + - test-openclaw-integration + - test-integration + - test-ag2-integration + - test-crewai-integration + - test-litellm-integration + - test-pydantic-ai-integration + - test-hermes-integration + - test-llamaindex-integration + - test-pip-slim + - test-embed + - test-hindsight-all + - test-doc-examples + - test-upgrade + - verify-generated-files + - check-openapi-compatibility + runs-on: ubuntu-latest + permissions: + statuses: write + pull-requests: write + steps: + - name: Determine overall result + id: result + uses: actions/github-script@v7 + with: + script: | + const needs = ${{ toJSON(needs) }}; + const entries = Object.entries(needs); + const failed = entries.filter(([, v]) => v.result === 'failure'); + const skipped = entries.filter(([, v]) => v.result === 'skipped'); + const succeeded = entries.filter(([, v]) => v.result === 'success'); + const overall = failed.length > 0 ? 'failure' : 'success'; + const icon = overall === 'success' ? '✅' : '❌'; + const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + + let body = `## ${icon} safe-to-test CI results\n\n`; + body += `| Status | Count |\n|--------|-------|\n`; + body += `| ✅ Passed | ${succeeded.length} |\n`; + if (failed.length > 0) body += `| ❌ Failed | ${failed.length} |\n`; + if (skipped.length > 0) body += `| ⏭️ Skipped | ${skipped.length} |\n`; + + if (failed.length > 0) { + body += `\n### Failed jobs\n`; + for (const [name] of failed) { + body += `- \`${name}\`\n`; + } + } + + body += `\n[View full run](${runUrl})`; + + core.setOutput('state', overall); + core.setOutput('description', failed.length > 0 ? 'Some CI checks failed' : 'All CI checks passed'); + core.setOutput('body', body); + core.setOutput('run_url', runUrl); + + - name: Report status to PR + uses: actions/github-script@v7 + with: + script: | + await github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: context.payload.pull_request.head.sha, + state: '${{ steps.result.outputs.state }}', + context: 'CI / safe-to-test', + description: '${{ steps.result.outputs.description }}', + target_url: '${{ steps.result.outputs.run_url }}' + }); + + - name: Comment on PR + uses: actions/github-script@v7 + with: + script: | + const prNumber = context.payload.pull_request.number; + const marker = ''; + const body = `${marker}\n${{ steps.result.outputs.body }}`; + + // Update existing comment if present, otherwise create new one + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + }); + const existing = comments.find(c => c.body.includes(marker)); + + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body, + }); + } \ No newline at end of file