-
Notifications
You must be signed in to change notification settings - Fork 0
150 lines (136 loc) · 6.25 KB
/
Copy pathdeploy.yml
File metadata and controls
150 lines (136 loc) · 6.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# 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);
});'