Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

infra: publish PR previews for docs (second try) #5117

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions .github/workflows/docs-preview-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# This workflow builds the docs for each PR, and uploads the rendered docs as an
# artifact.
#
# The artifact will be picked up by docs-preview-deploy.yml and published at
# https://jj-preview.github.io/docs/pr-<number>.
#
# The split into "build" and "deploy" follows GitHub's guide at
# https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/.
# The build workflow has no access to the deploy key. The deploy workflow has
# access, but is only allowed to run from the main branch (by requesting the
# 'preview' environment).

name: docs-preview-build

permissions: read-all

on:
pull_request:
# Run the workflow only when these paths have changed
paths:
- "docs/**"
- "mkdocs.yml"
- "pyproject.toml"
- "uv.lock"
- ".github/workflows/docs-preview-build.yml"
# Run the workflow when anything changes OR when the PR is merged/closed.
# When the PR is closed, we will push a dir with a special file to tell the
# deploy workflow to remove the preview.
types: [opened, reopened, synchronize, closed]
branches: [main]

jobs:
upload-pr-number:
runs-on: ubuntu-latest
steps:
- env:
PR_NUMBER: ${{ github.event.number }}
run: |
echo "$PR_NUMBER" > pr_number

- uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882
with:
name: pr_number
path: pr_number

# This job runs whenever the PR is opened, reopened, or pushed into.
build-preview:
runs-on: ubuntu-latest
if: ${{ github.event.action != 'closed' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false

- name: Install uv
uses: astral-sh/setup-uv@2e657c127d5b1635d5a8e3fa40e0ac50a5bf6992

- name: Build the docs
# Intentionally without --strict, to have previews even if the docs are
# mildly broken
run: uv run mkdocs build

- uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882
with:
name: rendered-docs
path: rendered-docs

# This job runs whenever the PR is closed.
delete-preview:
runs-on: ubuntu-latest
if: ${{ github.event.action == 'closed' }}
steps:
- run: |
mkdir -p rendered-docs
touch rendered-docs/.delete-this-preview

- uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882
with:
name: rendered-docs
path: rendered-docs
138 changes: 138 additions & 0 deletions .github/workflows/docs-preview-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# This workflow pushes docs rendered by docs-preview-build.yml to
# https://github.com/jj-preview/docs.
#
# There, GitHub's own "deploy pages" action will run to publish the static files
# to https://jj-preview.github.io/docs/. This adds a 30s delay before the docs
# are actually published/updated.
#
# jj-preview/docs has a top-level .nojekyll file. Otherwise GitHub will try
# rendering with Jekyll, and the delay will be longer.
#
# We use a separate repo for two reasons:
# 1. No chance to accidentally mess up the main GH Pages
# 2. Avoid the impression that the published content is "official"

name: docs-preview-deploy

permissions: read-all

# Security note: 'on: workflow_run' workflows must be very careful about referencing any
# PR-author-controlled strings (branch names, emails, etc). Ideally - don't do it
# at all. This workflow only references the PR number, and takes care to validate it
# before using it.
on:
workflow_run:
workflows: [docs-preview-build]
types: [completed]

jobs:
get-pr-number:
runs-on: ubuntu-latest
outputs:
PR_NUMBER: ${{ steps.result.outputs.PR_NUMBER }}
steps:
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16
with:
name: pr_number
path: pr_number
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}

# Security note: The PR number is uploaded by the build workflow, and can
# be manipulated. The only thing an attacker can do, though, is overwrite
# someone else's docs preview. See
# https://github.com/orgs/community/discussions/25220
- id: result
run: |
PR_NUMBER=$(cat pr_number/pr_number)
if [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
echo "PR_NUMBER=$PR_NUMBER"
echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_OUTPUT"
else
echo "PR_NUMBER must contain only digits; something went wrong"
exit 1
fi

deploy:
runs-on: ubuntu-latest
# Give this job access to the deploy key in the 'preview' environment's secrets
environment: preview
permissions:
pull-requests: write # Needed to post a comment with the preview URL
if: ${{ github.event.workflow_run.conclusion == 'success' }}
needs: [get-pr-number]
# Never run jobs for different PRs in parallel, since they push to the same
# jj-preview/docs repo
concurrency:
group: docs-preview-${{ needs.get-pr-number.outputs.PR_NUMBER }}
# It is tempting to use cancel-in-progress: true, but then people will get
# spammed with cancellation notifications.
cancel-in-progress: false

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false

- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16
with:
name: rendered-docs
path: rendered-docs
# The token has to be provided explicitly when downloading artifacts
# from a different workflow run (docs-preview-build.yml in this case)
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}

# This workflow is used both for *publishing* previews and for *deleting*
# previews. We have to know if we are deleting or publishing a preview.
- name: Check if we are asked to delete the preview
id: should-delete
run: |
if [[ -f rendered-docs/.delete-this-preview ]]; then
echo "delete=true" >> $GITHUB_OUTPUT
rm rendered-docs/.delete-this-preview
else
echo "delete=false" >> $GITHUB_OUTPUT
fi

- name: Deploy
uses: JamesIves/github-pages-deploy-action@62fec3add6773ec5dbbf18d2ee4260911aa35cf4
with:
folder: rendered-docs
repository-name: jj-preview/docs
branch: main
target-folder: "pr-${{ needs.get-pr-number.outputs.PR_NUMBER }}"
# This key allows pushing to jj-preview/docs. It was set up like this:
#
# 1. ssh-keygen -t ed25519 -C "" -N "" -f <where to save the keys>
# 2. Add the public key to https://github.com/jj-preview/docs/settings/keys/new
# 3. Add the private key to a 'preview' environment secret in the main repo
ssh-key: ${{ secrets.DOCS_DEPLOY_KEY }}
git-config-name: "jj-docs[bot]"
git-config-email: "jj-docs[bot]@users.noreply.github.io"
commit-message: "Update preview for PR ${{ needs.get-pr-number.outputs.PR_NUMBER }}"
force: false

- name: Post a link to the docs in the PR
uses: thollander/actions-comment-pull-request@65f9e5c9a1f2cd378bd74b2e057c9736982a8e74
if: ${{ steps.should-delete.outputs.delete == 'false' }}}
with:
# (double blank line = new paragraph)
message: >
**📖 The documentation preview has been published to
https://jj-preview.github.io/docs/pr-${{ needs.get-pr-number.outputs.PR_NUMBER }}.**
Thanks for working on the docs!


Note: it will take about 30 seconds before the preview is available.


---


<sup>If the preview isn't working, please file an issue or reach out to us in Discord/IRC.
You can also build the docs on your own machine by following the instructions at
https://martinvonz.github.io/jj/prerelease/contributing/#previewing-the-html-documentation.</sup>

pr-number: ${{ needs.get-pr-number.outputs.PR_NUMBER }}
comment-tag: docs-preview
Loading