Integration connectivity check #52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: 'Integration connectivity check' | |
| # Maintainer-only diagnostic: verify the 3rd-party integration secrets and their | |
| # endpoints are usable, without building or running any project/PR code. | |
| # | |
| # workflow_dispatch can only be started by users with write access, from the | |
| # default branch, so it is inherently maintainer-gated and never runs for fork | |
| # PRs. It performs authenticated HTTP checks only; token values are never | |
| # printed. This is a diagnostic and does not replace the fork-ci environment | |
| # approval that gates the actual Sonar publish. | |
| # | |
| # 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. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| target: | |
| description: 'Which integration(s) to check' | |
| required: true | |
| default: all | |
| type: choice | |
| options: | |
| - all | |
| - sonarcloud | |
| - codecov | |
| schedule: | |
| # Nightly at 06:00 UTC. Scheduled runs only execute from the default branch. | |
| - cron: '0 6 * * *' | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: integrations-check-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # Independent jobs so the two checks run in parallel and a failing SonarCloud | |
| # check never blocks the Codecov check (and vice versa). | |
| sonarcloud: | |
| # Scheduled runs have no dispatch inputs, so always run both checks then. | |
| if: ${{ github.event_name == 'schedule' || github.event.inputs.target == 'all' || github.event.inputs.target == 'sonarcloud' }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| steps: | |
| - name: Check SonarCloud token and connectivity | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${SONAR_TOKEN:-}" ]; then | |
| echo "::error::SONAR_TOKEN secret is empty or not configured." | |
| exit 1 | |
| fi | |
| # Validate the token via SonarCloud's authentication endpoint. The token | |
| # is sent as HTTP basic-auth username; its value is never printed. | |
| body="$RUNNER_TEMP/sonar-validate.json" | |
| http_code="$(curl -sS -o "$body" -w '%{http_code}' \ | |
| -u "$SONAR_TOKEN:" https://sonarcloud.io/api/authentication/validate || true)" | |
| echo "sonarcloud.io/api/authentication/validate -> HTTP $http_code" | |
| if [ "$http_code" != "200" ]; then | |
| echo "::error::SonarCloud returned HTTP $http_code (token likely invalid/expired, or the API is unreachable)." | |
| exit 1 | |
| fi | |
| if ! grep -q '"valid":true' "$body"; then | |
| echo "::error::SonarCloud reports the token as NOT valid." | |
| cat "$body" | |
| exit 1 | |
| fi | |
| echo "OK: SONAR_TOKEN is valid and SonarCloud is reachable." | |
| codecov: | |
| # Scheduled runs have no dispatch inputs, so always run both checks then. | |
| if: ${{ github.event_name == 'schedule' || github.event.inputs.target == 'all' || github.event.inputs.target == 'codecov' }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| env: | |
| CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | |
| steps: | |
| - name: Check Codecov token and connectivity | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${CODECOV_TOKEN:-}" ]; then | |
| echo "::error::CODECOV_TOKEN secret is empty or not configured." | |
| exit 1 | |
| fi | |
| owner="${GITHUB_REPOSITORY%/*}" | |
| repo="${GITHUB_REPOSITORY#*/}" | |
| body="$RUNNER_TEMP/codecov.json" | |
| # CODECOV_TOKEN is the repo upload token: exercise the first step of | |
| # Codecov's upload handshake (create-commit) against the real HEAD | |
| # commit. This is the same call the uploader makes, so a valid token | |
| # accepts it; the token is sent only in the auth header (never printed). | |
| url="https://api.codecov.io/upload/github/$owner::::$repo/commits" | |
| http_code="$(curl -sS -o "$body" -w '%{http_code}' -X POST \ | |
| -H "Authorization: token $CODECOV_TOKEN" \ | |
| -H 'Content-Type: application/json' \ | |
| -d "{\"commitid\":\"$GITHUB_SHA\"}" "$url" || true)" | |
| echo "POST api.codecov.io/upload/github/.../commits -> HTTP $http_code" | |
| case "$http_code" in | |
| 200|201|202) | |
| # 202 "Accepted" means Codecov queued the create-commit request, | |
| # which requires a valid, authorized token -> the token works. | |
| echo "OK: CODECOV_TOKEN authenticates against Codecov (HTTP $http_code)." ;; | |
| 401|403) | |
| echo "::error::Codecov rejected the token (HTTP $http_code) - it is invalid, expired, or lacks access to this repo." | |
| exit 1 ;; | |
| 404) | |
| # The upload endpoint returns 404 "Repository not found" both for an | |
| # invalid/unauthorized upload token and for a repo not activated on | |
| # Codecov (it does not disclose which, by design). | |
| echo "::error::Codecov returned 404 'Repository not found' - the upload token is invalid/unauthorized, or the repo is not activated on Codecov." | |
| cat "$body" || true | |
| exit 1 ;; | |
| *) | |
| echo "::error::Unexpected response from Codecov (HTTP $http_code) - likely a reachability/service issue." | |
| cat "$body" || true | |
| exit 1 ;; | |
| esac |