-
Notifications
You must be signed in to change notification settings - Fork 0
166 lines (149 loc) · 7.21 KB
/
Copy pathweekly-maintenance.yml
File metadata and controls
166 lines (149 loc) · 7.21 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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
name: Weekly maintenance
# Two chores that are TIME-driven rather than commit-driven, which is exactly why
# neither was happening: a pre-commit hook and a push-triggered workflow can only ever
# catch what a commit introduces.
#
# 1. dateModified drift. gen-page-dates.js computes it, prints it, and deliberately
# does not gate on it — the reason is in that file: `modified` goes stale the
# moment a page is committed, so failing on it would make every commit block the
# next one through the pre-commit hook. So drift was repaired by hand, once
# (commit f56a330, "re-date the pages whose source had moved past their
# dateModified"). A human happening to notice is not a process, and a docs page
# advertising a stale dateModified is a freshness signal working against us.
#
# 2. External link rot. `check:links:external` existed for months with nothing
# running it anywhere — the only hit for it across .github/, .githooks/ and
# package.json was its own definition. It was also BROKEN, which is why nobody
# missed it: check-links.js dialled every URL over plain http, including https
# ones, and had no request timeout at all. Both fixed in the same change as this
# file; see the comment above `request()`. scripts/external-allowlist.txt keeps
# the expected failures (npmjs 403ing an unattended HEAD, and friends) from
# making the job cry wolf.
#
# Both are Monday-morning jobs, deliberately separate: one commits, the other opens an
# issue, and a failure in either must not hide the other.
on:
schedule:
# 06:20 UTC Monday. Off the hour, because the hour is when everyone else's cron
# runs and GitHub queues them.
- cron: "20 6 * * 1"
workflow_dispatch:
concurrency:
group: weekly-maintenance
cancel-in-progress: false
jobs:
dates:
name: Repair dateModified drift
runs-on: ubuntu-latest
# A scheduled job that hangs burns quota until GitHub's 6-hour default kills it.
timeout-minutes: 20
permissions:
contents: write
steps:
- uses: actions/checkout@v7
with:
# REQUIRED. gen-page-dates.js reads git history for every page
# (`git log --follow --diff-filter=A`); with the default shallow fetch every
# file looks as though one commit added it. Same reason refresh-api-docs.yml
# and sync-cli-guide.yml set it.
fetch-depth: 0
# REQUIRED by the `npm test` step below: the search ranker is a submodule
# at vendor/search-ranker, and the build throws without it. See checks.yml.
submodules: true
- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
- run: npm ci
- name: Rewrite only the dateModified values
id: fix
run: |
node scripts/gen-page-dates.js --fix-modified | tee /tmp/fix.log
if [ -z "$(git status --porcelain -- src/_data/pageDates.json)" ]; then
echo "changed=false" >> "$GITHUB_OUTPUT"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
# A GITHUB_TOKEN push does not trigger other workflows, so checks.yml will NOT
# run for the commit this job creates, and Cloudflare Pages deploys from the push
# itself. The gate has to run here or not at all — the same reasoning
# refresh-api-docs.yml and sync-cli-guide.yml carry.
- name: Verify
if: steps.fix.outputs.changed == 'true'
run: npm test
- name: Commit & push
id: commit
if: steps.fix.outputs.changed == 'true'
run: |
# An explicit path, not `git add -A`: this repo's working tree also holds
# untracked local-only material (promotion/, the *-PLAN.md notes) that must
# never be committed by a bot.
git config user.name "imqueue-bot"
git config user.email "bot@imqueue.com"
git add -- src/_data/pageDates.json
git commit -m "chore(dates): refresh dateModified for pages whose source moved
Rewritten by scripts/gen-page-dates.js --fix-modified. Publication dates and
the key set are untouched; only \`modified\` values changed.
$(cat /tmp/fix.log)"
git push
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
# The point of repairing the date is that something re-reads it. Diff-scoped, so
# this submits the handful of URLs whose lastmod actually moved rather than the
# whole sitemap.
- name: IndexNow — the URLs whose lastmod moved
if: steps.commit.outputs.sha != ''
run: |
urls=$(node scripts/changed-urls.js org "${{ steps.commit.outputs.sha }}^..${{ steps.commit.outputs.sha }}")
if [ -z "$urls" ]; then
echo "pageDates.json changed but no page URL did — nothing to submit."
exit 0
fi
# Unquoted on purpose: one word per URL.
node scripts/indexnow-ping.js org $urls || true
link-rot:
name: External link rot
runs-on: ubuntu-latest
# Measured: 1m13s for the full run (crawl + 75 unique external HEADs). The
# per-request timeout and the result memo are what make that true; this is the belt
# to their braces, because a scheduled job that hangs burns quota silently.
timeout-minutes: 25
permissions:
# To open/update the tracking issue below.
issues: write
steps:
- uses: actions/checkout@v7
with:
# REQUIRED: check:links:external builds both editions, and the build throws
# without the vendor/search-ranker submodule. See checks.yml.
submodules: true
- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
- run: npm ci
- name: Check external links
id: links
# Never fails the workflow. Link rot is somebody else's deploy, so a red X here
# would be noise on a schedule nobody triggered — the issue below is the
# signal, and it is one issue, updated, rather than a new one every Monday.
continue-on-error: true
run: |
set +e
npm run check:links:external > /tmp/links.log 2>&1
echo "status=$?" >> "$GITHUB_OUTPUT"
tail -40 /tmp/links.log
- name: Open or update the tracking issue
if: steps.links.outputs.status != '0'
env:
GH_TOKEN: ${{ github.token }}
run: |
body=$(printf 'The weekly external link check found unreachable links.\n\nAllowed failures live in `scripts/external-allowlist.txt` — if one of these is a link that works in a browser and only fails a scripted HEAD, add it there with the reason rather than removing the link.\n\n```\n%s\n```\n\nRun: `%s`\n' \
"$(grep -iE 'broken|external HTTP' /tmp/links.log | head -40)" \
"npm run check:links:external")
existing=$(gh issue list --state open --label link-rot --limit 1 --json number --jq '.[0].number')
if [ -n "$existing" ]; then
gh issue comment "$existing" --body "$body"
else
gh label create link-rot --description "Weekly external link check" --color ededed 2>/dev/null || true
gh issue create --title "External link rot" --label link-rot --body "$body"
fi