diff --git a/.github/workflows/sigscanner-check.yml b/.github/workflows/sigscanner-check.yml index 3b4caf9..3e96429 100644 --- a/.github/workflows/sigscanner-check.yml +++ b/.github/workflows/sigscanner-check.yml @@ -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: @@ -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: 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 \ + -H "X-SIGSCANNER-SECRET: $SIGSCANNER_API_KEY" \ + --data-urlencode "pr=$PR_NUMBER" \ + --data-urlencode "repository=$REPOSITORY" \ + "$SIGSCANNER_URL") + 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 + # 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