Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 82 additions & 57 deletions .github/workflows/codeql-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
# runner, then one coordinator POSTs repository_dispatch to
# codeql-scan-dispatch.yml (native, unrestricted, in
# ContextualWisdomLab/.github) with the remaining language matrix. The
# handler publishes codeql-dispatch/<language> and reruns only that exact
# failed job. On rerun the shard reads the terminal status once. Design:
# handler may publish codeql-dispatch/<language> for observability, but
# required verdict admission is bound to the exact completed dispatch run
# (repository, PR, head, live base, required run, and language job). Design:
# docs/adr/0025-codeql-required-workflow-dispatch-architecture.md. The
# merge-preview scan (analyze-merge) is required nowhere (PR #1766) and was
# dropped, not migrated.
Expand Down Expand Up @@ -157,10 +158,11 @@ jobs:
matrix: ${{ fromJSON(needs.detect-languages.outputs.matrix) }}
steps:
- name: Read current-head CodeQL dispatch verdict
# Shards never dispatch. They re-check the live head, consume an
# authenticated codeql-dispatch/<language> verdict when one exists,
# and otherwise fail pending so the runner is released. One
# coordinator job POSTs the remaining language matrix after every
# Shards never dispatch. They re-check the live head/base and consume
# only an exact completed dispatch-run language job. Commit statuses
# published by the handler are observability only: a same-head base
# retarget must never inherit an old-base status as required evidence.
# One coordinator POSTs the remaining language matrix after every
# shard has a job id.
id: dispatch
if: needs.detect-languages.outputs.code == 'true'
Expand All @@ -176,6 +178,7 @@ jobs:
set -euo pipefail
live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"
live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')"
live_base_ref="$(printf '%s' "$live_pr" | jq -r '.base.ref // empty')"
live_base="$(printf '%s' "$live_pr" | jq -r '.base.sha // empty')"
live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')"
if [ -z "$live_head" ] || [ -z "$live_state" ]; then
Expand All @@ -190,7 +193,7 @@ jobs:
echo "Pull request head moved on the live open PR; a fresh dispatch will fire for the current head."
exit 0
fi
if ! [[ "$live_base" =~ ^[0-9a-fA-F]{40}$ ]]; then
if [ -z "$live_base_ref" ] || ! [[ "$live_base" =~ ^[0-9a-fA-F]{40}$ ]]; then
echo "::error::Could not validate live pull request base SHA before CodeQL verdict read."
exit 1
fi
Expand All @@ -199,41 +202,26 @@ jobs:
exit 1
fi

statuses="$(gh api "repos/${TARGET_REPOSITORY}/commits/${PR_HEAD_SHA}/statuses")"
verdict_state="$(printf '%s' "$statuses" | jq -r --arg ctx "codeql-dispatch/${LANGUAGE}" '
[
.[]
| select(.context == $ctx)
| select(
(.creator.login // "" | ascii_downcase) as $creator
| $creator == "opencode-agent" or $creator == "opencode-agent[bot]"
)
]
| first // {} | .state // empty
')"
case "$verdict_state" in
success|failure|error)
echo "verdict=${verdict_state}" >>"$GITHUB_OUTPUT"
echo "Found authenticated current-head CodeQL verdict for ${LANGUAGE}: ${verdict_state}."
exit 0
;;
esac

expected_title="CodeQL Scan Dispatch ${TARGET_REPOSITORY}#${PR_NUMBER}@${PR_HEAD_SHA}/${live_base}/${REQUIRED_RUN_ID}"
expected_title="CodeQL Scan Dispatch ${TARGET_REPOSITORY}#${PR_NUMBER}@${PR_HEAD_SHA}/${live_base_ref}@${live_base}/${REQUIRED_RUN_ID}"
expected_job="CodeQL dispatch scan (${LANGUAGE})"
runs_json="$(gh api --paginate --slurp "repos/ContextualWisdomLab/.github/actions/workflows/codeql-scan-dispatch.yml/runs")"
run_id="$(printf '%s' "$runs_json" | jq -r --arg title "$expected_title" --arg path ".github/workflows/codeql-scan-dispatch.yml" '
run_ids="$(printf '%s' "$runs_json" | jq -r --arg title "$expected_title" --arg path ".github/workflows/codeql-scan-dispatch.yml" '
[
.[] | .workflow_runs[]
| select(.path == $path)
| select(.event == "repository_dispatch")
| select(.status == "completed")
| select(.display_title == $title or .name == $title)
| .id
]
| first
| .id // empty
| .[]
')"
if [[ "$run_id" =~ ^[1-9][0-9]*$ ]]; then
while IFS= read -r run_id; do
[ -n "$run_id" ] || continue
if ! [[ "$run_id" =~ ^[1-9][0-9]*$ ]]; then
echo "::error::Exact CodeQL dispatch lookup returned a malformed run id."
exit 1
fi
jobs_json="$(gh api --paginate --slurp "repos/ContextualWisdomLab/.github/actions/runs/${run_id}/jobs")"
job_conclusion="$(printf '%s' "$jobs_json" | jq -r --arg name "$expected_job" '
[.[] | .jobs[] | select(.name == $name)]
Expand All @@ -242,14 +230,14 @@ jobs:
case "$job_conclusion" in
success|failure)
echo "verdict=${job_conclusion}" >>"$GITHUB_OUTPUT"
echo "Found completed CodeQL dispatch scan job for ${LANGUAGE}: ${job_conclusion}."
echo "Found exact completed CodeQL dispatch scan job for ${LANGUAGE}: ${job_conclusion} (run_id=${run_id})."
exit 0
;;
esac
fi
done <<<"$run_ids"

if [ "$RUN_ATTEMPT" != "1" ]; then
echo "::error::Exact CodeQL job was rerun without an authenticated terminal verdict."
echo "::error::Exact CodeQL job was rerun without an exact terminal dispatch verdict."
exit 1
fi
echo "verdict=pending" >>"$GITHUB_OUTPUT"
Expand Down Expand Up @@ -279,7 +267,7 @@ jobs:
exit 1
;;
*)
echo "::error::CodeQL shard has no authenticated current-head verdict or dispatch receipt."
echo "::error::CodeQL shard has no exact current-head/base/run verdict or dispatch receipt."
exit 1
;;
esac
Expand Down Expand Up @@ -370,33 +358,54 @@ jobs:
)"
done < <(printf '%s' "$include_json" | jq -c '.[]')

statuses="$(gh api "repos/${TARGET_REPOSITORY}/commits/${PR_HEAD_SHA}/statuses")"
expected_title="CodeQL Scan Dispatch ${TARGET_REPOSITORY}#${PR_NUMBER}@${live_head}/${live_base_ref}@${live_base}/${REQUIRED_RUN_ID}"
runs_json="$(gh api --paginate --slurp "repos/ContextualWisdomLab/.github/actions/workflows/codeql-scan-dispatch.yml/runs?per_page=100")"
completed_run_ids="$(printf '%s' "$runs_json" | jq -r --arg title "$expected_title" --arg path ".github/workflows/codeql-scan-dispatch.yml" '
[
.[] | .workflow_runs[]
| select(.path == $path)
| select(.event == "repository_dispatch")
| select(.status == "completed")
| select(.display_title == $title or .name == $title)
| .id
]
| .[]
')"

pending_matrix='[]'
while IFS= read -r entry; do
language="$(printf '%s' "$entry" | jq -r '.language // empty')"
verdict_state="$(printf '%s' "$statuses" | jq -r --arg ctx "codeql-dispatch/${language}" '
[
.[]
| select(.context == $ctx)
| select(
(.creator.login // "" | ascii_downcase) as $creator
| $creator == "opencode-agent" or $creator == "opencode-agent[bot]"
)
]
| first // {} | .state // empty
')"
case "$verdict_state" in
success|failure|error)
echo "Found authenticated current-head CodeQL verdict for ${language}: ${verdict_state}."
;;
*)
pending_matrix="$(jq -c --argjson entry "$entry" '. + [$entry]' <<<"$pending_matrix")"
;;
esac
expected_job="CodeQL dispatch scan (${language})"
terminal_conclusion=""
terminal_run_id=""
while IFS= read -r run_id; do
[ -n "$run_id" ] || continue
if ! [[ "$run_id" =~ ^[1-9][0-9]*$ ]]; then
echo "::error::Exact CodeQL dispatch lookup returned a malformed run id."
exit 1
fi
dispatch_jobs_json="$(gh api --paginate --slurp "repos/ContextualWisdomLab/.github/actions/runs/${run_id}/jobs")"
job_conclusion="$(printf '%s' "$dispatch_jobs_json" | jq -r --arg name "$expected_job" '
[.[] | .jobs[] | select(.name == $name)]
| if length == 1 then .[0].conclusion else empty end
')"
case "$job_conclusion" in
success|failure)
terminal_conclusion="$job_conclusion"
terminal_run_id="$run_id"
break
;;
esac
done <<<"$completed_run_ids"
if [ -n "$terminal_conclusion" ]; then
echo "Found exact terminal CodeQL dispatch job for ${language}: ${terminal_conclusion} (run_id=${terminal_run_id})."
else
pending_matrix="$(jq -c --argjson entry "$entry" '. + [$entry]' <<<"$pending_matrix")"
fi
done < <(printf '%s' "$include_json" | jq -c '.[]')

if [ "$(printf '%s' "$pending_matrix" | jq 'length')" -eq 0 ]; then
echo "All detected CodeQL languages already have authenticated terminal verdicts; skipping dispatch."
echo "All detected CodeQL languages already have exact terminal dispatch verdicts; skipping dispatch."
exit 0
fi

Expand All @@ -411,6 +420,22 @@ jobs:
exit 1
fi

active_run_id="$(printf '%s' "$runs_json" | jq -r --arg title "$expected_title" --arg path ".github/workflows/codeql-scan-dispatch.yml" '
[
.[] | .workflow_runs[]
| select(.path == $path)
| select(.event == "repository_dispatch")
| select(.status == "queued" or .status == "in_progress" or .status == "waiting" or .status == "requested" or .status == "pending")
| select(.display_title == $title or .name == $title)
]
| first
| .id // empty
')"
if [[ "$active_run_id" =~ ^[1-9][0-9]*$ ]]; then
echo "Identical CodeQL dispatch is already active (run_id=${active_run_id}); preserving it."
exit 0
fi

if [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then
echo "::error::CodeQL scan dispatch requires GitHub OIDC."
exit 1
Expand Down
108 changes: 73 additions & 35 deletions .github/workflows/codeql-scan-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ run-name: >-
github.repository }}#${{
github.event.client_payload.pr_number || 'event' }}@${{
github.event.client_payload.pr_head_sha || github.sha }}/${{
github.event.client_payload.pr_base_ref || 'none' }}@${{
github.event.client_payload.pr_base_sha || 'none' }}/${{
github.event.client_payload.required_run_id || github.run_id }}

Expand Down Expand Up @@ -513,72 +514,109 @@ jobs:
echo "::error::Could not publish the CodeQL dispatch status after all configured credentials failed; the exact required job will remain failed and will not be woken with stale or missing evidence."
exit 1

- name: Wake exact CodeQL required job
if: >-
always()
&& steps.publish_status.outcome == 'success'
&& needs.validate-dispatch.outputs.target_repository != ''
&& needs.validate-dispatch.outputs.pr_number != ''
&& needs.validate-dispatch.outputs.head_sha != ''
&& needs.validate-dispatch.outputs.required_run_id != ''
&& needs.validate-dispatch.outputs.required_jobs != ''
wake-required-codeql:
name: Wake exact CodeQL required run
needs: [validate-dispatch, scan]
if: >-
always()
&& needs.validate-dispatch.result == 'success'
&& needs.scan.result != 'cancelled'
&& needs.validate-dispatch.outputs.target_repository != ''
&& needs.validate-dispatch.outputs.pr_number != ''
&& needs.validate-dispatch.outputs.base_ref != ''
&& needs.validate-dispatch.outputs.base_sha != ''
&& needs.validate-dispatch.outputs.head_sha != ''
&& needs.validate-dispatch.outputs.required_run_id != ''
&& needs.validate-dispatch.outputs.required_jobs != ''
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
actions: write
contents: read
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1
with:
egress-policy: audit

- name: Wake exact CodeQL required run
env:
GH_TOKEN: ${{ needs.validate-dispatch.outputs.target_repository == github.repository && github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }}
TARGET_REPOSITORY: ${{ needs.validate-dispatch.outputs.target_repository }}
PR_NUMBER: ${{ needs.validate-dispatch.outputs.pr_number }}
BASE_REF: ${{ needs.validate-dispatch.outputs.base_ref }}
BASE_SHA: ${{ needs.validate-dispatch.outputs.base_sha }}
HEAD_SHA: ${{ needs.validate-dispatch.outputs.head_sha }}
REQUIRED_RUN_ID: ${{ needs.validate-dispatch.outputs.required_run_id }}
REQUIRED_JOBS: ${{ needs.validate-dispatch.outputs.required_jobs }}
REQUIRED_LANGUAGE: ${{ matrix.language }}
WAKE_TOKEN_SOURCE: ${{ needs.validate-dispatch.outputs.target_repository == github.repository && 'github-token' || secrets.PR_REVIEW_MERGE_TOKEN != '' && 'PR_REVIEW_MERGE_TOKEN' || secrets.OPENCODE_APPROVE_TOKEN != '' && 'OPENCODE_APPROVE_TOKEN' || 'unavailable' }}
run: |
set -euo pipefail
if [ -z "${GH_TOKEN:-}" ] || [ "$WAKE_TOKEN_SOURCE" = "unavailable" ]; then
echo "::error::Actions-capable CodeQL wake credential is unavailable."
exit 1
fi
REQUIRED_JOB_ID="$(printf '%s' "$REQUIRED_JOBS" | jq -r --arg lang "$REQUIRED_LANGUAGE" '
[.[] | select(.language == $lang) | .job_id | tostring]
| if length == 1 and (.[0] | test("^[1-9][0-9]*$")) then .[0] else empty end
')"
if ! [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] ||
! [[ "$REQUIRED_JOB_ID" =~ ^[1-9][0-9]*$ ]] ||
! [[ "$REQUIRED_LANGUAGE" =~ ^[a-z0-9-]+$ ]]; then
if [ -z "$BASE_REF" ] ||
! [[ "$BASE_SHA" =~ ^[0-9a-fA-F]{40}$ ]] ||
! [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] ||
! printf '%s' "$REQUIRED_JOBS" | jq -e '
type == "array" and length > 0
and all(.[]; (.language | type == "string" and test("^[a-z0-9-]+$"))
and (.job_id | type == "number" and . > 0 and floor == .))
and (([.[].language] | length) == ([.[].language] | unique | length))
and (([.[].job_id] | length) == ([.[].job_id] | unique | length))
' >/dev/null; then
echo "::error::CodeQL wake identity is non-canonical."
exit 1
fi

pull="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"
live_state="$(printf '%s' "$pull" | jq -r '.state // empty')"
live_base_ref="$(printf '%s' "$pull" | jq -r '.base.ref // empty')"
live_base="$(printf '%s' "$pull" | jq -r '.base.sha // empty')"
live_head="$(printf '%s' "$pull" | jq -r '.head.sha // empty')"
if [ "$live_state" != "open" ] || [ "$live_head" != "$HEAD_SHA" ]; then
echo "::error::CodeQL wake rejected a closed PR or stale head."
if [ "$live_state" != "open" ] ||
[ "$live_base_ref" != "$BASE_REF" ] ||
[ "$live_base" != "$BASE_SHA" ] ||
Comment thread
coderabbitai[bot] marked this conversation as resolved.
[ "$live_head" != "$HEAD_SHA" ]; then
echo "::error::CodeQL wake rejected a closed PR or stale base/head identity."
exit 1
fi

run="$(gh api "repos/${TARGET_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"
run_identity="$(printf '%s' "$run" | jq -r --arg head "$HEAD_SHA" --argjson run_id "$REQUIRED_RUN_ID" '
run_identity="$(printf '%s' "$run" | jq -r --arg base_ref "$BASE_REF" --arg base "$BASE_SHA" --arg head "$HEAD_SHA" --argjson pr_number "$PR_NUMBER" --argjson run_id "$REQUIRED_RUN_ID" '
select(.id == $run_id)
| select(.event == "pull_request")
| select(.path == ".github/workflows/codeql-pr.yml")
| select(.head_sha == $head)
| select(.status == "completed")
Comment thread
coderabbitai[bot] marked this conversation as resolved.
| select([
.pull_requests[]?
| select(.number == $pr_number and .head.sha == $head and .base.ref == $base_ref and .base.sha == $base)
] | length == 1)
| .id // empty
')"
expected_name="CodeQL compatibility analysis (${REQUIRED_LANGUAGE})"
job="$(gh api "repos/${TARGET_REPOSITORY}/actions/jobs/${REQUIRED_JOB_ID}")"
job_identity="$(printf '%s' "$job" | jq -r --arg head "$HEAD_SHA" --arg name "$expected_name" --argjson run_id "$REQUIRED_RUN_ID" --argjson job_id "$REQUIRED_JOB_ID" '
select(.id == $job_id)
| select(.run_id == $run_id)
| select(.head_sha == $head)
| select(.name == $name)
| select(.status == "completed" and .conclusion == "failure")
| .id // empty
')"
if [ "$run_identity" != "$REQUIRED_RUN_ID" ] ||
[ "$job_identity" != "$REQUIRED_JOB_ID" ]; then
echo "::error::CodeQL wake rejected missing or ambiguous exact run/job identity."
if [ "$run_identity" != "$REQUIRED_RUN_ID" ]; then
echo "::error::CodeQL wake rejected missing or ambiguous exact run/base identity."
exit 1
fi

gh api -X POST "repos/${TARGET_REPOSITORY}/actions/jobs/${REQUIRED_JOB_ID}/rerun" >/dev/null
echo "Re-ran exact failed CodeQL job ${REQUIRED_JOB_ID} for ${REQUIRED_LANGUAGE} on ${HEAD_SHA}."
while IFS= read -r required_job; do
required_language="$(jq -r '.language' <<<"$required_job")"
required_job_id="$(jq -r '.job_id | tostring' <<<"$required_job")"
expected_name="CodeQL compatibility analysis (${required_language})"
job="$(gh api "repos/${TARGET_REPOSITORY}/actions/jobs/${required_job_id}")"
job_identity="$(printf '%s' "$job" | jq -r --arg head "$HEAD_SHA" --arg name "$expected_name" --argjson run_id "$REQUIRED_RUN_ID" --argjson job_id "$required_job_id" '
select(.id == $job_id and .run_id == $run_id and .head_sha == $head)
| select(.name == $name)
| select(.status == "completed" and .conclusion == "failure")
| .id // empty
')"
if [ "$job_identity" != "$required_job_id" ]; then
echo "::error::CodeQL wake rejected missing or ambiguous required job identity."
exit 1
fi
done < <(printf '%s' "$REQUIRED_JOBS" | jq -c '.[]')

gh api -X POST "repos/${TARGET_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}/rerun-failed-jobs" >/dev/null
echo "Re-ran failed jobs in exact CodeQL run ${REQUIRED_RUN_ID} for ${HEAD_SHA} on base ${BASE_REF}@${BASE_SHA} after all dispatch shards completed."
Loading
Loading