Atlas refresh #13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 }} |