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
208 changes: 57 additions & 151 deletions .github/workflows/sigscanner-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ concurrency:
group: ${{ github.workflow }}-pr-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

permissions:
pull-requests: read
permissions: {}

jobs:
sigscanner-check:
Expand All @@ -21,170 +20,77 @@ jobs:
env:
REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_COMMIT_COUNT: ${{ github.event.pull_request.commits }}
VERIFY_MAX_ATTEMPTS: "3"
steps:
- name: "Fetch PR commits"
id: fetch-commits
env:
GH_TOKEN: ${{ github.token }}
run: |
# Fetch all commit hashes and their corresponding committers in this PR
gh api "repos/$REPOSITORY/pulls/$PR_NUMBER/commits" --paginate \
--jq '.[] | [.sha, (.committer.login // "")] | join(",")' \
> /tmp/commits_with_committer.csv

commit_count=$(wc -l < /tmp/commits_with_committer.csv | tr -d ' ')
echo "Found $commit_count commits in PR #$PR_NUMBER"
echo "commit-count=$commit_count" >> "$GITHUB_OUTPUT"

if [[ $commit_count -eq 0 ]]; then
echo "❌ Unexpected: no commits to verify"
exit 1
fi

- name: "Sigscanner check"
id: sigscanner
continue-on-error: true
env:
Comment thread
timweri marked this conversation as resolved.
SIGSCANNER_URL: ${{ secrets.SIGSCANNER_URL }}
SIGSCANNER_API_KEY: ${{ secrets.SIGSCANNER_API_KEY }}
COMMIT_COUNT: ${{ steps.fetch-commits.outputs.commit-count }}
run: |
> /tmp/verified_commits.csv

echo "🔎 Verifying $COMMIT_COUNT commits"

# Loop through all the commits
# For each commit, query Sigscanner with retry to check if it's verified
# Verified commit hashes with committer username are saved to /tmp/verified_commits.csv
while IFS=, read -r commit_sha committer_username; do
[[ -z "$commit_sha" ]] && continue

commit_is_verified=false
request_attempt=1

while [[ $request_attempt -le $VERIFY_MAX_ATTEMPTS ]]; do
response=$(curl -s --max-time 20 -G \
-H "X-SIGSCANNER-SECRET: $SIGSCANNER_API_KEY" \
--data-urlencode "commit=$commit_sha" \
--data-urlencode "repository=$REPOSITORY" \
--data-urlencode "author=$committer_username" \
"$SIGSCANNER_URL")

res_verified=$(echo "$response" | jq -r '.verified')
res_error=$(echo "$response" | jq -r '.error')

if [[ "$res_verified" == "true" ]]; then
commit_is_verified=true
break
elif [[ "$res_error" == "null" || "$res_error" == "" ]]; then
# This means the commit is explicitly unverified and shouldn't be retried
break
echo "Verifying $PR_COMMIT_COUNT commits..."
request_attempt=1
total_start=$SECONDS
while [[ $request_attempt -le $VERIFY_MAX_ATTEMPTS ]]; do
echo "::group::Attempt $request_attempt/$VERIFY_MAX_ATTEMPTS — calling Sigscanner API..."
attempt_start=$SECONDS

http_code=$(curl -s -o /tmp/sigscanner_response --max-time 300 -w '%{http_code}' -G \
Comment thread
timweri marked this conversation as resolved.
-H "X-SIGSCANNER-SECRET: $SIGSCANNER_API_KEY" \
--data-urlencode "pr=$PR_NUMBER" \
--data-urlencode "repository=$REPOSITORY" \
"$SIGSCANNER_URL")
Comment thread
timweri marked this conversation as resolved.
response=$(cat /tmp/sigscanner_response)

elapsed=$(( SECONDS - attempt_start ))
echo "API responded in ${elapsed}s (HTTP $http_code)"
echo "::endgroup::"

if [[ "$http_code" != "200" ]]; then
echo "❌ Sigscanner API returned HTTP $http_code (attempt $request_attempt, ${elapsed}s)"
echo "If this PR has many commits, Sigscanner might time out. Try running the workflow again. Sigscanner will pick up from the last verified commit."
if [[ $request_attempt -lt $VERIFY_MAX_ATTEMPTS ]]; then
echo "⏳ Retrying in 15s..."
sleep 15
fi

[[ $request_attempt -lt $VERIFY_MAX_ATTEMPTS ]] && sleep 15
request_attempt=$((request_attempt + 1))
done

if [[ "$commit_is_verified" == "true" ]]; then
echo "✅ $commit_sha"
echo "$commit_sha,$committer_username" >> /tmp/verified_commits.csv
else
echo "❌ $commit_sha"
continue
fi
done < /tmp/commits_with_committer.csv

verified_commit_count=$(wc -l < /tmp/verified_commits.csv | tr -d ' ')
echo "Verified: $verified_commit_count / $COMMIT_COUNT"

if [[ $verified_commit_count -eq $COMMIT_COUNT ]]; then
echo "✅ All commits verified"
exit 0
fi

echo "❌ Not all commits verified"
exit 1

- name: "Sigscanner fallback check"
if: ${{ steps.sigscanner.outcome == 'failure' }}
env:
API_TOKEN: ${{ secrets.SIGSCANNER_API_TOKEN }}
API_URL: ${{ secrets.SIGSCANNER_API_URL }}
COMMIT_COUNT: ${{ steps.fetch-commits.outputs.commit-count }}
run: |
touch /tmp/verified_commits.csv

# Extract commits failed to verify earlier by comparing the verified commits file
# with the full list of commits
grep -vxFf /tmp/verified_commits.csv /tmp/commits_with_committer.csv \
> /tmp/pending_commits.csv

pending_commit_count=$(wc -l < /tmp/pending_commits.csv | tr -d ' ')

if [[ $pending_commit_count -eq 0 ]]; then
echo "✅ All commits verified"
exit 0
fi

echo "🔎 Fallback: verifying $pending_commit_count remaining commits"

# Loop through all the commits again with retry with the fallback API
while IFS=, read -r commit_sha committer_username; do
[[ -z "$commit_sha" ]] && continue

commit_is_verified=false
request_attempt=1

while [[ $request_attempt -le $VERIFY_MAX_ATTEMPTS ]]; do
body=$(jq -n \
--arg commit "$commit_sha" \
--arg repository "$REPOSITORY" \
--arg author "$committer_username" \
'{commit: $commit, repository: $repository, author: $author}')

http_status=$(curl --silent --output /dev/null --write-out '%{http_code}' \
--max-time 20 -X POST \
-H "Content-Type: application/json" \
-H "Authorization: $API_TOKEN" \
--url "$API_URL" \
--data "$body")

case $http_status in
200)
commit_is_verified=true
break
;;
400)
echo "❌ $commit_sha - Bad request"
break
;;
403) break ;;
5??)
[[ $request_attempt -lt $VERIFY_MAX_ATTEMPTS ]] && sleep 15
;;
*)
echo "❌ $commit_sha - Unexpected: $http_status"
break
;;
esac

if ! echo "$response" | jq empty >/dev/null 2>&1; then
echo "❌ HTTP 200 but body is not valid JSON (attempt $request_attempt, ${elapsed}s)"
echo
if [[ $request_attempt -lt $VERIFY_MAX_ATTEMPTS ]]; then
echo "⏳ Retrying in 15s..."
sleep 15
fi
request_attempt=$((request_attempt + 1))
done
continue
fi

if [[ "$commit_is_verified" == "true" ]]; then
echo "✅ $commit_sha"
echo "$commit_sha,$committer_username" >> /tmp/verified_commits.csv
res_verified=$(echo "$response" | jq -r '.verified')
res_error=$(echo "$response" | jq -r '.error')

if [[ "$res_verified" == "true" ]]; then
echo "✅ All commits verified"
exit 0
elif [[ "$(echo "$response" | jq '(.unverified_commits // []) | length > 0')" == "true" ]]; then
Comment thread
timweri marked this conversation as resolved.
# Non-empty unverified_commits: definitive result, do not retry
echo "❌ Unverified commits:"
echo "$response" | jq -r '.unverified_commits[] | " - \(.)"'
break
else
echo "❌ $commit_sha"
echo "❌ Error: $res_error"
fi
done < /tmp/pending_commits.csv

total_verified_count=$(wc -l < /tmp/verified_commits.csv | tr -d ' ')
echo "Verified: $total_verified_count / $COMMIT_COUNT"

if [[ $total_verified_count -ne $COMMIT_COUNT ]]; then
echo "❌ Not all commits verified by fallback"
exit 1
fi
if [[ $request_attempt -lt $VERIFY_MAX_ATTEMPTS ]]; then
echo "⏳ Retrying in 15s..."
sleep 15
fi
request_attempt=$((request_attempt + 1))
done

echo "✅ All commits verified"
total_elapsed=$(( SECONDS - total_start ))
echo "❌ Not all commits verified (total time: ${total_elapsed}s)"
exit 1
Loading