deploy #34
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
| # Scheduled rebuild and deploy. | |
| # | |
| # This site is a READER of two registries it does not own. awesome-dsh-plugins | |
| # refreshes its star counts four times a day and lands triaged entries whenever | |
| # a maintainer merges; awesome-dsh-themes moves on its own schedule. Nothing in | |
| # either repo can push here. | |
| # | |
| # Before this workflow existed the site only moved when a human ran the three | |
| # commands below, so it drifted from the registries silently while continuing | |
| # to publish a `built:` date on /api/plugins — a site whose entire argument is | |
| # that a number should say where it came from and when. A stale number that | |
| # looks fresh is the exact failure it exists to criticise. | |
| # | |
| # Runs after the registry's own 02:17 UTC sweep has had time to land. | |
| # | |
| # Setup: repo Settings > Secrets and variables > Actions needs | |
| # CLOUDFLARE_API_TOKEN (Workers Scripts: Edit) and CLOUDFLARE_ACCOUNT_ID. | |
| # Without them the deploy step fails loudly rather than skipping quietly. | |
| name: deploy | |
| on: | |
| schedule: | |
| # Daily, an hour after the registry's first sweep of the day. | |
| - cron: "42 3 * * *" | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| # A push and the nightly tick can collide. Never deploy two builds at once; | |
| # let the newer one win, because both read the same upstream registries. | |
| group: deploy | |
| cancel-in-progress: true | |
| jobs: | |
| deploy: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # Fail here rather than after a four-minute build, and fail with the | |
| # command that fixes it. A workflow whose first bad day costs you a build | |
| # and a stack trace teaches you nothing. | |
| - name: Preflight — the deploy credentials exist | |
| env: | |
| TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| ACCOUNT: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| run: | | |
| missing="" | |
| [ -n "$TOKEN" ] || missing="$missing CLOUDFLARE_API_TOKEN" | |
| [ -n "$ACCOUNT" ] || missing="$missing CLOUDFLARE_ACCOUNT_ID" | |
| if [ -n "$missing" ]; then | |
| echo "::error::missing repository secret(s):$missing" | |
| echo "set them with:" | |
| for s in $missing; do | |
| echo " gh secret set $s --repo $GITHUB_REPOSITORY" | |
| done | |
| echo "the token needs the Workers Scripts: Edit permission on this account." | |
| exit 1 | |
| fi | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - run: npm ci | |
| # The two censuses the front page argues from. Neither was in this job, | |
| # so `npm run data` refreshed the registry projection every night while | |
| # data/ecosystem.json stayed frozen at the day a human last ran the | |
| # script -- twelve days, over which the topic grew from 9,016 repos to | |
| # 13,007 and the page went on printing the smaller number under a fresh | |
| # `built:` date. Exactly the failure the header of this file names. | |
| # | |
| # continue-on-error, because a bad day at the GitHub search API should | |
| # not block a deploy of otherwise-current data. scripts/check-claims.mjs | |
| # turns the build red once either census passes 14 days, which is the | |
| # difference between one flaky run and a step that has quietly stopped. | |
| - name: Re-measure the ecosystem and the seams | |
| continue-on-error: true | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| node scripts/measure-ecosystem.mjs | |
| node scripts/measure-seams.mjs | |
| # Reads registry.npmjs.org only, and its subject moves whenever dsh | |
| # publishes: a census against yesterday's `latest` is not stale by | |
| # date and is still wrong. check-claims fails the build when this | |
| # file and the ecosystem census disagree about what npx installs. | |
| node scripts/measure-installability.mjs | |
| # Fetches both registries over the network. DATA_SOURCE is deliberately | |
| # unset: a `local` build publishes `source: "local checkout"` on | |
| # /api/plugins, and production said exactly that once already. | |
| - name: Build the data projection | |
| run: npm run data | |
| - name: Check what the build is about to publish | |
| run: | | |
| set -euo pipefail | |
| node -e ' | |
| const m = require("./public/_data/meta.json"); | |
| const n = m.counts.plugins; | |
| console.log(`source=${m.source} plugins=${n} built=${m.built} verifiedAgainst=${m.verifiedAgainst}`); | |
| if (m.source !== "dshworks registries") { | |
| throw new Error(`refusing to deploy a build that says source=${m.source}`); | |
| } | |
| if (!(n > 1000)) { | |
| throw new Error(`refusing to deploy ${n} plugins; the registry fetch probably failed`); | |
| } | |
| ' | |
| # vinext build bakes the routes into dist/server/wrangler.json and | |
| # .wrangler/deploy/config.json points wrangler there, so the build must | |
| # run before the deploy or wrangler.jsonc edits are ignored in silence. | |
| # npm run check:claims, not `npm run deploy`: the deploy script re-runs | |
| # `npm run data` and this job has already done it above. | |
| - name: Check the copy against the data | |
| run: npm run check:claims | |
| - name: Build and deploy | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| run: npx vinext deploy | |
| - name: Verify the deploy from outside | |
| run: | | |
| set -euo pipefail | |
| sleep 15 | |
| for path in / /sponsor /api/plugins; do | |
| code=$(curl -s -o /dev/null -w '%{http_code}' "https://dsh.works${path}") | |
| echo "${path} -> ${code}" | |
| [ "$code" = "200" ] || exit 1 | |
| done | |
| # The published meta must match what we just built, not a cached | |
| # earlier deploy. `source` is the field that catches a local build. | |
| curl -s https://dsh.works/api/plugins \ | |
| | node -e ' | |
| let s=""; process.stdin.on("data",d=>s+=d).on("end",()=>{ | |
| const d=JSON.parse(s); | |
| console.log(`live: source=${d.source} plugins=${d.counts.plugins} built=${d.built}`); | |
| if (d.source !== "dshworks registries") process.exit(1); | |
| });' |