Skip to content

codecov-pr

codecov-pr #138

Workflow file for this run

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