Skip to content

Atlas refresh

Atlas refresh #13

Workflow file for this run

name: Atlas refresh
# Regenerates the road-conditions atlas + the vendored catalog snapshots on a
# schedule. The atlas is ~83% live-resolved feeds (WZDx registry + Autobahn
# index), so it is NON-deterministic and network-bound — it must NOT be a git
# hook (a pre-commit/pre-push drift-check would flap on every upstream change).
# A weekly scheduled run keeps the public commons snapshot and the bundled
# runtime fallback fresh, opening a PR only when something actually changed.
on:
schedule:
- cron: "15 5 * * 3" # Wednesdays 05:15 UTC (staggered off the Monday liveness/link jobs)
workflow_dispatch:
concurrency:
group: atlas-refresh
cancel-in-progress: false
permissions:
contents: read
jobs:
refresh:
name: regenerate the atlas + catalog snapshots
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write # push the refresh branch
pull-requests: write # open the refresh PR
env:
# Don't run the local Husky git hooks (pre-push runs the full test suite) when
# the create-pull-request action makes its internal commit/push — CI already
# built the packages, and a data-only refresh shouldn't re-run the suite here.
HUSKY: "0"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .nvmrc
cache: pnpm
- run: pnpm install --frozen-lockfile
# Build so export-atlas can import the compiled @openconditions/roads barrel.
- run: pnpm build
# Snapshot the resolved-feed counts BEFORE regenerating, so the guard below
# can detect an abnormal drop (a broken resolver or an upstream outage).
- name: Baseline resolved-feed counts
id: baseline
run: |
node -e "const a=JSON.parse(require('fs').readFileSync('packages/roads/atlas/roads.json5','utf8'));const c=p=>a.filter(f=>String(f.id).startsWith(p)).length;console.log('wzdx='+c('wzdx-'));console.log('autobahn='+c('autobahn-'))" >> "$GITHUB_OUTPUT"
# Live-resolve the WZDx registry + Autobahn index (both public, no secrets),
# rewriting atlas/roads.json5 and the vendored snapshots. resolveWithSnapshot
# falls back to the committed snapshot on a transient upstream failure.
- name: Regenerate atlas + snapshots
run: pnpm --filter @openconditions/roads export:atlas
# The atlas is built from untrusted third-party registries and CI does not
# validate its content — so refuse to open/merge a refresh that abnormally
# loses feeds (below an absolute floor, or >30% under the pre-run baseline).
# A tripped guard fails the job (no PR, no auto-merge) so a human investigates.
- name: Guard against an abnormal feed drop
env:
OLD_WZDX: ${{ steps.baseline.outputs.wzdx }}
OLD_AUTOBAHN: ${{ steps.baseline.outputs.autobahn }}
run: |
counts=$(node -e "const a=JSON.parse(require('fs').readFileSync('packages/roads/atlas/roads.json5','utf8'));const c=p=>a.filter(f=>String(f.id).startsWith(p)).length;console.log(c('wzdx-')+' '+c('autobahn-'))")
NEW_WZDX=${counts% *}; NEW_AUTOBAHN=${counts#* }
echo "wzdx: ${OLD_WZDX} -> ${NEW_WZDX} | autobahn: ${OLD_AUTOBAHN} -> ${NEW_AUTOBAHN}"
fail=0
if [ "$NEW_WZDX" -lt 10 ]; then echo "::error::WZDx feeds ${NEW_WZDX} below floor 10"; fail=1; fi
if [ "$NEW_AUTOBAHN" -lt 100 ]; then echo "::error::Autobahn feeds ${NEW_AUTOBAHN} below floor 100"; fail=1; fi
if [ "${OLD_WZDX:-0}" -gt 0 ] && [ "$NEW_WZDX" -lt $((OLD_WZDX * 70 / 100)) ]; then echo "::error::WZDx dropped >30% (${OLD_WZDX} -> ${NEW_WZDX})"; fail=1; fi
if [ "${OLD_AUTOBAHN:-0}" -gt 0 ] && [ "$NEW_AUTOBAHN" -lt $((OLD_AUTOBAHN * 70 / 100)) ]; then echo "::error::Autobahn dropped >30% (${OLD_AUTOBAHN} -> ${NEW_AUTOBAHN})"; fail=1; fi
if [ "$fail" -ne 0 ]; then
echo "Abnormal feed drop — refusing to open a refresh PR. Check the resolvers / upstream registries."
exit 1
fi
echo "Feed counts healthy."
# Opens/updates a PR ONLY when the atlas or a snapshot actually changed; a
# no-op run creates no PR. Uses a PAT if provided (so required CI runs on the
# PR and it can auto-merge); otherwise the default token (PR opens, merge by hand).
- name: Open a refresh PR if the atlas changed
id: cpr
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ secrets.ATLAS_PAT || secrets.GITHUB_TOKEN }}
add-paths: |
packages/roads/atlas/roads.json5
packages/roads/src/catalog/snapshots/*.json
branch: chore/atlas-refresh
delete-branch: true
commit-message: "chore(roads): refresh road-conditions atlas + catalog snapshots"
title: "chore(roads): weekly atlas + catalog-snapshot refresh"
body: |
Automated weekly regeneration of `packages/roads/atlas/roads.json5` and the
vendored catalog snapshots (`packages/roads/src/catalog/snapshots/*.json`)
from the live WZDx registry + Autobahn index. Feed counts passed the drop guard.
Generated artifact — auto-merges once required CI is green. The refreshed
snapshots reach production on the next image build/deploy.
labels: automated, atlas
# Auto-merge a healthy refresh once required CI passes (the drop guard already
# ran; a PAT-authored PR triggers the required checks so this is protected).
- name: Enable auto-merge on the refresh PR
if: steps.cpr.outputs.pull-request-number
env:
GH_TOKEN: ${{ secrets.ATLAS_PAT || secrets.GITHUB_TOKEN }}
run: gh pr merge ${{ steps.cpr.outputs.pull-request-number }} --auto --squash --repo ${{ github.repository }}