diff --git a/tools/ci/check-code-scanning-tools-zero.sh b/tools/ci/check-code-scanning-tools-zero.sh index 032ddb7..a6f64a7 100755 --- a/tools/ci/check-code-scanning-tools-zero.sh +++ b/tools/ci/check-code-scanning-tools-zero.sh @@ -79,7 +79,8 @@ if [[ "${EVENT_NAME}" == "pull_request" && -n "${GITHUB_EVENT_PATH:-}" && -f "${ if [[ -n "${pr_number}" ]]; then files_json="${OUT_DIR}/pr-files.json" if gh api "repos/${REPO}/pulls/${pr_number}/files?per_page=100" --paginate > "${files_json}" 2>> "${RAW_LOG}"; then - if jq -r '.[].filename' "${files_json}" | grep -Eiq '^(\.qodana/|qodana\.ya?ml$|\.github/workflows/qodana\.yml$)'; then + if jq -r '.[].filename' "${files_json}" | grep -Eiq '^(\.qodana/|qodana\.ya?ml$|\.github/workflows/qodana\.yml$)' || \ + jq -r '.[].filename' "${files_json}" | grep -Eq '^(tools/ci/check-code-scanning-tools-zero\.sh$|tools/versioning/compute-pr-labels\.js$)'; then has_qodana_changes="true" fi else @@ -104,6 +105,117 @@ else log "INFO: Event=${EVENT_NAME:-} -> pruefe Code-Scanning Alerts fuer ref=${QUERY_REF}" fi +# Alert state can lag until the qodana workflow for the same SHA uploads SARIF. +# Wait fail-closed in both cases: +# - main push validation (ref=main), +# - PR Qodana-cleanup validation (ref=refs/pull//merge). +if [[ -n "${GITHUB_SHA:-}" ]]; then + should_wait_for_qodana="false" + if [[ "${EVENT_NAME}" == "push" && "${QUERY_REF}" == "refs/heads/main" ]]; then + should_wait_for_qodana="true" + fi + if [[ "${QUERY_REF}" != "refs/heads/main" ]]; then + should_wait_for_qodana="true" + fi + + if [[ "${should_wait_for_qodana}" == "true" ]]; then + wait_attempt=1 + # Qodana can be queued behind runner load; allow an overridable fail-closed window. + wait_max_attempts="${QODANA_WAIT_MAX_ATTEMPTS:-120}" + wait_delay="${QODANA_WAIT_DELAY_SECONDS:-10}" + total_wait_timeout=$((wait_max_attempts * wait_delay)) + run_event_filter="${EVENT_NAME:-push}" + pr_head_sha="" + pr_head_ref="" + if [[ "${EVENT_NAME}" == "pull_request" && -n "${GITHUB_EVENT_PATH:-}" && -f "${GITHUB_EVENT_PATH:-}" ]]; then + pr_head_sha="$(jq -r '.pull_request.head.sha // empty' "${GITHUB_EVENT_PATH}")" + pr_head_ref="$(jq -r '.pull_request.head.ref // empty' "${GITHUB_EVENT_PATH}")" + if [[ -n "${pr_head_sha}" ]]; then + run_event_filter="pull_request" + fi + fi + wait_started_epoch="$(date +%s)" + log "INFO: Warte auf qodana-Run (max_wait=${total_wait_timeout}s, attempts=${wait_max_attempts}, delay=${wait_delay}s, event_filter=${run_event_filter})" + while true; do + qodana_runs_json="${OUT_DIR}/qodana-runs.json" + api_path="repos/${REPO}/actions/runs?event=${run_event_filter}&per_page=100" + if [[ "${EVENT_NAME}" == "pull_request" && -n "${pr_head_sha}" ]]; then + api_path="${api_path}&head_sha=${pr_head_sha}" + elif [[ "${EVENT_NAME}" != "pull_request" ]]; then + api_path="${api_path}&head_sha=${GITHUB_SHA}" + fi + if ! gh api "${api_path}" > "${qodana_runs_json}" 2>> "${RAW_LOG}"; then + if (( wait_attempt >= wait_max_attempts )); then + fail "Qodana-Runstatus fuer aktuellen SHA konnte nicht geladen werden" + fi + log "WARN: Qodana-Runstatus API-Fehler, retry ${wait_attempt}/${wait_max_attempts}" + sleep "${wait_delay}" + wait_attempt=$((wait_attempt + 1)) + continue + fi + + if [[ "${EVENT_NAME}" == "pull_request" ]]; then + qodana_fields="$(jq -r \ + --arg pr_head_sha "${pr_head_sha}" \ + --arg pr_head_ref "${pr_head_ref}" \ + '.workflow_runs + | map(select(.name=="qodana")) + | (if $pr_head_sha != "" then + map(select(.head_sha == $pr_head_sha)) + elif $pr_head_ref != "" then + map(select(.head_branch == $pr_head_ref)) + else + . + end) + | sort_by(.created_at) + | reverse + | .[0] + | [(.status // ""), (.conclusion // ""), (.html_url // "")] + | @tsv' "${qodana_runs_json}")" + else + qodana_fields="$(jq -r \ + '.workflow_runs + | map(select(.name=="qodana")) + | sort_by(.created_at) + | reverse + | .[0] + | [(.status // ""), (.conclusion // ""), (.html_url // "")] + | @tsv' "${qodana_runs_json}")" + fi + IFS=$'\t' read -r qodana_status qodana_conclusion qodana_url <<< "${qodana_fields:-}" + + if [[ -z "${qodana_status}" ]]; then + if (( wait_attempt >= wait_max_attempts )); then + waited_seconds=$(( $(date +%s) - wait_started_epoch )) + fail "Kein qodana-Run fuer aktuellen Kontext gefunden (sha=${GITHUB_SHA}, pr_head_sha=${pr_head_sha:-n/a}, pr_head_ref=${pr_head_ref:-n/a}, waited=${waited_seconds}s)" + fi + log "INFO: qodana-Run noch nicht sichtbar (retry ${wait_attempt}/${wait_max_attempts})" + sleep "${wait_delay}" + wait_attempt=$((wait_attempt + 1)) + continue + fi + + if [[ "${qodana_status}" != "completed" ]]; then + if (( wait_attempt >= wait_max_attempts )); then + waited_seconds=$(( $(date +%s) - wait_started_epoch )) + fail "qodana-Run fuer SHA=${GITHUB_SHA} ist nicht abgeschlossen (status=${qodana_status}, waited=${waited_seconds}s)" + fi + log "INFO: warte auf qodana-Runabschluss (status=${qodana_status}, retry ${wait_attempt}/${wait_max_attempts})" + sleep "${wait_delay}" + wait_attempt=$((wait_attempt + 1)) + continue + fi + + if [[ "${qodana_conclusion}" != "success" ]]; then + fail "qodana-Run fuer SHA=${GITHUB_SHA} ist fehlgeschlagen (conclusion=${qodana_conclusion:-unknown}, url=${qodana_url:-n/a})" + fi + + log "INFO: qodana-Run fuer SHA=${GITHUB_SHA} erfolgreich abgeschlossen (${qodana_url:-n/a})" + break + done + fi +fi + attempt=1 delay=2 max_attempts=3 diff --git a/tools/versioning/compute-pr-labels.js b/tools/versioning/compute-pr-labels.js index ac81ea6..b088a5e 100644 --- a/tools/versioning/compute-pr-labels.js +++ b/tools/versioning/compute-pr-labels.js @@ -21,6 +21,8 @@ const AREA_RULES = [ { prefix: '.qodana/', label: 'area:qodana' }, { exact: 'qodana.yaml', label: 'area:qodana' }, { exact: '.github/workflows/qodana.yml', label: 'area:qodana' }, + { exact: 'tools/ci/check-code-scanning-tools-zero.sh', label: 'area:qodana' }, + { exact: 'tools/versioning/compute-pr-labels.js', label: 'area:qodana' }, { prefix: 'src/FileTypeDetection/Infrastructure/Archive', label: 'area:archive' }, { prefix: 'src/FileTypeDetection/ArchiveProcessing', label: 'area:archive' }, { exact: 'src/FileTypeDetection/EvidenceHashing.vb', label: 'area:hashing' },