-
Notifications
You must be signed in to change notification settings - Fork 1.3k
203 lines (186 loc) · 8.63 KB
/
Copy pathdocs-preview.yaml
File metadata and controls
203 lines (186 loc) · 8.63 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
# This workflow posts a docs preview link as a PR comment whenever a
# pull request that touches docs/ is opened or updated. The preview
# is served by coder.com's branch-preview feature at /docs/@<branch>.
#
# The link deep-links to the first added/modified/renamed Markdown file
# under docs/ so reviewers land on the page that actually changed.
# Branch names are URL-encoded so that names containing slashes or
# other special characters produce working links.
#
# On subsequent pushes (synchronize) the existing comment is updated
# rather than creating a duplicate. If a previous push had a Markdown
# file but the current push has none, the stale comment is deleted so
# readers don't follow a dead deep-link. If the PR only deletes
# Markdown files (or only changes non-Markdown files such as images or
# manifest.json), no comment is posted.
name: docs-preview
on:
pull_request:
types:
- opened
- synchronize
- reopened
paths:
- "docs/**"
# docs/.style/** is contributor tooling and never deploys to coder.com.
# Skipping the workflow on .style-only PRs avoids posting a preview
# link that 404s, since manifest-driven coder.com routing rejects
# paths under .style. Mixed PRs still trigger; the selection logic
# below filters .style files out of the preview-target pick.
- "!docs/.style/**"
concurrency:
group: docs-preview-${{ github.event.pull_request.number }}
cancel-in-progress: true
permissions:
contents: read
jobs:
docs-preview:
runs-on: ubuntu-latest
permissions:
pull-requests: write # needed for commenting on PRs
steps:
- name: Post docs preview comment
env:
GH_TOKEN: ${{ github.token }}
BRANCH: ${{ github.event.pull_request.head.ref }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
# Marker embedded in the comment body so we can find this
# workflow's own comments later. Keep this in one place so
# later refactors don't drift between the body construction
# and the jq selectors used to find existing comments.
DOCS_PREVIEW_MARKER='<!-- docs-preview -->'
# Returns IDs of github-actions[bot] comments on the PR whose
# body contains DOCS_PREVIEW_MARKER. Used by both the stale-
# comment-cleanup branch (when this push has no Markdown
# changes) and the upsert branch below.
list_docs_preview_comments() {
gh api --paginate \
"repos/${REPO}/issues/${PR_NUMBER}/comments" \
--jq ".[] | select(.user.login == \"github-actions[bot]\") | select(.body | contains(\"${DOCS_PREVIEW_MARKER}\")) | .id"
}
# Fetch the list of non-deleted files from the PR. This is
# intentionally not piped into grep so that a gh-api failure
# (network, auth, rate-limit) propagates immediately instead
# of being swallowed by `|| true`.
all_files=$(gh api --paginate \
"repos/${REPO}/pulls/${PR_NUMBER}/files" \
--jq '.[] | select(.status != "removed") | .filename')
# Pick the first Markdown file under docs/, excluding the
# contributor-tooling subtree at docs/.style/**. Mixed PRs that
# touch both .style and a public docs page should land the
# preview link on the public page; .style-only PRs already get
# short-circuited by the trigger filter above and never reach
# this code path.
#
# `|| true` keeps the pipeline from failing when grep finds no
# matches or head triggers SIGPIPE under `set -o pipefail`.
first_doc=$(printf '%s\n' "$all_files" \
| grep -E '^docs/.*\.md$' \
| grep -v '^docs/\.style/' \
| head -n 1) || true
if [ -z "$first_doc" ]; then
echo "No added/modified Markdown files under docs/ on this push."
# Now that the workflow fires on synchronize, this branch
# is reachable on pushes that drop all Markdown while still
# touching docs/ (e.g. a push that removes the file an
# earlier push had previewed but adds a new image). The
# previous preview comment now points at a deleted page;
# delete it so readers don't follow a dead deep-link.
#
# Intentionally decoupled from head so that a gh-api failure
# propagates here instead of being swallowed by `|| true`. In
# this branch the workflow has no preview link to post anyway
# (no Markdown in the push), so a transient list failure is a
# cosmetic miss; log and exit cleanly rather than red-checking
# every docs-touching PR during a comments-endpoint hiccup.
# The next push will retry the cleanup. The upsert path below
# uses strict propagation by contrast, because silent failure
# there would create duplicate comments.
stale_comment_ids=$(list_docs_preview_comments) || {
echo "Could not list preview comments; skipping cleanup."
exit 0
}
stale_id=$(printf '%s\n' "$stale_comment_ids" | head -n 1) || true
if [ -n "$stale_id" ]; then
if gh api --method DELETE \
"repos/${REPO}/issues/comments/${stale_id}"; then
echo "Deleted stale docs preview comment (id=${stale_id})."
else
echo "Failed to delete stale docs preview comment (id=${stale_id}); leaving in place."
fi
fi
exit 0
fi
# Map the repo path to the docs site URL path.
# docs/README.md -> "" (docs root)
# docs/<dir>/index.md -> "<dir>" (directory index)
# docs/<dir>/README.md -> "<dir>" (directory index)
# docs/<dir>/<file>.md -> "<dir>/<file>"
rel="${first_doc#docs/}"
case "$rel" in
README.md)
page_path=""
;;
*)
base="$(basename "$rel")"
dir="$(dirname "$rel")"
if [ "$dir" = "." ]; then
dir=""
fi
case "$base" in
index.md|README.md)
page_path="$dir"
;;
*)
stripped="${base%.md}"
if [ -z "$dir" ]; then
page_path="$stripped"
else
page_path="${dir}/${stripped}"
fi
;;
esac
;;
esac
# URL-encode the branch name so slashes and special
# characters don't break the preview URL. The page path is
# left as-is because its components are simple ASCII path
# segments and the slashes between them must be preserved.
encoded_branch=$(jq -rn --arg b "$BRANCH" '$b | @uri')
url="https://coder.com/docs/@${encoded_branch}"
if [ -n "$page_path" ]; then
url="${url}/${page_path}"
fi
# The literal backticks around ${first_doc} are escaped so
# they survive the double-quoted string as Markdown inline
# code; ${url} and ${first_doc} expand normally.
comment_body="## Docs preview
[:book: View docs preview](${url}) for \`${first_doc}\`
${DOCS_PREVIEW_MARKER}"
# Upsert: update the existing docs-preview comment if one
# exists, otherwise create a new one. This prevents duplicate
# preview comments on every push to the PR.
#
# Intentionally not piped into head so that a gh-api failure
# (network, auth, rate-limit) propagates immediately instead
# of being swallowed by `|| true`.
all_comment_ids=$(list_docs_preview_comments)
existing_id=$(printf '%s\n' "$all_comment_ids" | head -n 1) || true
if [ -n "$existing_id" ]; then
if ! gh api --method PATCH \
"repos/${REPO}/issues/comments/${existing_id}" \
--field body="$comment_body"; then
echo "PATCH failed (comment may have been deleted); creating a new comment."
existing_id=""
else
echo "Updated existing docs preview comment (id=${existing_id})."
fi
fi
if [ -z "$existing_id" ]; then
gh pr comment "${PR_NUMBER}" \
--repo "${REPO}" \
--body "$comment_body"
echo "Created new docs preview comment."
fi