@@ -9,8 +9,7 @@ concurrency:
99 group : ${{ github.workflow }}-pr-${{ github.event.pull_request.number || github.run_id }}
1010 cancel-in-progress : true
1111
12- permissions :
13- pull-requests : read
12+ permissions : {}
1413
1514jobs :
1615 sigscanner-check :
@@ -21,170 +20,77 @@ jobs:
2120 env :
2221 REPOSITORY : ${{ github.repository }}
2322 PR_NUMBER : ${{ github.event.pull_request.number }}
23+ PR_COMMIT_COUNT : ${{ github.event.pull_request.commits }}
2424 VERIFY_MAX_ATTEMPTS : " 3"
2525 steps :
26- - name : " Fetch PR commits"
27- id : fetch-commits
28- env :
29- GH_TOKEN : ${{ github.token }}
30- run : |
31- # Fetch all commit hashes and their corresponding committers in this PR
32- gh api "repos/$REPOSITORY/pulls/$PR_NUMBER/commits" --paginate \
33- --jq '.[] | [.sha, (.committer.login // "")] | join(",")' \
34- > /tmp/commits_with_committer.csv
35-
36- commit_count=$(wc -l < /tmp/commits_with_committer.csv | tr -d ' ')
37- echo "Found $commit_count commits in PR #$PR_NUMBER"
38- echo "commit-count=$commit_count" >> "$GITHUB_OUTPUT"
39-
40- if [[ $commit_count -eq 0 ]]; then
41- echo "❌ Unexpected: no commits to verify"
42- exit 1
43- fi
44-
4526 - name : " Sigscanner check"
4627 id : sigscanner
47- continue-on-error : true
4828 env :
4929 SIGSCANNER_URL : ${{ secrets.SIGSCANNER_URL }}
5030 SIGSCANNER_API_KEY : ${{ secrets.SIGSCANNER_API_KEY }}
51- COMMIT_COUNT : ${{ steps.fetch-commits.outputs.commit-count }}
5231 run : |
53- > /tmp/verified_commits.csv
54-
55- echo "🔎 Verifying $COMMIT_COUNT commits"
56-
57- # Loop through all the commits
58- # For each commit, query Sigscanner with retry to check if it's verified
59- # Verified commit hashes with committer username are saved to /tmp/verified_commits.csv
60- while IFS=, read -r commit_sha committer_username; do
61- [[ -z "$commit_sha" ]] && continue
62-
63- commit_is_verified=false
64- request_attempt=1
65-
66- while [[ $request_attempt -le $VERIFY_MAX_ATTEMPTS ]]; do
67- response=$(curl -s --max-time 20 -G \
68- -H "X-SIGSCANNER-SECRET: $SIGSCANNER_API_KEY" \
69- --data-urlencode "commit=$commit_sha" \
70- --data-urlencode "repository=$REPOSITORY" \
71- --data-urlencode "author=$committer_username" \
72- "$SIGSCANNER_URL")
73-
74- res_verified=$(echo "$response" | jq -r '.verified')
75- res_error=$(echo "$response" | jq -r '.error')
76-
77- if [[ "$res_verified" == "true" ]]; then
78- commit_is_verified=true
79- break
80- elif [[ "$res_error" == "null" || "$res_error" == "" ]]; then
81- # This means the commit is explicitly unverified and shouldn't be retried
82- break
32+ echo "Verifying $PR_COMMIT_COUNT commits..."
33+ request_attempt=1
34+ total_start=$SECONDS
35+ while [[ $request_attempt -le $VERIFY_MAX_ATTEMPTS ]]; do
36+ echo "::group::Attempt $request_attempt/$VERIFY_MAX_ATTEMPTS — calling Sigscanner API..."
37+ attempt_start=$SECONDS
38+
39+ http_code=$(curl -s -o /tmp/sigscanner_response --max-time 300 -w '%{http_code}' -G \
40+ -H "X-SIGSCANNER-SECRET: $SIGSCANNER_API_KEY" \
41+ --data-urlencode "pr=$PR_NUMBER" \
42+ --data-urlencode "repository=$REPOSITORY" \
43+ "$SIGSCANNER_URL")
44+ response=$(cat /tmp/sigscanner_response)
45+
46+ elapsed=$(( SECONDS - attempt_start ))
47+ echo "API responded in ${elapsed}s (HTTP $http_code)"
48+ echo "::endgroup::"
49+
50+ if [[ "$http_code" != "200" ]]; then
51+ echo "❌ Sigscanner API returned HTTP $http_code (attempt $request_attempt, ${elapsed}s)"
52+ 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."
53+ if [[ $request_attempt -lt $VERIFY_MAX_ATTEMPTS ]]; then
54+ echo "⏳ Retrying in 15s..."
55+ sleep 15
8356 fi
84-
85- [[ $request_attempt -lt $VERIFY_MAX_ATTEMPTS ]] && sleep 15
8657 request_attempt=$((request_attempt + 1))
87- done
88-
89- if [[ "$commit_is_verified" == "true" ]]; then
90- echo "✅ $commit_sha"
91- echo "$commit_sha,$committer_username" >> /tmp/verified_commits.csv
92- else
93- echo "❌ $commit_sha"
58+ continue
9459 fi
95- done < /tmp/commits_with_committer.csv
96-
97- verified_commit_count=$(wc -l < /tmp/verified_commits.csv | tr -d ' ')
98- echo "Verified: $verified_commit_count / $COMMIT_COUNT"
99-
100- if [[ $verified_commit_count -eq $COMMIT_COUNT ]]; then
101- echo "✅ All commits verified"
102- exit 0
103- fi
104-
105- echo "❌ Not all commits verified"
106- exit 1
107-
108- - name : " Sigscanner fallback check"
109- if : ${{ steps.sigscanner.outcome == 'failure' }}
110- env :
111- API_TOKEN : ${{ secrets.SIGSCANNER_API_TOKEN }}
112- API_URL : ${{ secrets.SIGSCANNER_API_URL }}
113- COMMIT_COUNT : ${{ steps.fetch-commits.outputs.commit-count }}
114- run : |
115- touch /tmp/verified_commits.csv
116-
117- # Extract commits failed to verify earlier by comparing the verified commits file
118- # with the full list of commits
119- grep -vxFf /tmp/verified_commits.csv /tmp/commits_with_committer.csv \
120- > /tmp/pending_commits.csv
121-
122- pending_commit_count=$(wc -l < /tmp/pending_commits.csv | tr -d ' ')
123-
124- if [[ $pending_commit_count -eq 0 ]]; then
125- echo "✅ All commits verified"
126- exit 0
127- fi
128-
129- echo "🔎 Fallback: verifying $pending_commit_count remaining commits"
130-
131- # Loop through all the commits again with retry with the fallback API
132- while IFS=, read -r commit_sha committer_username; do
133- [[ -z "$commit_sha" ]] && continue
134-
135- commit_is_verified=false
136- request_attempt=1
137-
138- while [[ $request_attempt -le $VERIFY_MAX_ATTEMPTS ]]; do
139- body=$(jq -n \
140- --arg commit "$commit_sha" \
141- --arg repository "$REPOSITORY" \
142- --arg author "$committer_username" \
143- '{commit: $commit, repository: $repository, author: $author}')
144-
145- http_status=$(curl --silent --output /dev/null --write-out '%{http_code}' \
146- --max-time 20 -X POST \
147- -H "Content-Type: application/json" \
148- -H "Authorization: $API_TOKEN" \
149- --url "$API_URL" \
150- --data "$body")
151-
152- case $http_status in
153- 200)
154- commit_is_verified=true
155- break
156- ;;
157- 400)
158- echo "❌ $commit_sha - Bad request"
159- break
160- ;;
161- 403) break ;;
162- 5??)
163- [[ $request_attempt -lt $VERIFY_MAX_ATTEMPTS ]] && sleep 15
164- ;;
165- *)
166- echo "❌ $commit_sha - Unexpected: $http_status"
167- break
168- ;;
169- esac
17060
61+ if ! echo "$response" | jq empty >/dev/null 2>&1; then
62+ echo "❌ HTTP 200 but body is not valid JSON (attempt $request_attempt, ${elapsed}s)"
63+ echo
64+ if [[ $request_attempt -lt $VERIFY_MAX_ATTEMPTS ]]; then
65+ echo "⏳ Retrying in 15s..."
66+ sleep 15
67+ fi
17168 request_attempt=$((request_attempt + 1))
172- done
69+ continue
70+ fi
17371
174- if [[ "$commit_is_verified" == "true" ]]; then
175- echo "✅ $commit_sha"
176- echo "$commit_sha,$committer_username" >> /tmp/verified_commits.csv
72+ res_verified=$(echo "$response" | jq -r '.verified')
73+ res_error=$(echo "$response" | jq -r '.error')
74+
75+ if [[ "$res_verified" == "true" ]]; then
76+ echo "✅ All commits verified"
77+ exit 0
78+ elif [[ "$(echo "$response" | jq '(.unverified_commits // []) | length > 0')" == "true" ]]; then
79+ # Non-empty unverified_commits: definitive result, do not retry
80+ echo "❌ Unverified commits:"
81+ echo "$response" | jq -r '.unverified_commits[] | " - \(.)"'
82+ break
17783 else
178- echo "❌ $commit_sha "
84+ echo "❌ Error: $res_error "
17985 fi
180- done < /tmp/pending_commits.csv
181-
182- total_verified_count=$(wc -l < /tmp/verified_commits.csv | tr -d ' ')
183- echo "Verified: $total_verified_count / $COMMIT_COUNT"
18486
185- if [[ $total_verified_count -ne $COMMIT_COUNT ]]; then
186- echo "❌ Not all commits verified by fallback"
187- exit 1
188- fi
87+ if [[ $request_attempt -lt $VERIFY_MAX_ATTEMPTS ]]; then
88+ echo "⏳ Retrying in 15s..."
89+ sleep 15
90+ fi
91+ request_attempt=$((request_attempt + 1))
92+ done
18993
190- echo "✅ All commits verified"
94+ total_elapsed=$(( SECONDS - total_start ))
95+ echo "❌ Not all commits verified (total time: ${total_elapsed}s)"
96+ exit 1
0 commit comments