Skip to content

Commit 52c286b

Browse files
committed
Merge branch 'add-automated-updates'
This topic adds several workflows to help keep https://git-scm.com/ up to date. Signed-off-by: Johannes Schindelin <[email protected]>
2 parents a4a8502 + 3a03504 commit 52c286b

File tree

9 files changed

+684
-0
lines changed

9 files changed

+684
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
name: 'Run Hugo/Pagefind and deploy to GitHub Pages'
2+
description: 'Runs Hugo and Pagefind and then deploys the result to GitHub Pages.'
3+
# This composite Action requires the following things in the calling workflow:
4+
#
5+
# permissions:
6+
# contents: write # to push changes (if any)
7+
# pages: write # to deploy to GitHub Pages
8+
# id-token: write # to verify that the deployment source is legit
9+
# environment:
10+
# name: github-pages
11+
# url: ${{ steps.<id-of-deployment-step>.outputs.url }}
12+
inputs:
13+
github-token:
14+
description: The GitHub token used to create an authenticated client
15+
default: ${{ github.token }}
16+
required: true
17+
outputs:
18+
url:
19+
description: The URL to which the site was deployed
20+
value: ${{ steps.deploy.outputs.page_url }}
21+
runs:
22+
using: "composite"
23+
steps:
24+
- name: push changes (if needed)
25+
shell: bash
26+
run: |
27+
test "$(git rev-parse HEAD)" = "$(git rev-parse refs/remotes/origin/${{ github.ref_name }})" ||
28+
git push origin HEAD:${{ github.ref }}
29+
30+
- name: un-sparse worktree to prepare for deployment
31+
shell: bash
32+
run: git sparse-checkout disable
33+
34+
- name: setup GitHub Pages
35+
id: pages
36+
uses: actions/configure-pages@v5
37+
38+
- name: configure Hugo and Pagefind version
39+
shell: bash
40+
run: |
41+
set -x &&
42+
echo "HUGO_VERSION=$(sed -n 's/^ *hugo_version: *//p' <hugo.yml)" >>$GITHUB_ENV
43+
echo "PAGEFIND_VERSION=$(sed -n 's/^ *pagefind_version: *//p' <hugo.yml)" >>$GITHUB_ENV
44+
45+
- name: install Hugo ${{ env.HUGO_VERSION }}
46+
shell: bash
47+
run: |
48+
set -x &&
49+
curl -Lo /tmp/hugo.deb https://github.com/gohugoio/hugo/releases/download/v$HUGO_VERSION/hugo_extended_${HUGO_VERSION}_linux-amd64.deb &&
50+
sudo dpkg -i /tmp/hugo.deb
51+
52+
- name: run Hugo to build the pages
53+
env:
54+
HUGO_RELATIVEURLS: false
55+
shell: bash
56+
run: hugo config && hugo --minify --baseURL "${{ steps.pages.outputs.base_url }}/"
57+
58+
- name: run Pagefind ${{ env.PAGEFIND_VERSION }} to build the search index
59+
shell: bash
60+
run: npx -y pagefind@${{ env.PAGEFIND_VERSION }} --site public
61+
62+
- name: upload GitHub Pages artifact
63+
uses: actions/upload-pages-artifact@v3
64+
with:
65+
path: ./public
66+
67+
- name: deploy
68+
id: deploy
69+
uses: actions/deploy-pages@v4
70+
71+
- name: construct `--remap` option for lychee
72+
id: remap
73+
shell: bash
74+
run: |
75+
base_url='${{ steps.pages.outputs.base_url }}'
76+
echo "result=$(echo "$base_url" |
77+
sed 's|^\(.*\)\(/git-scm\.com\)$|(\1)?\2(.*)|') file://$PWD/public\$2" \
78+
>>$GITHUB_OUTPUT
79+
80+
- name: check for broken links
81+
id: lychee
82+
uses: lycheeverse/lychee-action@d4128702eae98bbc5ecf74df0165a8156c80920a # until an official version is out that includes https://github.com/lycheeverse/lychee/pull/1422
83+
with:
84+
lycheeVersion: nightly # until an official version includes https://github.com/lycheeverse/lychee/pull/1422
85+
args: >-
86+
--offline
87+
--fallback-extensions html
88+
--base '${{ steps.pages.outputs.base_url }}'
89+
--remap '${{ steps.remap.outputs.result }}'
90+
--exclude file:///path/to/repo.git/
91+
--exclude file:///caminho/para/o/reposit%C3%B3rio.git/
92+
--exclude file:///ruta/a/repositorio.git/
93+
--exclude file:///sl%C3%B3%C3%B0/til/hirsla.git/
94+
--exclude file:///Pfad/zum/Repo.git/
95+
--exclude file:///chemin/du/d%C3%A9p%C3%B4t.git/
96+
--exclude file:///srv/git/project.git
97+
public/
98+
output: lychee.md
99+
jobSummary: true
100+
fail: false
101+
failIfEmpty: false # needed because its default overrides `fail = false`
102+
103+
- name: ${{ env.lychee_exit_code != '0' && 'maybe close' || 'open or update' }} link checker issue
104+
uses: actions/github-script@v7
105+
with:
106+
github-token: ${{ inputs.github-token }}
107+
script: |
108+
const fs = await import('fs')
109+
// GitHub issues can only have 64k characters in their body
110+
const body = (s => {
111+
if (s.length < 65535) return s
112+
return s.replace(/^([^]{0,65000}\n)[^]*\n(.+)\n?$/, '$1\n[...]\n\n$2')
113+
})(await fs.promises.readFile('lychee.md', 'utf8'))
114+
115+
const issues = await (async () => {
116+
const req = { owner: context.repo.owner, repo: context.repo.repo }
117+
const q = `"Link+Checker+Report"+in:title+is:issue+label:linkchecker+is:open+repo:${req.owner}/${req.repo}`
118+
try {
119+
return await github.rest.search.issuesAndPullRequests({ ...req, q, sort: 'created', per_page: 1 })
120+
} catch (e) {
121+
console.log(`Warning: ${e}; sleeping for 30 seconds and falling back to calling listForRepo()`)
122+
await new Promise(r => setTimeout(r, 30000))
123+
124+
const issues = await github.rest.issues.listForRepo({...req, state: 'open', per_page: 100})
125+
return {
126+
data: {
127+
items: issues.data.filter(
128+
e => e.title === 'Link Checker Report'
129+
&& e.labels.filter(l => l.name === 'linkchecker').length > 0
130+
)
131+
}
132+
}
133+
}
134+
})()
135+
136+
if (issues.data.items.length === 0) {
137+
if (process.env.lychee_exit_code !== '0') {
138+
await github.rest.issues.create({ ...req, title: 'Link Checker Report', body, labels: ['linkchecker'] })
139+
}
140+
} else {
141+
req.issue_number = issues.data.items[0].number
142+
await github.rest.issues.createComment({ ...req, body })
143+
if (process.env.lychee_exit_code === '0') {
144+
await github.rest.issues.update({ ...req, state: 'closed' })
145+
}
146+
}

.github/workflows/ci.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,22 @@ jobs:
5757
}
5858
done
5959
exit $res
60+
61+
- name: check for broken links
62+
id: lychee
63+
uses: lycheeverse/lychee-action@d4128702eae98bbc5ecf74df0165a8156c80920a # until an official version is out that includes https://github.com/lycheeverse/lychee/pull/1422
64+
with:
65+
lycheeVersion: nightly # until an official version includes https://github.com/lycheeverse/lychee/pull/1422
66+
args: >-
67+
--offline
68+
--fallback-extensions html
69+
--exclude file:///path/to/repo.git/
70+
--exclude file:///caminho/para/o/reposit%C3%B3rio.git/
71+
--exclude file:///ruta/a/repositorio.git/
72+
--exclude file:///sl%C3%B3%C3%B0/til/hirsla.git/
73+
--exclude file:///Pfad/zum/Repo.git/
74+
--exclude file:///chemin/du/d%C3%A9p%C3%B4t.git/
75+
--exclude file:///srv/git/project.git
76+
public/
77+
output: lychee.md
78+
jobSummary: true

.github/workflows/deploy.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Deploy to GitHub Pages
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- gh-pages
8+
9+
jobs:
10+
deploy:
11+
runs-on: ubuntu-latest
12+
permissions:
13+
pages: write # to deploy to GitHub Pages
14+
id-token: write # to verify that the deployment source is legit
15+
issues: write # to open tickets upon broken links
16+
environment:
17+
name: github-pages
18+
url: ${{ steps.deploy.outputs.url }}
19+
steps:
20+
- uses: actions/checkout@v4
21+
- name: deploy to GitHub Pages
22+
id: deploy
23+
uses: ./.github/actions/deploy-to-github-pages

.github/workflows/update-book.yml

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
name: Update Progit Book
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
force-rebuild:
7+
description: Force re-building all books (e.g. after a script change)
8+
type: boolean
9+
default: false
10+
schedule:
11+
# check daily for updates
12+
- cron: '29 4 * * *'
13+
14+
jobs:
15+
check-for-updates:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
with:
20+
sparse-checkout: |
21+
external/book/sync
22+
script
23+
- uses: actions/github-script@v7
24+
id: get-pending
25+
with:
26+
script: |
27+
const { getPendingBookUpdates } = require('./script/ci-helper.js')
28+
29+
const pending = await getPendingBookUpdates(github, ${{ inputs.force-rebuild == true }})
30+
// an empty matrix is invalid and makes the workflow run fail, unfortunately
31+
return pending.length ? pending : ['']
32+
- name: ruby setup
33+
# Technically, we do not need Ruby in this job. But we do want to cache
34+
# Ruby & The Gems for use in the matrix in the next job.
35+
if: steps.get-pending.outputs.result != '[""]'
36+
uses: ruby/setup-ruby@v1
37+
with:
38+
bundler-cache: true
39+
outputs:
40+
matrix: ${{ steps.get-pending.outputs.result }}
41+
update-book:
42+
needs: check-for-updates
43+
if: needs.check-for-updates.outputs.matrix != '[""]'
44+
runs-on: ubuntu-latest
45+
strategy:
46+
matrix:
47+
language: ${{ fromJson(needs.check-for-updates.outputs.matrix) }}
48+
fail-fast: false
49+
steps:
50+
- uses: actions/checkout@v4
51+
with:
52+
sparse-checkout: |
53+
script
54+
external/book/sync
55+
external/book/data
56+
external/book/content/book/${{ matrix.language.lang }}
57+
external/book/static/book/${{ matrix.language.lang }}
58+
- name: clone ${{ matrix.language.repository }}
59+
run: |
60+
printf '%s\n' /progit-clone/ /vendor >>.git/info/exclude &&
61+
62+
# Clone the book's sources
63+
git clone --depth 1 --single-branch \
64+
https://github.com/${{ matrix.language.repository }} progit-clone
65+
- name: ruby setup
66+
uses: ruby/setup-ruby@v1
67+
with:
68+
bundler-cache: true
69+
- name: update book/${{ matrix.language.lang }}
70+
run: |
71+
# this seems to be needed to let `bundle exec` see `vendor/bundle/`
72+
{ bundle check || bundle install --frozen; } &&
73+
74+
# generate the HTML
75+
bundle exec ruby ./script/update-book2.rb ${{ matrix.language.lang }} progit-clone
76+
- name: commit changes
77+
run: |
78+
# record the commit hash
79+
mkdir -p external/book/sync &&
80+
git -C progit-clone rev-parse HEAD >external/book/sync/book-${{ matrix.language.lang }}.sha &&
81+
82+
# commit it all
83+
git add -A external/book &&
84+
git -c user.name=${{ github.actor }} \
85+
-c user.email=${{ github.actor }}@noreply.github.com \
86+
commit -m 'book: update ${{ matrix.language.lang }}' \
87+
-m 'Updated via the `update-book.yml` GitHub workflow.'
88+
- name: verify that there are no uncommitted changes
89+
run: |
90+
git update-index --refresh &&
91+
if test -n "$(git diff HEAD)$(git ls-files --exclude-standard --other)"
92+
then
93+
echo '::error::there are uncommitted changes!' >&2
94+
git status >&2
95+
exit 1
96+
fi
97+
- name: generate the bundle
98+
run: |
99+
git branch -m book-${{ matrix.language.lang }}
100+
git bundle create ${{ matrix.language.lang }}.bundle refs/remotes/origin/${{ github.ref_name }}..book-${{ matrix.language.lang }}
101+
- uses: actions/upload-artifact@v4
102+
with:
103+
name: bundle-${{ matrix.language.lang }}
104+
path: ${{ matrix.language.lang }}.bundle
105+
push-updates:
106+
needs: [check-for-updates, update-book]
107+
if: needs.check-for-updates.outputs.matrix != '[""]'
108+
permissions:
109+
contents: write # to push changes (if any)
110+
pages: write # to deploy to GitHub Pages
111+
id-token: write # to verify that the deployment source is legit
112+
issues: write # to open tickets upon broken links
113+
environment:
114+
name: github-pages
115+
url: ${{ steps.deploy.outputs.url }}
116+
runs-on: ubuntu-latest
117+
steps:
118+
- uses: actions/checkout@v4
119+
- uses: actions/download-artifact@v4
120+
- name: apply updates
121+
id: apply
122+
run: |
123+
for lang in $(echo '${{ needs.check-for-updates.outputs.matrix }}' |
124+
sed -n 's/\[\?{[^}]*"lang":"\([^"]*\)[^}]*},\?\]\?/\1 /gp')
125+
do
126+
git -c core.editor=: \
127+
-c user.name=${{ github.actor }} \
128+
-c user.email=${{ github.actor }}@noreply.github.com \
129+
pull --no-rebase bundle-$lang/$lang.bundle book-$lang ||
130+
exit 1
131+
done
132+
- name: deploy to GitHub Pages
133+
id: deploy
134+
uses: ./.github/actions/deploy-to-github-pages

0 commit comments

Comments
 (0)