From 6900a4fe85fd89a68037fb9d38d1955390c7a2e1 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 22 Oct 2026 20:59:20 +0000 Subject: [PATCH] ci: gate fork followers on artifact presence, not actor SonarCloud githubactions:S8232 flags gating job execution on forgeable github.event.workflow_run context (actor.login). Gate on a fact of the completed run instead: a gate job checks whether the CI run produced its artifact (sonar-analysis-inputs / codecov-payload) via the Actions API, and the real job only runs through needs: when it exists. This covers any skipped CI run - including the ones the igel-devin-ai actor guard produces - rather than just that actor. --- .github/workflows/fork-coverage.yml | 32 ++++++++++++++++++++++------- .github/workflows/fork-sonar.yml | 30 ++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/.github/workflows/fork-coverage.yml b/.github/workflows/fork-coverage.yml index bd7c5cf9..c3fb2704 100644 --- a/.github/workflows/fork-coverage.yml +++ b/.github/workflows/fork-coverage.yml @@ -18,15 +18,33 @@ permissions: actions: read jobs: - codecov: - # Only for PRs that originate from a fork (same-repo PRs upload directly in - # the main CI workflow, which has secrets). Skipped for PR runs sent by - # igel-devin-ai (Devin): those are skipped in the CI build itself, so no - # coverage artifact exists to download. + # Gate job: a skipped CI run (e.g. pull_request runs sent by igel-devin-ai) + # concludes 'success' but produces no artifacts, so only run the upload job + # when the coverage payload actually exists. This checks a fact of the + # completed run instead of forgeable github.event.* context values. + coverage-payload: if: > github.event.workflow_run.event == 'pull_request' && - github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name && - github.event.workflow_run.actor.login != 'igel-devin-ai' + github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name + runs-on: ubuntu-latest + outputs: + present: ${{ steps.check.outputs.present }} + steps: + - name: Check for coverage payload in the CI run + id: check + env: + GH_TOKEN: ${{ github.token }} + run: | + present="$(curl -sS -H "Authorization: Bearer $GH_TOKEN" \ + "${{ github.api_url }}/repos/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}/artifacts?per_page=100" \ + | jq -r '.artifacts[] | select(.name == "codecov-payload" and (.expired | not)) | .name')" + echo "present=${present:+true}" >> "$GITHUB_OUTPUT" + + codecov: + needs: coverage-payload + # Only runs when the gate job found the artifact (a skipped needed job + # skips this one automatically). + if: needs.coverage-payload.outputs.present == 'true' runs-on: ubuntu-latest env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/fork-sonar.yml b/.github/workflows/fork-sonar.yml index 5aeaaaa9..7c74f290 100644 --- a/.github/workflows/fork-sonar.yml +++ b/.github/workflows/fork-sonar.yml @@ -33,14 +33,34 @@ concurrency: cancel-in-progress: true jobs: - sonar: - # Skipped for PR runs sent by igel-devin-ai (Devin): the CI build itself is - # skipped for that actor, so no analysis-inputs artifact exists to download. + # Gate job: a skipped CI run (e.g. pull_request runs sent by igel-devin-ai) + # concludes 'success' but produces no artifacts, so only run the analysis when + # the inputs actually exist. This checks a fact of the completed run instead + # of forgeable github.event.* context values. + analysis-inputs: if: > github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' && - github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name && - github.event.workflow_run.actor.login != 'igel-devin-ai' + github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name + runs-on: ubuntu-latest + outputs: + present: ${{ steps.check.outputs.present }} + steps: + - name: Check for analysis inputs in the CI run + id: check + env: + GH_TOKEN: ${{ github.token }} + run: | + present="$(curl -sS -H "Authorization: Bearer $GH_TOKEN" \ + "${{ github.api_url }}/repos/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}/artifacts?per_page=100" \ + | jq -r '.artifacts[] | select(.name == "sonar-analysis-inputs" and (.expired | not)) | .name')" + echo "present=${present:+true}" >> "$GITHUB_OUTPUT" + + sonar: + needs: analysis-inputs + # Only runs when the gate job found the artifact (a skipped needed job + # skips this one automatically). + if: needs.analysis-inputs.outputs.present == 'true' runs-on: ubuntu-latest timeout-minutes: 20 environment: fork-ci