Skip to content
Closed
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
89 changes: 89 additions & 0 deletions .github/workflows/docs-pr-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Builds the docs site for every PR that touches docs, and (for same-repo PRs)
# publishes a live preview at: https://<org>.github.io/<repo>/pr-preview/pr-<N>/
#
# The build step doubles as a CI gate: if `mkdocs build` fails, the check fails,
# so a broken docs change can't merge. The preview is torn down when the PR closes.
#
# Security notes:
# - Uses `pull_request` (NOT `pull_request_target`): fork PRs run with a
# read-only GITHUB_TOKEN and no secrets, so building untrusted PR code is safe.
# - The write-scoped preview deploy is gated to same-repo PRs only (fork PRs
# still get the build gate, just no preview — GitHub gives them a read-only
# token regardless).
# - No untrusted event data (PR title/body/branch) is interpolated into any
# `run:` step, so there's no command-injection surface.
# - The one third-party action (rossjrw, an individual account) is pinned to a
# full commit SHA.
name: Docs PR Preview

on:
pull_request:
types: [opened, reopened, synchronize, closed]
paths:
- "docs/**"
- "mkdocs.yml"
- "docs/requirements.txt"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docs/requirements.txt is already matched by the docs/** glob above it, so this entry is redundant.

Suggested change
- "docs/requirements.txt"

- ".github/workflows/docs-pr-preview.yml"

permissions:
contents: write # publish the preview to the gh-pages branch
pull-requests: write # post/update the preview link comment
Comment on lines +28 to +30

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting contents: write and pull-requests: write at workflow level means every build step — npm ci, npm run docs:api, mkdocs build — executes with repository write access. For same-repo PRs, a supply-chain compromise in an npm dependency could abuse this token.

The canonical hardening for pull_request workflows is to split into two jobs so the untrusted build code runs read-only:

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      # ... all build steps ...
      - uses: actions/upload-artifact@v4
        with:
          name: site
          path: site/

  deploy-preview:
    needs: build
    if: github.event.pull_request.head.repo.full_name == github.repository
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: site
          path: site/
      - uses: rossjrw/pr-preview-action@ffa7509e91a3ec8dfc2e5536c4d5c1acdf7a6de9
        with:
          source-dir: site
          preview-branch: gh-pages
          umbrella-dir: pr-preview
          action: auto

This way write credentials are never present in the process tree that runs untrusted build code. For fork PRs GitHub overrides the token to read-only anyway, so the real exposure is same-repo PRs only — but that's still worth minimising.


concurrency:
group: docs-preview-${{ github.event.number }}
cancel-in-progress: true

jobs:
build-and-preview:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
if: github.event.action != 'closed'
uses: actions/setup-node@v4
with:
node-version: "18"

- name: Setup Python
if: github.event.action != 'closed'
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install Node dependencies
if: github.event.action != 'closed'
run: npm ci

- name: Install Coded Action Apps dependencies
if: github.event.action != 'closed'
working-directory: packages/coded-action-app
run: npm ci

- name: Install docs requirements
if: github.event.action != 'closed'
run: |
python3 -m venv docs-env
source docs-env/bin/activate
pip install -r docs/requirements.txt

- name: Generate API documentation
if: github.event.action != 'closed'
run: npm run docs:api

- name: Build MkDocs site (CI gate)
if: github.event.action != 'closed'
run: |
source docs-env/bin/activate
mkdocs build

- name: Deploy / update / remove PR preview
# Same-repo PRs only — fork PRs get a read-only token and can't publish.
if: github.event.pull_request.head.repo.full_name == github.repository
uses: rossjrw/pr-preview-action@ffa7509e91a3ec8dfc2e5536c4d5c1acdf7a6de9 # v1.6.1
with:
source-dir: site
preview-branch: gh-pages
umbrella-dir: pr-preview
action: auto
Loading