Repository navigation
codecov-pr #141
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: codecov-pr | |
| on: | |
| workflow_run: | |
| workflows: [ci] | |
| types: [completed] | |
| permissions: {} | |
| concurrency: | |
| group: codecov-pr-${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }} | |
| cancel-in-progress: false | |
| jobs: | |
| upload: | |
| name: coverage-upload-pr | |
| if: >- | |
| github.event.workflow_run.event == 'pull_request' && | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.pull_requests[0].base.ref == 'main' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 25 | |
| permissions: | |
| actions: read | |
| contents: read | |
| id-token: write | |
| steps: | |
| - name: Validate source workflow run | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }} | |
| PULL_REQUESTS_JSON: ${{ toJson(github.event.workflow_run.pull_requests) }} | |
| REPOSITORY: ${{ github.repository }} | |
| RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }} | |
| RUN_CONCLUSION: ${{ github.event.workflow_run.conclusion }} | |
| RUN_EVENT: ${{ github.event.workflow_run.event }} | |
| RUN_ID: ${{ github.event.workflow_run.id }} | |
| RUN_REPOSITORY: ${{ github.event.workflow_run.repository.full_name }} | |
| WORKFLOW_PATH: ${{ github.event.workflow_run.path }} | |
| run: | | |
| set -euo pipefail | |
| test "$RUN_EVENT" = pull_request | |
| test "$RUN_CONCLUSION" = success | |
| test "$RUN_REPOSITORY" = "$REPOSITORY" | |
| test "$WORKFLOW_PATH" = .github/workflows/ci.yml | |
| printf '%s' "$PULL_REQUESTS_JSON" | jq -e 'length == 1' >/dev/null | |
| for value in "$PR_NUMBER" "$RUN_ATTEMPT" "$RUN_ID"; do | |
| case "$value" in | |
| *[!0-9]*|'') echo "invalid source workflow identity" >&2; exit 1 ;; | |
| esac | |
| done | |
| jobs_file="$RUNNER_TEMP/source-workflow-jobs.json" | |
| jobs_url="https://api.github.com/repos/$REPOSITORY/actions/runs/$RUN_ID/jobs?filter=latest&per_page=100" | |
| http_code="$(curl --silent --show-error --connect-timeout 5 --max-time 20 \ | |
| --header 'Accept: application/vnd.github+json' \ | |
| --header "Authorization: Bearer $GITHUB_TOKEN" \ | |
| --header 'X-GitHub-Api-Version: 2022-11-28' \ | |
| --output "$jobs_file" --write-out '%{http_code}' "$jobs_url")" | |
| test "$http_code" = 200 || { echo "source jobs API returned HTTP $http_code" >&2; exit 1; } | |
| jq -e ' | |
| .total_count < 100 and | |
| ([.jobs[]? | select(.name == "coverage-report")] | length) == 1 and | |
| ([.jobs[]? | select(.name == "coverage-report" and .conclusion == "success")] | length) == 1 and | |
| ([.jobs[]? | select(.name == "required-ci")] | length) == 1 and | |
| ([.jobs[]? | select(.name == "required-ci" and .conclusion == "success")] | length) == 1 | |
| ' "$jobs_file" >/dev/null | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: coverage-comparison-profiles | |
| path: coverage-profiles | |
| github-token: ${{ github.token }} | |
| repository: ${{ github.repository }} | |
| run-id: ${{ github.event.workflow_run.id }} | |
| digest-mismatch: error | |
| - name: Validate tested commit and comparison profiles | |
| id: metadata | |
| env: | |
| EVENT_BASE_SHA: ${{ github.event.workflow_run.pull_requests[0].base.sha }} | |
| EVENT_HEAD_SHA: ${{ github.event.workflow_run.pull_requests[0].head.sha }} | |
| EVENT_PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }} | |
| REPOSITORY: ${{ github.repository }} | |
| RUN_HEAD_SHA: ${{ github.event.workflow_run.head_sha }} | |
| RUN_ID: ${{ github.event.workflow_run.id }} | |
| SERVER_URL: ${{ github.server_url }} | |
| run: | | |
| set -euo pipefail | |
| expected=(active-union.out codecov-pr-metadata.tsv experimental-tui-union.out tooling-unit.out) | |
| mapfile -d '' actual < <(find coverage-profiles -mindepth 1 -maxdepth 1 -print0 | sort -z) | |
| test "${#actual[@]}" -eq "${#expected[@]}" || { echo "comparison artifact has an unexpected entry count" >&2; exit 1; } | |
| for index in "${!expected[@]}"; do | |
| test "${actual[$index]}" = "coverage-profiles/${expected[$index]}" || { | |
| echo "comparison artifact contains an unexpected entry: ${actual[$index]}" >&2 | |
| exit 1 | |
| } | |
| done | |
| metadata=coverage-profiles/codecov-pr-metadata.tsv | |
| test -f "$metadata" && test ! -L "$metadata" | |
| test "$(wc -l < "$metadata")" -eq 7 | |
| mapfile -t rows < "$metadata" | |
| test "${#rows[@]}" -eq 7 | |
| test "${rows[0]}" = $'field\tvalue' | |
| read_field() { | |
| local expected_field="$1" | |
| local row="$2" | |
| local prefix value | |
| prefix="${expected_field}"$'\t' | |
| case "$row" in | |
| "$prefix"*) ;; | |
| *) return 1 ;; | |
| esac | |
| value="${row#"$prefix"}" | |
| test -n "$value" | |
| case "$value" in | |
| *$'\t'*) return 1 ;; | |
| esac | |
| printf '%s' "$value" | |
| } | |
| repository="$(read_field repository "${rows[1]}")" | |
| run_id="$(read_field run_id "${rows[2]}")" | |
| pr_number="$(read_field pr_number "${rows[3]}")" | |
| base_sha="$(read_field base_sha "${rows[4]}")" | |
| head_sha="$(read_field head_sha "${rows[5]}")" | |
| tested_sha="$(read_field tested_sha "${rows[6]}")" | |
| test "$repository" = "$REPOSITORY" | |
| test "$run_id" = "$RUN_ID" | |
| test "$pr_number" = "$EVENT_PR_NUMBER" | |
| test "$base_sha" = "$EVENT_BASE_SHA" | |
| test "$head_sha" = "$EVENT_HEAD_SHA" | |
| if test "$RUN_HEAD_SHA" != "$head_sha" && test "$RUN_HEAD_SHA" != "$tested_sha"; then | |
| echo "source workflow head SHA matches neither tested nor pull-request head" >&2 | |
| exit 1 | |
| fi | |
| for value in "$run_id" "$pr_number"; do | |
| case "$value" in | |
| *[!0-9]*|'') echo "invalid comparison metadata identity" >&2; exit 1 ;; | |
| esac | |
| done | |
| for sha in "$base_sha" "$head_sha" "$tested_sha"; do | |
| case "$sha" in | |
| *[!0-9a-fA-F]*|'') echo "invalid comparison metadata SHA" >&2; exit 1 ;; | |
| esac | |
| test "${#sha}" -eq 40 || { echo "comparison metadata SHA must contain 40 hexadecimal characters" >&2; exit 1; } | |
| done | |
| git init --quiet | |
| git remote add origin "$SERVER_URL/$REPOSITORY.git" | |
| git fetch --no-tags --depth=2 origin "$tested_sha" | |
| read -r first_parent second_parent extra_parent < <(git show -s --format=%P "$tested_sha") | |
| test -n "$first_parent" && test -n "$second_parent" && test -z "$extra_parent" || { | |
| echo "tested pull-request commit is not a two-parent merge" >&2 | |
| exit 1 | |
| } | |
| test "$first_parent" = "$base_sha" || { echo "tested merge first parent does not match the base" >&2; exit 1; } | |
| test "$second_parent" = "$head_sha" || { echo "tested merge second parent does not match the head" >&2; exit 1; } | |
| git update-ref refs/heads/codecov "$tested_sha" | |
| git symbolic-ref HEAD refs/heads/codecov | |
| git read-tree "$tested_sha" | |
| for profile in active-union.out experimental-tui-union.out tooling-unit.out; do | |
| filename="coverage-profiles/$profile" | |
| test -f "$filename" && test ! -L "$filename" | |
| size="$(wc -c < "$filename")" | |
| test "$size" -gt 0 && test "$size" -le 33554432 || { echo "$profile has an invalid size" >&2; exit 1; } | |
| awk ' | |
| NR == 1 { if ($0 != "mode: atomic") exit 1; next } | |
| $0 !~ /^[^[:space:]]+:[0-9]+\.[0-9]+,[0-9]+\.[0-9]+ [1-9][0-9]* [0-9]+$/ { exit 1 } | |
| END { if (NR < 2) exit 1 } | |
| ' "$filename" || { echo "$profile is not a canonical atomic Go profile" >&2; exit 1; } | |
| done | |
| { | |
| printf 'base_sha=%s\n' "$base_sha" | |
| printf 'pr_number=%s\n' "$pr_number" | |
| printf 'tested_sha=%s\n' "$tested_sha" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Wait for exact base coverage uploads | |
| env: | |
| CODECOV_PARENT_SHA: ${{ steps.metadata.outputs.base_sha }} | |
| REPOSITORY: ${{ github.repository }} | |
| TESTED_SHA: ${{ steps.metadata.outputs.tested_sha }} | |
| run: | | |
| set -euo pipefail | |
| if ! git cat-file -e "$TESTED_SHA:codecov.yml"; then | |
| echo "tested commit does not contain the required Codecov policy" >&2 | |
| exit 1 | |
| fi | |
| if ! git cat-file -e "$CODECOV_PARENT_SHA:codecov.yml"; then | |
| echo "pull-request base does not contain the required Codecov policy" >&2 | |
| exit 1 | |
| fi | |
| response_file="$RUNNER_TEMP/codecov-base-uploads.json" | |
| uploads_url="https://api.codecov.io/api/v2/github/${REPOSITORY%%/*}/repos/${REPOSITORY#*/}/commits/$CODECOV_PARENT_SHA/uploads/?page_size=100" | |
| required_flags=(active-union experimental-tui tooling) | |
| deadline=$((SECONDS + 900)) | |
| while (( SECONDS < deadline )); do | |
| http_code="$(curl --silent --show-error --connect-timeout 5 --max-time 20 --output "$response_file" \ | |
| --write-out '%{http_code}' "$uploads_url" || true)" | |
| if test "$http_code" = 200; then | |
| missing_flags=() | |
| for required_flag in "${required_flags[@]}"; do | |
| if ! jq -e --arg required_flag "$required_flag" \ | |
| 'any(.results[]?; .state == "merged" and ((.flags // []) | index($required_flag)))' \ | |
| "$response_file" >/dev/null; then | |
| missing_flags+=("$required_flag") | |
| fi | |
| done | |
| if (( ${#missing_flags[@]} == 0 )); then | |
| echo "Codecov accepted every comparison upload for $CODECOV_PARENT_SHA." | |
| exit 0 | |
| fi | |
| echo "Waiting for base Codecov flags: ${missing_flags[*]}" | |
| else | |
| echo "Waiting for base Codecov uploads (HTTP $http_code)." | |
| fi | |
| sleep 10 | |
| done | |
| echo "Timed out waiting for complete Codecov uploads on $CODECOV_PARENT_SHA" >&2 | |
| exit 1 | |
| - name: Upload pull-request active union to Codecov | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| version: v11.3.1 | |
| files: coverage-profiles/active-union.out | |
| flags: active-union | |
| commit_parent: ${{ steps.metadata.outputs.base_sha }} | |
| override_commit: ${{ steps.metadata.outputs.tested_sha }} | |
| override_pr: ${{ steps.metadata.outputs.pr_number }} | |
| override_branch: pr-${{ steps.metadata.outputs.pr_number }} | |
| disable_search: true | |
| disable_file_fixes: true | |
| fail_ci_if_error: true | |
| use_oidc: true | |
| - name: Upload pull-request experimental TUI union to Codecov | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| version: v11.3.1 | |
| files: coverage-profiles/experimental-tui-union.out | |
| flags: experimental-tui | |
| commit_parent: ${{ steps.metadata.outputs.base_sha }} | |
| override_commit: ${{ steps.metadata.outputs.tested_sha }} | |
| override_pr: ${{ steps.metadata.outputs.pr_number }} | |
| override_branch: pr-${{ steps.metadata.outputs.pr_number }} | |
| disable_search: true | |
| disable_file_fixes: true | |
| fail_ci_if_error: true | |
| use_oidc: true | |
| - name: Upload pull-request tooling unit profile to Codecov | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| version: v11.3.1 | |
| files: coverage-profiles/tooling-unit.out | |
| flags: tooling | |
| commit_parent: ${{ steps.metadata.outputs.base_sha }} | |
| override_commit: ${{ steps.metadata.outputs.tested_sha }} | |
| override_pr: ${{ steps.metadata.outputs.pr_number }} | |
| override_branch: pr-${{ steps.metadata.outputs.pr_number }} | |
| disable_search: true | |
| disable_file_fixes: true | |
| fail_ci_if_error: true | |
| use_oidc: true |