Skip to content
Merged
Changes from all commits
Commits
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
59 changes: 57 additions & 2 deletions .github/workflows/integrations-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ name: 'Integration connectivity check'
#
# It also runs on a nightly schedule so an expiring/revoked SONAR_TOKEN or
# CODECOV_TOKEN surfaces as a failed run (and the usual GitHub failure
# notification) before it silently breaks a real CI/release run. Scheduled runs
# check all integrations.
# notification) before it silently breaks a real CI/release run. The sonarcloud
# job additionally warns once the token is within 7 days of its expiry.
# Scheduled runs check all integrations.
#
# Both jobs are skipped on forks (same convention as dependency-review.yml):
# the secrets are absent there, so jobs would fail nightly on every fork.
Expand Down Expand Up @@ -74,6 +75,60 @@ jobs:
fi
echo "OK: SONAR_TOKEN is valid and SonarCloud is reachable."

- name: Check SonarCloud token expiry
run: |
set -euo pipefail
if [ -z "${SONAR_TOKEN:-}" ]; then
echo "::error::SONAR_TOKEN secret is empty or not configured."
exit 1
fi
# Warn ahead of expiry so the token can be rotated before CI/release
# jobs break: authentication/validate only reports valid/invalid, not
# the expiration date, so query the user tokens endpoint. Runs only on
# the upstream repo (job-level fork guard) after the validate step, so
# reachability and validity are already proven - anything that still
# prevents determining the expiry is a real problem and fails here.
body="$RUNNER_TEMP/sonar-tokens.json"
http_code="$(curl -sS -o "$body" -w '%{http_code}' \
-u "$SONAR_TOKEN:" https://sonarcloud.io/api/user_tokens/search || true)"
echo "sonarcloud.io/api/user_tokens/search -> HTTP $http_code"
if [ "$http_code" != "200" ]; then
echo "::error::user_tokens/search returned HTTP $http_code although the token validated; cannot determine its expiry."
exit 1
fi
# SONAR_TOKEN is the user token whose name contains "github"
# (case-insensitive, anywhere in the name).
match_count="$(jq '[.userTokens[] | select((.name | ascii_downcase) | contains("github"))] | length' "$body" 2>/dev/null || echo '')"
if [ -z "$match_count" ]; then
echo "::error::Could not parse the user_tokens/search response; cannot determine the token expiry."
cat "$body" || true
exit 1
fi
if [ "$match_count" = "0" ]; then
echo "::error::No user token with 'github' in its name found; SONAR_TOKEN does not match the expected token or it was renamed."
exit 1
fi
# Soonest expirationDate (YYYY-MM-DDTHH:MM:SSZ) among the matching
# tokens; tokens created without an expiry have no expirationDate.
expiry="$(jq -r '[.userTokens[] | select((.name | ascii_downcase) | contains("github")) | .expirationDate // empty] | sort | .[0] // empty' "$body")"
if [ -z "$expiry" ]; then
echo "The 'github' user token(s) have no expiration date; nothing to warn about."
exit 0
fi
if ! expiry_epoch="$(date -d "$expiry" +%s 2>/dev/null)"; then
echo "::error::Could not parse expirationDate '$expiry'; cannot determine the token expiry."
exit 1
fi
days_left=$(( (expiry_epoch - $(date +%s)) / 86400 ))
expiry_day="${expiry%%T*}"
if [ "$days_left" -lt 0 ]; then
echo "::warning::SONAR_TOKEN expired $((-days_left)) day(s) ago on $expiry_day; rotate it to restore CI/release jobs."
elif [ "$days_left" -le 7 ]; then
echo "::warning::SONAR_TOKEN expires in $days_left day(s) on $expiry_day; rotate it to avoid CI/release failures."
else
echo "OK: SONAR_TOKEN expires on $expiry_day ($days_left days remaining)."
fi

codecov:
# Scheduled runs have no dispatch inputs, so always run both checks then.
if: ${{ github.event.repository.fork == false && (github.event_name == 'schedule' || github.event.inputs.target == 'all' || github.event.inputs.target == 'codecov') }}
Expand Down
Loading