-
Notifications
You must be signed in to change notification settings - Fork 0
267 lines (244 loc) · 11.3 KB
/
docs.yml
File metadata and controls
267 lines (244 loc) · 11.3 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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
name: Docs
# Triggers:
# - push to main touching docs/** or this workflow → deploy latest to GitHub Pages
# - push of a major release tag (v2.0.0, v3.0.0 …) → versioned snapshot
# - workflow_dispatch → manual deploy of latest
on:
push:
branches: [main]
paths:
- "docs/**"
- ".github/workflows/docs.yml"
tags:
# Glob pattern (not regex): matches v1.0.0, v2.0.0, v10.0.0 …
- "v[0-9]*.0.0"
workflow_dispatch:
# Deployment strategy: GitHub Actions Pages source (actions/deploy-pages — official, no third party).
# • gh-pages branch = versioned snapshot STORAGE only (not served directly by Pages)
# • Every deploy job assembles a combined artifact:
# - latest docs built from main at the artifact root
# - each vX/ snapshot from the gh-pages branch merged in
# → single artifact deployed via actions/deploy-pages
# • Snapshot job stores the built snapshot in gh-pages branch via plain git,
# then pushes the updated versions.json to main (no [skip ci]) which
# re-triggers the deploy job to include the new snapshot immediately.
# Requires: Settings → Pages → Source: GitHub Actions.
permissions:
contents: write
pages: write
id-token: write
# Only one concurrent deployment for the same ref; cancel outdated runs.
concurrency:
group: docs-${{ github.ref }}
cancel-in-progress: true
jobs:
# ── Deploy (latest) ─────────────────────────────────────────────────────────
# Assembles a combined Pages artifact:
# 1. Builds the latest docs (base: /github-code-search/)
# 2. Merges existing versioned snapshots from the gh-pages storage branch
# into the artifact (docs/.vitepress/dist/vX/ for each stored version)
# 3. Uploads the artifact and deploys via actions/deploy-pages
# Requires: Settings → Pages → Source: GitHub Actions.
deploy:
name: Build and deploy docs
if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- name: Checkout
uses: actions/checkout@v4.2.2
with:
# Needed to fetch the gh-pages storage branch for versioned snapshots.
fetch-depth: 0
- name: Setup Bun
uses: oven-sh/setup-bun@v2.1.2
with:
bun-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Build latest docs
run: bun run docs:build
# Base URL: /github-code-search/ (default in config.mts)
- name: Merge versioned snapshots from gh-pages storage
run: |
set -euo pipefail
if git ls-remote --heads origin gh-pages | grep -q gh-pages; then
git fetch origin gh-pages
# List only top-level entries that match v<integer> (e.g. v1, v2).
# Using a pathspec glob and a strict regex avoids iterating over
# unrelated files (.nojekyll, README, etc.) at the branch root.
for entry in $(git ls-tree --name-only origin/gh-pages -- 'v[0-9]*'); do
if echo "$entry" | grep -qE '^v[0-9]+$'; then
echo "Merging snapshot: $entry"
mkdir -p "docs/.vitepress/dist/$entry"
git archive origin/gh-pages "$entry" | tar -x -C docs/.vitepress/dist/
# Validate: warn and remove if the extracted directory is empty.
if [ -z "$(ls -A "docs/.vitepress/dist/$entry" 2>/dev/null)" ]; then
echo "Warning: snapshot '$entry' is empty after extraction — removing."
rmdir "docs/.vitepress/dist/$entry"
fi
fi
done
else
echo "No gh-pages branch yet — skipping snapshot merge"
fi
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3.0.1
with:
path: docs/.vitepress/dist
- name: Deploy to GitHub Pages
id: deploy
uses: actions/deploy-pages@v4.0.5
# ── Snapshot (versioned) ────────────────────────────────────────────────────
# Triggered by a major release tag (e.g. v2.0.0).
# 1. Builds docs with a versioned base URL (/github-code-search/v2/).
# 2. Stores the output in the gh-pages branch under /v2/ using plain git
# (no third-party action). The gh-pages branch is storage only — Pages
# still points to GitHub Actions; the deploy job merges snapshots in.
# 3. Prepends the entry to versions.json on main WITHOUT [skip ci], which
# re-triggers the deploy job so the new snapshot is live immediately.
#
# Convention: only tags matching vX.0.0 (major bumps) trigger a snapshot.
# Patch and minor releases update the main docs in-place via the deploy job.
snapshot:
name: Snapshot versioned docs
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4.2.2
with:
# Full history needed to push to gh-pages and commit versions.json to main.
fetch-depth: 0
- name: Extract major version from tag
id: ver
run: |
# Validate that the tag strictly matches vX.0.0 before proceeding.
# The workflow trigger filter (v[0-9]*.0.0) is the primary guard, but
# this ensures the script fails fast if triggered with an unexpected ref.
if ! echo "$GITHUB_REF_NAME" | grep -Eq '^v[0-9]+\.0\.0$'; then
echo "Error: '$GITHUB_REF_NAME' does not match expected pattern vX.0.0" >&2
exit 1
fi
MAJOR="${GITHUB_REF_NAME%%.*}" # e.g. v2 from v2.0.0
echo "major=$MAJOR" >> "$GITHUB_OUTPUT"
- name: Setup Bun
uses: oven-sh/setup-bun@v2.1.2
with:
bun-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Build versioned snapshot
env:
# config.mts reads VITEPRESS_BASE when set; falls back to /github-code-search/
VITEPRESS_BASE: /github-code-search/${{ steps.ver.outputs.major }}/
run: bun run docs:build
- name: Store snapshot in gh-pages branch
run: |
set -euo pipefail
MAJOR="${{ steps.ver.outputs.major }}"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Set up the gh-pages worktree (create orphan branch if it doesn't exist yet).
if git ls-remote --heads origin gh-pages | grep -q gh-pages; then
git fetch origin gh-pages
git worktree add /tmp/gh-pages-storage origin/gh-pages
else
git worktree add --orphan -b gh-pages /tmp/gh-pages-storage
touch /tmp/gh-pages-storage/.nojekyll # prevent Jekyll processing
fi
# Copy the built snapshot into its versioned directory.
rm -rf "/tmp/gh-pages-storage/$MAJOR"
cp -r docs/.vitepress/dist "/tmp/gh-pages-storage/$MAJOR"
# Commit and push.
cd /tmp/gh-pages-storage
git add .
git diff --staged --quiet || git commit -m "docs: store snapshot $MAJOR [skip ci]"
git push origin gh-pages
# Explicit cleanup — belt-and-suspenders even on ephemeral runners.
git worktree remove /tmp/gh-pages-storage
- name: Prepend new version entry to versions.json
run: |
MAJOR="${{ steps.ver.outputs.major }}"
LINK="/${MAJOR}/"
# Idempotent — skip if the entry already exists.
jq --arg text "$MAJOR" --arg link "$LINK" \
'if any(.[]; .link == $link) then . else [{"text": $text, "link": $link}] + . end' \
docs/public/versions.json > /tmp/versions_new.json
mv /tmp/versions_new.json docs/public/versions.json
- name: Generate blog post stub for new major version
run: |
set -euo pipefail
# Convert vX.0.0 → v<maj>-0-0 for the file name (e.g. v3.0.0 → v3-0-0)
TAG="$GITHUB_REF_NAME"
SLUG="${TAG//./-}" # v3.0.0 → v3-0-0
BLOG_FILE="docs/blog/release-${SLUG}.md"
RELEASE_DATE="$(date -u +%Y-%m-%d)"
# Idempotent — skip creation if the file already exists (manually authored).
if [ -f "$BLOG_FILE" ]; then
echo "Blog post $BLOG_FILE already exists — skipping stub generation."
else
# Fix: use Python to write the file so heredoc indentation never
# leaks into the generated Markdown (which would break frontmatter).
python3 - <<PY
import pathlib
tag = "$TAG"
release_date = "$RELEASE_DATE"
blog_file = "$BLOG_FILE"
content = (
f'---\ntitle: "What\'s new in {tag}"\n'
f'description: "Highlights of github-code-search {tag}"\n'
f'date: {release_date}\n---\n\n'
f'# What\'s new in github-code-search {tag}\n\n'
f'> Full release notes: <https://github.com/fulll/github-code-search/releases/tag/{tag}>\n\n'
'<!-- TODO: fill in feature highlights, usage examples and screenshots. -->\n'
)
pathlib.Path(blog_file).write_text(content, encoding="utf-8")
print(f"Created blog stub: {blog_file}")
PY
fi
- name: Update blog/index.md table with new major version
run: |
set -euo pipefail
TAG="$GITHUB_REF_NAME"
SLUG="${TAG//./-}"
# Add a row to the blog index table only if the version isn't already listed.
if grep -qF "release-${SLUG}" docs/blog/index.md; then
echo "Blog index already contains ${TAG} — skipping."
else
python3 - <<PY
import re, pathlib
path = pathlib.Path("docs/blog/index.md")
content = path.read_text()
tag = "$TAG"
slug = "$SLUG"
new_row = f"| [{tag}](./release-{slug}) | <!-- TODO: add summary --> |"
# Insert the new row after the last markdown table row (line ending with |).
updated = re.sub(
r"(\|[^\n]+\|)(\s*\Z)",
lambda m: m.group(1) + "\n" + new_row + m.group(2),
content,
count=1,
flags=re.DOTALL,
)
path.write_text(updated)
print(f"Inserted row for {tag} into blog/index.md")
PY
fi
- name: Commit blog stub and versions.json to main
# Pin to exact commit SHA to prevent supply-chain attacks.
uses: stefanzweifel/git-auto-commit-action@8621497c8c39c72f3e2a999a26b4ca1b5058a842 # v5.0.1
with:
# No [skip ci] — the push to main matches paths: docs/** and re-triggers
# the deploy job, which merges the new snapshot into the Pages artifact.
commit_message: "docs: add ${{ steps.ver.outputs.major }} to versions.json and blog"
file_pattern: docs/public/versions.json docs/blog/
branch: main