mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
ci: rerun docs preview job on subsequent pushes (#25456)
Fixes DOCS-174: the docs-preview workflow only fired on `pull_request: opened`. Subsequent pushes left the preview comment stale. ## Changes - Add `synchronize` and `reopened` to trigger types so subsequent pushes retrigger the workflow. - Add a workflow-level `concurrency` group keyed by PR number with `cancel-in-progress: true` so rapid successive pushes don't race the comment-upsert lookup. - Replace always-create comment logic with an upsert: find the existing comment containing `<!-- docs-preview -->` and PATCH it; fall through to create only when none exists or the PATCH itself fails (comment was deleted between find and update). - Filter the upsert lookup to comments authored by `github-actions[bot]` so a human comment containing the marker is never silently overwritten. - Decouple the `gh api` lookup from the `head -n 1` pipe so API failures (network, auth, rate-limit) propagate immediately instead of being swallowed by `|| true`. - Delete the stale preview comment when a `synchronize` push drops all Markdown changes (e.g. a follow-up push that removes the file an earlier push had previewed but still touches `docs/`). The previous preview comment would otherwise point at a deleted page. - Extract the marker and the comment-selector jq into a single `DOCS_PREVIEW_MARKER` variable and a `list_docs_preview_comments` shell function so the stale-cleanup and upsert branches share one source of truth. ## Out of scope Vercel ISR cache invalidation for feature branch previews requires a coder.com change (the `algolia-docs-sync` endpoint only accepts `main` and `release/*` refs). Tracked separately in DOCS-174 out-of-scope notes. Pulls that fully revert their `docs/` changes in a follow-up push won't fire this workflow at all (GitHub's `paths` filter requires a path match in the diff), so a stale preview comment can survive on that specific edge. Removing the `paths` filter to handle it would run the workflow on every PR push, which is disproportionate. Acknowledged in [CRF-12](https://github.com/coder/coder/pull/25456#discussion_r3313738550). <details> <summary>Implementation notes</summary> **Marker and selector deduplication**: The marker string and jq selector previously appeared at three sites (comment body, stale-cleanup API call, upsert API call). They're now consolidated into `DOCS_PREVIEW_MARKER` plus a `list_docs_preview_comments` shell function so a future marker change updates one place. **Comment body construction**: The double-quoted multi-line string form with escaped backticks (`` \` ``) for the inline-code spans is shellcheck-clean. An earlier draft used `printf -v comment_body` with a single-quoted format string containing backticks, which triggered SC2016; the printf-three-pieces workaround that replaced it has since been simplified to the direct double-quoted form. **Upsert logic**: `gh api --paginate` fetches all PR comments, jq filters to `github-actions[bot]`-authored comments containing the marker, and the workflow PATCHes the first match. If the PATCH fails (404 because the comment was deleted between find and update), the script falls through to `gh pr comment` to create a new one. Self-heals on the next push if both paths somehow fail. **Stale-cleanup logic**: Same selector as upsert, but in the early-exit branch when no Markdown files exist in this push. `DELETE` failures are logged and execution continues (the next push will re-attempt or post a fresh comment), so a transient API failure won't fail the CI job. </details> > Generated by Coder Agents on behalf of @nickvigilante
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# This workflow posts a docs preview link as a PR comment whenever a
|
||||
# pull request that touches files under docs/ is opened. The preview
|
||||
# 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
|
||||
@@ -7,8 +7,12 @@
|
||||
# Branch names are URL-encoded so that names containing slashes or
|
||||
# other special characters produce working links.
|
||||
#
|
||||
# If the PR only deletes Markdown files (or only changes non-Markdown
|
||||
# files such as images or manifest.json), no comment is posted.
|
||||
# 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
|
||||
|
||||
@@ -16,9 +20,15 @@ on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
paths:
|
||||
- "docs/**"
|
||||
|
||||
concurrency:
|
||||
group: docs-preview-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -35,6 +45,22 @@ jobs:
|
||||
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
|
||||
@@ -51,7 +77,38 @@ jobs:
|
||||
| head -n 1) || true
|
||||
|
||||
if [ -z "$first_doc" ]; then
|
||||
echo "No added/modified Markdown files under docs/, skipping preview comment."
|
||||
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
|
||||
|
||||
@@ -97,9 +154,37 @@ jobs:
|
||||
url="${url}/${page_path}"
|
||||
fi
|
||||
|
||||
gh pr comment "${PR_NUMBER}" \
|
||||
--repo "${REPO}" \
|
||||
--body "## Docs preview
|
||||
# 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 -->"
|
||||
${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
|
||||
|
||||
Reference in New Issue
Block a user