Repository navigation
180 lines (161 loc) · 6.97 KB
/
Copy pathbook.yml
File metadata and controls
180 lines (161 loc) · 6.97 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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
name: Book
# Every push and pull request: render the book (re-running only the code of
# chapters that changed, thanks to the committed _freeze/ folder), check that
# every old page survives in its chapter and every redirect lands on a real
# page, check internal links (including section anchors), and deploy to
# Netlify (a preview for pull requests, production for master).
#
# Monthly: re-run the code of every chapter from scratch and check external
# links too, so tutorials broken by package updates or link rot get noticed.
# Problems are reported in a GitHub issue.
on:
push:
branches: [master]
pull_request:
schedule:
- cron: "0 3 1 * *"
workflow_dispatch:
permissions:
contents: read
issues: write
pull-requests: write
statuses: write
# a newer push supersedes an older one, but scheduled and manual runs (also on
# master) get their own group, so a merge doesn't cancel the monthly full run
# and a manual run doesn't cancel a deploy
concurrency:
group: book-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
jobs:
book:
runs-on: ubuntu-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
FULL_RUN: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4
- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true
- uses: r-lib/actions/setup-r-dependencies@v2
with:
dependencies: '"hard"'
# the runner image usually ships a recent Quarto; installing a pinned
# older one fails (apt refuses to downgrade), so only install if missing
- name: Check for Quarto
id: quarto
run: |
if command -v quarto >/dev/null; then
echo "found=true" >> "$GITHUB_OUTPUT"
fi
- uses: quarto-dev/quarto-actions/setup@v2
if: steps.quarto.outputs.found != 'true'
- name: Quarto version
run: quarto --version
- name: Forget saved results (monthly full run)
if: env.FULL_RUN == 'true'
run: rm -rf _freeze
- name: Render
run: quarto render
- name: Find code that errors
# chunks run with `error: true`, so a broken tutorial still renders,
# showing the error; report them and block deployment
run: |
grep -rl 'cell-output-error' _book --include='*.html' \
| sed 's|^_book/||' > chunk-errors.txt || true
if [ -s chunk-errors.txt ]; then
echo "### Chapters with code that errors" >> "$GITHUB_STEP_SUMMARY"
sed 's/^/- /' chunk-errors.txt >> "$GITHUB_STEP_SUMMARY"
echo "::error::Tutorial code errors found; see the job summary"
exit 1
fi
# every old Hugo/WordPress address in _redirects must still land on a
# real chapter and section; fix failures by editing _redirects
- name: Check redirects
run: python3 migration/check_redirects.py
- name: Check links
id: links
uses: lycheeverse/lychee-action@v2
with:
# 403/429: sites that refuse automated checkers (Stack Overflow,
# Wiley, ScienceDirect, GBIF, doi.org) but work in a browser.
# .lycheeignore lists links we can't fix (generated by packages).
# --include-fragments also checks that #section anchors exist.
args: >-
--root-dir ${{ github.workspace }}/_book
--no-progress
--include-fragments
--accept '200..=299,403,429'
${{ env.FULL_RUN != 'true' && '--offline' || '' }}
'_book/**/*.html'
output: lychee-report.md
fail: false
- name: Fail on broken internal links
if: env.FULL_RUN != 'true' && steps.links.outputs.exit_code != 0
run: |
cat lychee-report.md >> "$GITHUB_STEP_SUMMARY"
exit 1
# without these the deploy step below skips silently and still passes,
# so the live site would stop updating unnoticed; only checked on
# master, because pull requests from forks never get secrets
- name: Check Netlify credentials
if: github.event_name == 'push'
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
run: |
if [ -z "$NETLIFY_AUTH_TOKEN" ] || [ -z "$NETLIFY_SITE_ID" ]; then
echo "::error::NETLIFY_AUTH_TOKEN or NETLIFY_SITE_ID repository secret is missing; the site was not deployed"
exit 1
fi
- name: Deploy to Netlify
if: github.event_name == 'push' || github.event_name == 'pull_request'
uses: nwtgck/actions-netlify@v3
with:
publish-dir: ./_book
production-branch: master
production-deploy: ${{ github.ref == 'refs/heads/master' }}
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: ${{ github.event.pull_request.title || github.event.head_commit.message }}
enable-pull-request-comment: true
enable-commit-comment: false
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
- name: Report problems found by the monthly run
# always(): a failed render is the main thing this run is for; the
# script below decides whether there is anything to report
if: always() && env.FULL_RUN == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
run: |
# a step that failed (installing packages, rendering) stops the
# later ones, so their outputs may be missing
failed=${{ job.status != 'success' }}
links_broken=${{ steps.links.outputs.exit_code != '' && steps.links.outputs.exit_code != '0' }}
if [ -s chunk-errors.txt ]; then chunk_errors=true; else chunk_errors=false; fi
if [ "$failed$links_broken$chunk_errors" = falsefalsefalse ]; then
exit 0
fi
{
echo "The monthly re-run of every tutorial found problems."
echo "Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
echo
if [ "$failed" = true ]; then
echo "## The build failed"
echo "A step of the run failed (for example installing packages or rendering), so later checks may not have run. See the run log."
echo
fi
if [ "$chunk_errors" = true ]; then
echo "## Chapters with code that errors"
sed 's/^/- [ ] /' chunk-errors.txt
echo
fi
if [ "$links_broken" = true ]; then
echo "## Broken links"
cat lychee-report.md
fi
} > issue.md
gh issue create --title "Monthly check: broken tutorials or links ($(date +%Y-%m))" \
--body-file issue.md