Skip to content

Freshness, mirror lists #2

Freshness, mirror lists

Freshness, mirror lists #2

name: Freshness, mirror lists
# Mirrors go away. A list that was healthy when it was generated decays, and the
# riscv64 outage started with a list holding one server.
#
# ⚠ This does not run daily, and must not. Upstream mirror ranking tools decline
# to ship a timer for this because of the load it puts on mirrors, and that
# reasoning applies here. Monthly, opening a pull request.
#
# The candidate pools in mirrors/*.pool are hand maintained and nothing else
# watches it, so a pool entry that stops answering is reported here too.
on:
workflow_dispatch:
schedule:
- cron: "45 03 1 * *" # the 1st of each month, 03:45 UTC
defaults:
run:
shell: bash
permissions:
contents: read
concurrency:
group: freshness-mirrors
cancel-in-progress: false
jobs:
regenerate:
name: Regenerate and test the mirror lists
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Measure the lists as they stand
id: before
run: |
set -euo pipefail
# The probe is the same one the static suite uses, so before and after
# are measured with one instrument.
total=0
for a in amd64 arm64 armv7 loong64 riscv64 ppc ppc64 ppc64le; do
n="$(awk '/^Server/ { c++ } END { print c + 0 }' "rootfs/$a/etc/pacman.d/mirrorlist")"
echo "$a: $n servers"
total=$((total + n))
done
echo "servers_before=$total" >> "$GITHUB_OUTPUT"
# A pool entry that no longer answers is invisible until a regeneration
# drops it, so it is counted explicitly. Every hand maintained pool is
# probed, and the architecture each belongs to is read from that
# architecture's own pacman.conf, so a new port's pool needs no edit
# here.
dead=0
for pool in mirrors/*.pool; do
[ -e "$pool" ] || continue
d="${pool#mirrors/}"
d="${d%.pool}"
pacman_arch="$(awk -F= '/^[[:space:]]*Architecture[[:space:]]*=/ { gsub(/[[:space:]]/, "", $2); print $2; exit }' "rootfs/$d/etc/pacman.conf")"
while IFS= read -r s; do
[ -n "$s" ] || continue
url="${s//\$repo/core}"
url="${url//\$arch/$pacman_arch}"
code="$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 10 --max-time 30 -L "$url/core.db")"
echo " pool $d $code $s"
if [ "$code" != "200" ]; then dead=$((dead + 1)); fi
done < <(awk '/^[[:space:]]*Server[[:space:]]*=/ { sub(/^[^=]*=[[:space:]]*/, ""); print }' "$pool")
done
echo "pool_dead=$dead" >> "$GITHUB_OUTPUT"
echo "::notice::the hand maintained pools have $dead entries that do not answer"
- name: Regenerate
id: gen
run: |
set -euo pipefail
branch="freshness/mirrors-$(date -u +%Y%m%d)"
git switch -c "$branch"
echo "branch=$branch" >> "$GITHUB_OUTPUT"
scripts/gen-mirrorlist all
total=0
for a in amd64 arm64 armv7 loong64 riscv64 ppc ppc64 ppc64le; do
n="$(awk '/^Server/ { c++ } END { print c + 0 }' "rootfs/$a/etc/pacman.d/mirrorlist")"
echo "$a: $n servers"
total=$((total + n))
done
echo "servers_after=$total" >> "$GITHUB_OUTPUT"
if git diff --quiet -- rootfs; then
echo "changed=false" >> "$GITHUB_OUTPUT"
echo "::notice::the regenerated lists are identical, nothing to open"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
git diff --stat -- rootfs
fi
- name: Test the regenerated lists
if: steps.gen.outputs.changed == 'true'
run: |
set -euo pipefail
# The mirror test is the one that matters here, and the rest of the
# static suite guards against a regeneration corrupting a config.
tests/run.sh static
- name: Open a pull request carrying the measurement
if: steps.gen.outputs.changed == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: ${{ steps.gen.outputs.branch }}
BEFORE: ${{ steps.before.outputs.servers_before }}
AFTER: ${{ steps.gen.outputs.servers_after }}
POOL_DEAD: ${{ steps.before.outputs.pool_dead }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add rootfs
git commit -m "mirrors: regenerate the mirror lists"
git push --set-upstream origin "$BRANCH"
# The printf formats are markdown. Backticks inside them are literal
# text for the pull request body, not shell expansions.
# shellcheck disable=SC2016
{
printf '## What moved\n\n'
printf 'Regenerated by `scripts/gen-mirrorlist all`. Every server written\n'
printf 'answered 200 for `core.db` at generation time.\n\n'
printf '| | before | after |\n'
printf '| --- | --- | --- |\n'
printf '| active servers, every list | %s | %s |\n\n' "$BEFORE" "$AFTER"
printf 'Per architecture, after regeneration:\n\n'
for a in amd64 arm64 armv7 loong64 riscv64 ppc ppc64 ppc64le; do
printf -- '- `%s`: %s servers\n' "$a" "$(awk '/^Server/ { c++ } END { print c + 0 }' "rootfs/$a/etc/pacman.d/mirrorlist")"
done
printf '\nThe hand maintained pools under `mirrors/` had **%s** entries between\n' "$POOL_DEAD"
printf 'them that did not answer when this ran. Nothing else watches those files.\n\n'
printf '## What the tests did\n\n'
printf '`tests/run.sh static`, the whole suite, including\n'
printf '`40-mirrors-reachable.sh` which probes every written entry again from\n'
printf 'the runner rather than trusting the generator.\n\n'
printf '## ⚠ What this job did NOT verify\n\n'
printf -- '- **No image was built.** A mirror can serve `core.db` and still fail\n'
printf ' mid transfer on a package.\n'
printf -- '- **Ranking is by connection time from one runner in one region.** The\n'
printf ' order suits that runner, not every consumer.\n'
printf -- '- **Anchors are not ranked and are never dropped.** If an anchor is down\n'
printf ' the generator refuses to write rather than quietly removing it.\n'
printf -- '- **The pools were probed, not repaired.** A dead entry stays in its\n'
printf ' `mirrors/*.pool` file until someone edits it.\n'
printf -- '- **https availability was tested per host, once.** A host that answers\n'
printf ' https intermittently may be written as either.\n'
printf -- '- **This pull request carries no status check.** A pull request opened\n'
printf ' with the built-in token has its run held at `action_required` until a\n'
printf ' human approves it. The tree was tested inside run `%s`, linked above.\n' "$GITHUB_RUN_ID"
printf ' See `HISTORY/maintainer-actions.md` section 4.\n'
} > /tmp/body.md
gh pr create --base main --head "$BRANCH" \
--title "mirrors: regenerate the mirror lists" --body-file /tmp/body.md