Skip to content

pull-mirrors: fix null mirror flags (rate-limited geoip) + show table in run summary - #956

Merged
igorpecovnik merged 6 commits into
mainfrom
fix/mirrors-flag-geoip
Aug 7, 2026
Merged

pull-mirrors: fix null mirror flags (rate-limited geoip) + show table in run summary#956
igorpecovnik merged 6 commits into
mainfrom
fix/mirrors-flag-geoip

Conversation

@igorpecovnik

@igorpecovnik igorpecovnik commented Aug 7, 2026

Copy link
Copy Markdown
Member

Why the flags broke

In PR #955 every flag in docs/Mirrors.md turned into https://flagsapi.com/null/shiny/32.png.

Root cause is in .github/workflows/pull-mirrors-from-db.yml:

SITE_FLAG=$(curl --max-time 5 -s http://ipwhois.app/json/"${IP}" | jq -r '.country_code')

ipwhois.app's free tier now rate-limits after ~2 requests and responds:

{"success":false,"message":"Rate limit exceeded","country_code":null}

The loop runs over ~40 mirrors with only sleep 1, so it trips the limit almost immediately and jq -r emits the literal null for nearly every mirror → flagsapi.com/null. (Reproduced: the 3rd rapid request already returns Rate limit exceeded.)

Fix

  • Switch to ip-api.com (free, 45 req/min, no key) which returns countryCode.
  • Retry + validate: up to 3 attempts on a transient/rate-limited response, and require a 2-letter code; otherwise leave it empty rather than emitting null.
  • Guard empty IP: ip-api.com/json/ with no address geolocates the runner — which would stamp a wrong flag on a mirror whose DNS didn't resolve. Now skipped.
  • Graceful cell: with no country resolved, render a plain region link instead of a broken flag image.
  • Run summary: append the rendered Current Mirrors table to $GITHUB_STEP_SUMMARY so it's visible on the workflow run.

Validation

  • YAML parses.
  • Live-tested the new lookup: 185.94.135.238 → IT, 65.21.120.247 → FI; empty IP → empty flag (fallback), no null, no runner-location leak.

Once merged, the next scheduled/dispatched run regenerates Mirrors.md with correct flags — superseding the bad auto-update in #955.

Create docs preview on PR

Documentation website preview will be available shortly:


 Open WWW preview 

Also: descriptive auto-PR title/body

While in this workflow, replaced the generated PR's uninformative title (Automatic documentation update) and body (Generate documentation.) with a title that names the refreshed file and a body describing what's regenerated (Current Mirrors table + rsync sizes, sourced from NetBox). This applies to future auto-update PRs like #955 / #957.

Create docs preview on PR

Documentation website preview will be available shortly:


 Open WWW preview 

Every flag in the generated Mirrors.md became flagsapi.com/null/... The
country code came from ipwhois.app, whose free tier now rate-limits after
~2 requests and returns {"success":false,"country_code":null}; the ~40-mirror
loop (sleep 1) tripped it almost immediately, so jq emitted the literal null.

- Switch the lookup to ip-api.com (free, 45 req/min, no key -> countryCode).
- Retry a few times on a transient/rate-limited response and validate a
  2-letter code; leave it empty rather than emitting "null".
- Guard the empty-IP case: ip-api.com/json/ with no address geolocates the
  runner itself, which would stamp a wrong flag on an unresolved mirror.
- When no country is resolved, render a plain region link instead of a broken
  flagsapi/null image.
- Also append the rendered Current Mirrors table to the workflow run summary.

Signed-off-by: Igor Pecovnik <igor@armbian.com>
@github-actions github-actions Bot added size/small PR with less then 50 lines Needs review Seeking for review GitHub Actions GitHub Actions code labels Aug 7, 2026
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@igorpecovnik, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 43a14181-26bc-4614-8903-b560d5e67cab

📥 Commits

Reviewing files that changed from the base of the PR and between 3ad1e6d and fc02e9b.

📒 Files selected for processing (2)
  • .github/workflows/pull-mirrors-from-db.yml
  • docs/Mirrors.md

Walkthrough

The workflow replaces ipwhois.app with retried and validated ip-api.com lookups. It skips lookups for empty IP values. Flag cells use flagsapi only when a valid country code exists and otherwise use a region link. The generated Current Mirrors table is appended to GITHUB_STEP_SUMMARY. Pull request metadata now describes mirror table and rsync-size regeneration.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the fixes for null mirror flags and the workflow run summary table.
Description check ✅ Passed The description directly explains the mirror flag fix, fallback behavior, run summary update, and generated PR metadata changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mirrors-flag-geoip

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

github-actions Bot pushed a commit that referenced this pull request Aug 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/pull-mirrors-from-db.yml (1)

98-98: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Avoid accepting cleartext geolocation response for the flag.

http://ip-api.com is the current free endpoint, but status and countryCode can be modified in cleartext before the two-letter validation. Use a more trusted secure source for the mirror country if any tampering can publish the wrong flag.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/pull-mirrors-from-db.yml at line 98, Update the GEO lookup
in the mirror-country flow to use a trusted HTTPS geolocation endpoint instead
of the cleartext ip-api.com URL, preserving the existing status and countryCode
response contract and downstream two-letter validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/pull-mirrors-from-db.yml:
- Around line 95-105: Move the decommissioning status check ahead of the
geolocation block in the mirror-processing flow, and only execute the IP lookup
when the mirror is not decommissioning. Preserve the existing SITE_FLAG
initialization and country-code validation for active mirrors, using the
existing status-check symbol visible nearby.

---

Nitpick comments:
In @.github/workflows/pull-mirrors-from-db.yml:
- Line 98: Update the GEO lookup in the mirror-country flow to use a trusted
HTTPS geolocation endpoint instead of the cleartext ip-api.com URL, preserving
the existing status and countryCode response contract and downstream two-letter
validation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 22b78db2-856e-43c0-ada5-2f2abd50a2b1

📥 Commits

Reviewing files that changed from the base of the PR and between d1d6374 and 9ab4968.

📒 Files selected for processing (1)
  • .github/workflows/pull-mirrors-from-db.yml

Comment thread .github/workflows/pull-mirrors-from-db.yml Outdated
The workflow opened every auto-update as "Automatic documentation update"
with the body "Generate documentation." - which says nothing about what
changed. Retitle it to name the file it refreshes and describe what gets
regenerated (Current Mirrors table + rsync sizes, sourced from NetBox), so
reviewers can see the intent at a glance.

Signed-off-by: Igor Pecovnik <igor@armbian.com>
@github-actions github-actions Bot added size/medium PR with more then 50 and less then 250 lines and removed size/small PR with less then 50 lines labels Aug 7, 2026
The decommissioning `continue` ran after dig + up to three ip-api lookups,
wasting network work and rate-limit budget on a mirror that's dropped anyway.
Move the check ahead of the IP/geolocation block so it short-circuits first;
SITE_FLAG init and country-code validation are unchanged for kept mirrors.

Signed-off-by: Igor Pecovnik <igor@armbian.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.github/workflows/pull-mirrors-from-db.yml (2)

97-105: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Enforce the ip-api.com rate limit across retries.

ip-api.com allows 45 requests/minute from one IP. This workflow makes up to 30 lookup attempts plus 40 post-mirror delays, so rapid or failed retries can exceed the limit. If throttled, later mirror flags can be dropped; cap retries and pace the batch to stay under the allowed interval.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/pull-mirrors-from-db.yml around lines 97 - 105, Update the
retry loop that performs the ip-api.com lookup to enforce the service rate
limit: cap the total lookup attempts and add sufficient pacing between requests,
including failed retries, so the full batch remains below 45 requests per
minute. Preserve the existing successful country-code validation and SITE_FLAG
behavior.

95-98: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Filter IP to an address before geolocation.

[[ -n "$IP" ]] also accepts valid DNS hostnames such as CNAME targets. The retry loop still runs three times for these values, then falls back to the plain-region flag because ip-api.com returns fail for non-IP queries. Use an IPv4/IPv6 match or an explicit address query before calling ip-api.com.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/pull-mirrors-from-db.yml around lines 95 - 98, Update the
geolocation guard around the retry loop in the workflow to proceed only when IP
contains a valid IPv4 or IPv6 address, not merely any non-empty hostname or
CNAME. Preserve the existing retry and fallback behavior, and skip the
ip-api.com request for non-address values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/workflows/pull-mirrors-from-db.yml:
- Around line 97-105: Update the retry loop that performs the ip-api.com lookup
to enforce the service rate limit: cap the total lookup attempts and add
sufficient pacing between requests, including failed retries, so the full batch
remains below 45 requests per minute. Preserve the existing successful
country-code validation and SITE_FLAG behavior.
- Around line 95-98: Update the geolocation guard around the retry loop in the
workflow to proceed only when IP contains a valid IPv4 or IPv6 address, not
merely any non-empty hostname or CNAME. Preserve the existing retry and fallback
behavior, and skip the ip-api.com request for non-address values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 42175b7f-5a58-4e55-a7f4-edb801772703

📥 Commits

Reviewing files that changed from the base of the PR and between 9ab4968 and 3ad1e6d.

📒 Files selected for processing (1)
  • .github/workflows/pull-mirrors-from-db.yml

igorpecovnik and others added 3 commits August 7, 2026 18:33
The GeoIP approach (ip-api.com, and ipwhois.app before it) left flags missing
under rate limits and was wrong for CDN-fronted mirrors (Hetzner geolocated to
FI, Macarne/Yandex to US). Use the site's human-maintained NetBox region slug
instead: deterministic, no rate limits, no external call. US mirrors live under
North America as coast regions, so those map to US; a region with no country
mapping (e.g. "Dynamic") renders flagless rather than wrong.

Verified against all active mirrors: every one resolves to the correct flag,
with only the intentionally country-less "Dynamic" mirror left flagless. Drops
the dig lookup and the per-row sleep that only paced the GeoIP calls.

Signed-off-by: Igor Pecovnik <igor@armbian.com>
- Flag image alt is now the country name (mapped from the ISO code) instead
  of the region label, so a US mirror under the "East coast" region reads as
  "United States", not "East coast".
- The "Dynamic" region is a Cloudflare-anycast mirror with no single country;
  render it with the UN "global" flag as an icon (flagsapi has no UN flag,
  flagcdn does) and a "Cloudflare anycast" alt, instead of a plain text link.

Signed-off-by: Igor Pecovnik <igor@armbian.com>
@igorpecovnik
igorpecovnik merged commit 5d78319 into main Aug 7, 2026
5 checks passed
@igorpecovnik
igorpecovnik deleted the fix/mirrors-flag-geoip branch August 7, 2026 16:42
github-actions Bot pushed a commit that referenced this pull request Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

GitHub Actions GitHub Actions code Needs review Seeking for review size/medium PR with more then 50 and less then 250 lines

Development

Successfully merging this pull request may close these issues.

1 participant