From 74205dd3fb86ae70708d81a0388bc6cd9d7ab9ae Mon Sep 17 00:00:00 2001 From: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com> Date: Wed, 19 Aug 2026 07:28:51 +0000 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20implement=20issue=20#439=20?= =?UTF-8?q?=E2=80=94=20[Fleet=20Monitor]=20petry-projects/TalkTerm=20?= =?UTF-8?q?=E2=80=94=20.github/workflows/sonarcloud.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 1 + scripts/test-sonarcloud-workflow.sh | 112 +++++++++++++ scripts/test-sonarcloud-workflow.test.sh | 198 +++++++++++++++++++++++ 3 files changed, 311 insertions(+) create mode 100755 scripts/test-sonarcloud-workflow.sh create mode 100644 scripts/test-sonarcloud-workflow.test.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 236d1503..75c91c45 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,3 +102,4 @@ jobs: bash scripts/test-pr-review-workflow.sh bash scripts/test-ci-workflow.sh bash scripts/test-copilot-setup-steps-workflow.sh + bash scripts/test-sonarcloud-workflow.sh diff --git a/scripts/test-sonarcloud-workflow.sh b/scripts/test-sonarcloud-workflow.sh new file mode 100755 index 00000000..fef6b3ae --- /dev/null +++ b/scripts/test-sonarcloud-workflow.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# Regression guard for sonarcloud.yml. Locks the flaky-scan resilience invariant +# whose loss inflates the Fleet Monitor failure rate (#439): +# +# The SonarCloud analysis endpoint is occasionally flaky, so a single transient +# blip must not fail the job. sonarcloud.yml mitigates this — per its own header +# convention — by running the scan with `continue-on-error: true` and following +# it with a retry step guarded on the first scan's `outcome == 'failure'`. If +# that pair silently drifts away (someone deletes the retry, or drops +# continue-on-error), every transient blip becomes a hard failure again and the +# workflow degrades. This guard fails the build the moment that happens. +# +# It also asserts every third-party action stays SHA-pinned (never a mutable tag), +# matching the workflow's own "SHA-pin every third-party action" convention. +# +# Accepts an optional workflow path (default: .github/workflows/sonarcloud.yml) so +# the checks can be exercised against fixtures — see test-sonarcloud-workflow.test.sh. +# Run: bash scripts/test-sonarcloud-workflow.sh +set -euo pipefail + +WORKFLOW="${1:-.github/workflows/sonarcloud.yml}" +SCAN_ACTION="SonarSource/sonarqube-scan-action" +PASS=true + +echo "=== test-sonarcloud-workflow ===" + +# ── Check 0: yq is available ─────────────────────────────────────────────── +if ! command -v yq &> /dev/null; then + echo "FAIL: 'yq' is required to parse YAML safely but was not found." + exit 1 +fi +echo "PASS: 'yq' is available" + +# ── Check 1: file exists ─────────────────────────────────────────────────── +if [[ ! -f "$WORKFLOW" ]]; then + echo "FAIL: $WORKFLOW not found" + exit 1 +fi +echo "PASS: $WORKFLOW exists" + +# ── Check 2: the primary scan runs with continue-on-error: true ──────────── +# The primary scan is the SonarSource scan step marked continue-on-error so a +# transient endpoint blip is swallowed rather than failing the job outright. +if ! primary_ce=$(yq " + [ .jobs[].steps[] + | select((.uses // \"\") | test(\"^${SCAN_ACTION}@\")) + | select(.[\"continue-on-error\"] == true) ] | length +" "$WORKFLOW" 2>/dev/null); then + echo "FAIL: yq failed to parse $WORKFLOW. Please check if it is valid YAML." + exit 1 +fi +if [[ "$primary_ce" -lt 1 ]]; then + echo "FAIL: no '${SCAN_ACTION}' scan step with 'continue-on-error: true' in $WORKFLOW" + echo " The first scan must be continue-on-error so a flaky endpoint blip" + echo " does not fail the job before the retry runs." + PASS=false +else + echo "PASS: primary scan step is 'continue-on-error: true' in $WORKFLOW" +fi + +# ── Check 3: a retry scan step is guarded on the primary's failure ───────── +# The retry re-runs the scan only when the primary reported failure, expressed as +# `steps..outcome == 'failure'` in its `if:` condition. This is what turns a +# single flaky blip into a self-healing run instead of a red build. +if ! retry_guards=$(yq " + [ .jobs[].steps[] + | select((.uses // \"\") | test(\"^${SCAN_ACTION}@\")) + | select((.[\"continue-on-error\"] // false) != true) + | select((.if // \"\") | test(\"outcome *== *'failure'\")) ] | length +" "$WORKFLOW" 2>/dev/null); then + echo "FAIL: yq failed to parse $WORKFLOW. Please check if it is valid YAML." + exit 1 +fi +if [[ "$retry_guards" -lt 1 ]]; then + echo "FAIL: no '${SCAN_ACTION}' retry step guarded on \"outcome == 'failure'\" in $WORKFLOW" + echo " Add a second scan step with:" + echo " if: ... && steps..outcome == 'failure'" + echo " so a transient first-scan failure is retried instead of failing CI." + PASS=false +else + echo "PASS: failure-guarded retry scan step present in $WORKFLOW" +fi + +# ── Check 4: every third-party action is SHA-pinned ──────────────────────── +# A mutable tag (e.g. @v8.2.1) lets an upstream change alter behavior without a +# commit here — a supply-chain and flakiness risk. Require a 40-hex commit SHA. +if ! mapfile -t uses < <(yq '.jobs[].steps[] | .uses // "" ' "$WORKFLOW" 2>/dev/null); then + echo "FAIL: yq failed to parse $WORKFLOW. Please check if it is valid YAML." + exit 1 +fi +unpinned="" +for ref in "${uses[@]}"; do + [[ -z "$ref" || "$ref" == "null" ]] && continue + if [[ ! "$ref" =~ @[0-9a-f]{40}$ ]]; then + unpinned+=" - ${ref}"$'\n' + fi +done +if [[ -n "$unpinned" ]]; then + echo "FAIL: unpinned action(s) in $WORKFLOW (SHA-pin every third-party action):" + printf '%s' "$unpinned" + PASS=false +else + echo "PASS: all third-party actions are SHA-pinned in $WORKFLOW" +fi + +echo "" +if [[ "$PASS" == "true" ]]; then + echo "All checks passed." +else + echo "One or more checks failed." + exit 1 +fi diff --git a/scripts/test-sonarcloud-workflow.test.sh b/scripts/test-sonarcloud-workflow.test.sh new file mode 100644 index 00000000..8227011d --- /dev/null +++ b/scripts/test-sonarcloud-workflow.test.sh @@ -0,0 +1,198 @@ +#!/usr/bin/env bash +# test-sonarcloud-workflow.test.sh — portable tests for the sonarcloud.yml +# regression guard (scripts/test-sonarcloud-workflow.sh). Verifies the guard +# enforces the flaky-scan resilience invariants that keep the SonarCloud +# workflow's Fleet Monitor failure rate low (#439): the primary scan step must +# be `continue-on-error: true` and be followed by a retry step guarded on that +# step's `outcome == 'failure'`, and every third-party action must be SHA-pinned. +# No bats dependency: the guard is driven as a subprocess against temporary +# fixture workflows. +# Run: bash scripts/test-sonarcloud-workflow.test.sh +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +GUARD="${SCRIPT_DIR}/test-sonarcloud-workflow.sh" + +fails=0 +pass() { + local desc="$1" + echo "ok - $desc" +} +fail() { + local desc="$1" + echo "FAIL - $desc" + fails=$((fails + 1)) +} + +# The guard uses yq to parse YAML (its Check 0). Without yq it exits early and +# these fixtures cannot be exercised, so skip cleanly rather than report noise. +if ! command -v yq >/dev/null 2>&1; then + echo "SKIP: 'yq' not installed — cannot exercise the guard" + exit 0 +fi + +if ! TMP="$(mktemp -d)"; then + echo "FAIL: Failed to create temporary directory" >&2 + exit 1 +fi +trap 'rm -rf "$TMP"' EXIT + +# ── Fixture fragments ────────────────────────────────────────────────────── +# A valid header shared by every fixture: top-level permissions, per-ref +# concurrency, and a checkout with fetch-depth: 0 (SHA-pinned). +write_header() { + local file="$1" + cat > "$file" <<'YAML' +name: SonarCloud Analysis +on: + push: + branches: [main] + pull_request: + branches: [main] +permissions: {} +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + sonarcloud: + name: SonarCloud + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + steps: + - name: Checkout repository + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 0 +YAML +} + +# The correct pair: a continue-on-error primary scan (id: sonar) followed by a +# retry guarded on that step's outcome == 'failure'. Both SHA-pinned. +append_good_scan() { + local file="$1" + cat >> "$file" <<'YAML' + - name: SonarCloud Scan + id: sonar + if: env.SONAR_TOKEN != '' + uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1 + continue-on-error: true + - name: SonarCloud Scan (retry) + if: env.SONAR_TOKEN != '' && steps.sonar.outcome == 'failure' + uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1 +YAML +} + +# A single scan step that is NOT continue-on-error: a transient endpoint blip +# fails the job outright (the exact regression this guard prevents). +append_scan_without_continue_on_error() { + local file="$1" + cat >> "$file" <<'YAML' + - name: SonarCloud Scan + id: sonar + if: env.SONAR_TOKEN != '' + uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1 + - name: SonarCloud Scan (retry) + if: env.SONAR_TOKEN != '' && steps.sonar.outcome == 'failure' + uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1 +YAML +} + +# A continue-on-error primary scan with NO retry: the first blip is swallowed and +# the analysis never runs — the resilience is only half present. +append_scan_without_retry() { + local file="$1" + cat >> "$file" <<'YAML' + - name: SonarCloud Scan + id: sonar + if: env.SONAR_TOKEN != '' + uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1 + continue-on-error: true +YAML +} + +# Correct scan pair but the retry action is pinned to a mutable tag, not a SHA. +append_good_scan_unpinned_retry() { + local file="$1" + cat >> "$file" <<'YAML' + - name: SonarCloud Scan + id: sonar + if: env.SONAR_TOKEN != '' + uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1 + continue-on-error: true + - name: SonarCloud Scan (retry) + if: env.SONAR_TOKEN != '' && steps.sonar.outcome == 'failure' + uses: SonarSource/sonarqube-scan-action@v8.2.1 +YAML +} + +run_guard() { + local file="$1" + bash "$GUARD" "$file" >/dev/null 2>&1 +} + +# ── Case 1: the real workflow is accepted ────────────────────────────────── +real="${SCRIPT_DIR}/../.github/workflows/sonarcloud.yml" +if run_guard "$real"; then + pass "the checked-in .github/workflows/sonarcloud.yml is accepted" +else + fail "the checked-in .github/workflows/sonarcloud.yml should be ACCEPTED" +fi + +# ── Case 2: a valid synthesized workflow is accepted ─────────────────────── +good="${TMP}/sonar-good.yml" +write_header "$good" +append_good_scan "$good" +if run_guard "$good"; then + pass "continue-on-error primary scan + failure-guarded retry is accepted" +else + fail "a valid continue-on-error + retry workflow should be ACCEPTED" +fi + +# ── Case 3: scan missing continue-on-error is rejected ───────────────────── +noce="${TMP}/sonar-no-continue.yml" +write_header "$noce" +append_scan_without_continue_on_error "$noce" +if run_guard "$noce"; then + fail "scan step without continue-on-error should be REJECTED" +else + pass "scan step without continue-on-error is rejected" +fi + +# ── Case 4: missing failure-guarded retry is rejected ────────────────────── +noretry="${TMP}/sonar-no-retry.yml" +write_header "$noretry" +append_scan_without_retry "$noretry" +if run_guard "$noretry"; then + fail "workflow without a failure-guarded retry step should be REJECTED" +else + pass "workflow without a failure-guarded retry step is rejected" +fi + +# ── Case 5: an unpinned (tag) action is rejected ─────────────────────────── +unpinned="${TMP}/sonar-unpinned.yml" +write_header "$unpinned" +append_good_scan_unpinned_retry "$unpinned" +if run_guard "$unpinned"; then + fail "workflow with a tag-pinned action should be REJECTED" +else + pass "workflow with a tag-pinned action is rejected" +fi + +# ── Case 6: a missing workflow file fails cleanly ────────────────────────── +if run_guard "${TMP}/does-not-exist.yml"; then + fail "a missing workflow file should be REJECTED" +else + pass "a missing workflow file is rejected" +fi + +echo "" +if [[ "$fails" -eq 0 ]]; then + echo "All tests passed." + exit 0 +fi +echo "$fails test(s) failed." >&2 +exit 1 From eded3a19e3461cdf9f00e978807e81d21ba32ced Mon Sep 17 00:00:00 2001 From: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com> Date: Wed, 19 Aug 2026 07:38:49 +0000 Subject: [PATCH 2/3] fix(reviews): address review comments [skip ci-relay] --- scripts/test-sonarcloud-workflow.sh | 78 ++++++++++++++++-------- scripts/test-sonarcloud-workflow.test.sh | 31 +++++++++- 2 files changed, 83 insertions(+), 26 deletions(-) diff --git a/scripts/test-sonarcloud-workflow.sh b/scripts/test-sonarcloud-workflow.sh index fef6b3ae..d94fd6cb 100755 --- a/scripts/test-sonarcloud-workflow.sh +++ b/scripts/test-sonarcloud-workflow.sh @@ -50,9 +50,11 @@ if ! primary_ce=$(yq " exit 1 fi if [[ "$primary_ce" -lt 1 ]]; then - echo "FAIL: no '${SCAN_ACTION}' scan step with 'continue-on-error: true' in $WORKFLOW" - echo " The first scan must be continue-on-error so a flaky endpoint blip" - echo " does not fail the job before the retry runs." + { + echo "FAIL: no '${SCAN_ACTION}' scan step with 'continue-on-error: true' in $WORKFLOW" + echo " The first scan must be continue-on-error so a flaky endpoint blip" + echo " does not fail the job before the retry runs." + } >&2 PASS=false else echo "PASS: primary scan step is 'continue-on-error: true' in $WORKFLOW" @@ -60,25 +62,48 @@ fi # ── Check 3: a retry scan step is guarded on the primary's failure ───────── # The retry re-runs the scan only when the primary reported failure, expressed as -# `steps..outcome == 'failure'` in its `if:` condition. This is what turns a -# single flaky blip into a self-healing run instead of a red build. -if ! retry_guards=$(yq " - [ .jobs[].steps[] - | select((.uses // \"\") | test(\"^${SCAN_ACTION}@\")) - | select((.[\"continue-on-error\"] // false) != true) - | select((.if // \"\") | test(\"outcome *== *'failure'\")) ] | length -" "$WORKFLOW" 2>/dev/null); then - echo "FAIL: yq failed to parse $WORKFLOW. Please check if it is valid YAML." - exit 1 -fi -if [[ "$retry_guards" -lt 1 ]]; then - echo "FAIL: no '${SCAN_ACTION}' retry step guarded on \"outcome == 'failure'\" in $WORKFLOW" - echo " Add a second scan step with:" - echo " if: ... && steps..outcome == 'failure'" - echo " so a transient first-scan failure is retried instead of failing CI." - PASS=false -else - echo "PASS: failure-guarded retry scan step present in $WORKFLOW" +# `steps..outcome == 'failure'` in its `if:` condition — bound to the primary +# step's specific id so the guard fires reliably. This is what turns a single +# flaky blip into a self-healing run instead of a red build. +if [[ "$primary_ce" -ge 1 ]]; then + if ! primary_id=$(yq " + [.jobs[].steps[] + | select((.uses // \"\") | test(\"^${SCAN_ACTION}@\")) + | select(.[\"continue-on-error\"] == true) + | .id // \"\"] | .[0] + " "$WORKFLOW" 2>/dev/null); then + echo "FAIL: yq failed to parse $WORKFLOW. Please check if it is valid YAML." >&2 + exit 1 + fi + if [[ -z "$primary_id" || "$primary_id" == "null" ]]; then + { + echo "FAIL: the '${SCAN_ACTION}' step with 'continue-on-error: true' in $WORKFLOW has no 'id:'" + echo " Give the primary scan step an 'id:' so the retry step can reference" + echo " steps..outcome == 'failure'" + } >&2 + PASS=false + else + if ! retry_guards=$(yq " + [ .jobs[].steps[] + | select((.uses // \"\") | test(\"^${SCAN_ACTION}@\")) + | select((.[\"continue-on-error\"] // false) != true) + | select((.if // \"\") | test(\"steps\\.${primary_id}\\.outcome *== *'failure'\")) ] | length + " "$WORKFLOW" 2>/dev/null); then + echo "FAIL: yq failed to parse $WORKFLOW. Please check if it is valid YAML." >&2 + exit 1 + fi + if [[ "$retry_guards" -lt 1 ]]; then + { + echo "FAIL: no '${SCAN_ACTION}' retry step guarded on \"steps.${primary_id}.outcome == 'failure'\" in $WORKFLOW" + echo " Add a second scan step with:" + echo " if: ... && steps.${primary_id}.outcome == 'failure'" + echo " so a transient first-scan failure is retried instead of failing CI." + } >&2 + PASS=false + else + echo "PASS: failure-guarded retry scan step present in $WORKFLOW" + fi + fi fi # ── Check 4: every third-party action is SHA-pinned ──────────────────────── @@ -88,16 +113,19 @@ if ! mapfile -t uses < <(yq '.jobs[].steps[] | .uses // "" ' "$WORKFLOW" 2>/dev/ echo "FAIL: yq failed to parse $WORKFLOW. Please check if it is valid YAML." exit 1 fi +sha_regex='@[0-9a-f]{40}$' unpinned="" for ref in "${uses[@]}"; do [[ -z "$ref" || "$ref" == "null" ]] && continue - if [[ ! "$ref" =~ @[0-9a-f]{40}$ ]]; then + if [[ ! "$ref" =~ $sha_regex ]]; then unpinned+=" - ${ref}"$'\n' fi done if [[ -n "$unpinned" ]]; then - echo "FAIL: unpinned action(s) in $WORKFLOW (SHA-pin every third-party action):" - printf '%s' "$unpinned" + { + echo "FAIL: unpinned action(s) in $WORKFLOW (SHA-pin every third-party action):" + printf '%s' "$unpinned" + } >&2 PASS=false else echo "PASS: all third-party actions are SHA-pinned in $WORKFLOW" diff --git a/scripts/test-sonarcloud-workflow.test.sh b/scripts/test-sonarcloud-workflow.test.sh index 8227011d..b66fb6f6 100644 --- a/scripts/test-sonarcloud-workflow.test.sh +++ b/scripts/test-sonarcloud-workflow.test.sh @@ -10,7 +10,10 @@ # Run: bash scripts/test-sonarcloud-workflow.test.sh set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +if ! SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; then + echo "FAIL: Failed to determine script directory" >&2 + exit 1 +fi GUARD="${SCRIPT_DIR}/test-sonarcloud-workflow.sh" fails=0 @@ -114,6 +117,22 @@ append_scan_without_retry() { YAML } +# A retry condition that guards on an unrelated step's failure rather than the +# primary scan's id, so the retry does not reliably fire on a primary-scan blip. +append_scan_wrong_retry_id() { + local file="$1" + cat >> "$file" <<'YAML' + - name: SonarCloud Scan + id: sonar + if: env.SONAR_TOKEN != '' + uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1 + continue-on-error: true + - name: SonarCloud Scan (retry) + if: env.SONAR_TOKEN != '' && steps.other.outcome == 'failure' + uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1 +YAML +} + # Correct scan pair but the retry action is pinned to a mutable tag, not a SHA. append_good_scan_unpinned_retry() { local file="$1" @@ -189,6 +208,16 @@ else pass "a missing workflow file is rejected" fi +# ── Case 7: retry condition referencing an unrelated step ID is rejected ──── +wrongid="${TMP}/sonar-wrong-retry-id.yml" +write_header "$wrongid" +append_scan_wrong_retry_id "$wrongid" +if run_guard "$wrongid"; then + fail "retry condition referencing an unrelated step ID should be REJECTED" +else + pass "retry condition referencing an unrelated step ID is rejected" +fi + echo "" if [[ "$fails" -eq 0 ]]; then echo "All tests passed." From 9dd2f0fbe63ed81caa0930ff498fed5b43e4cbb4 Mon Sep 17 00:00:00 2001 From: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com> Date: Wed, 19 Aug 2026 07:40:53 +0000 Subject: [PATCH 3/3] chore: dev-lead update (review-changes) [skip ci-relay] --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6c4620f..0363bf1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,3 +103,4 @@ jobs: bash scripts/test-ci-workflow.sh bash scripts/test-copilot-setup-steps-workflow.sh bash scripts/test-sonarcloud-workflow.sh + bash scripts/test-sonarcloud-workflow.test.sh