ci: add docs PR preview workflows (#37440)

* ci: add docs PR preview workflows (P10)

Adds preview builds for PRs touching docs/** or api/**, deployed to the
existing mattermost-docs-preview-pulls S3 bucket under a repo-scoped
mattermost/pr-<N>/ prefix so they don't collide with mattermost/docs's
own previews of the same bucket. Fork PRs are handled via a manual
workflow_dispatch since they can't access secrets; previews are cleaned
up automatically on PR close for both same-repo and fork PRs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: use PR author for docs preview TRIGGERING_ACTOR

pull_request.head.user.login is the owner of the head repo/branch, not
the PR author -- for same-repo PRs (head repo == base repo) this
resolves to the org name instead of the contributor who opened the PR.
pull_request.user.login is always the actual PR author.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: reword docs preview PR comment

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: harden docs preview workflows

- docs-preview-fork: add explicit permissions (statuses/pull-requests
  write, contents read) instead of relying on default token scope.
- docs-preview-template: validate PR_NUMBER is digits-only and quote
  the S3 destination before use in the upload step, guarding against
  script injection via the fork dispatch's free-text input.
- docs-preview-template: add a per-PR concurrency group so an older,
  slower build can't overwrite a newer upload.
- docs-preview/docs-preview-fork: replace secrets: inherit with an
  explicit secrets mapping, and declare the secrets contract on the
  reusable template, so only the two AWS preview credentials are
  passed instead of every repo/org secret.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: align docs preview build/permissions with docs-ci, drop 3rd-party comment action

- docs-preview-template: add Set up Go (api/server/go.mod), matching
  docs-ci -- make -C api build shells out to `go run .` for code
  sample extraction and needs a pinned toolchain, not whatever happens
  to be preinstalled on the runner.
- docs-preview-template: drop the explicit "Build OpenAPI spec" step;
  npm run build's prebuild lifecycle script already runs
  make -C api build, so it was running twice.
- docs-preview-template: replace peter-evans/create-or-update-comment
  with plain `gh pr comment`, dropping a third-party action pin;
  behavior is unchanged (posts a new comment every run).
- docs-preview: add explicit permissions (contents: read,
  pull-requests: write) so the reusable workflow's requested
  pull-requests: write isn't silently downgraded by a restrictive
  default token policy.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: use gh pr comment in docs-preview-cleanup, drop 3rd-party action

Same swap already made in docs-preview-template.yml -- gh is
preinstalled on the runner, so this drops another third-party action
pin. Also quotes the S3 URI via env vars for consistency with the
template's upload step.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: replace pull_request_target with pull_request in docs-preview-cleanup

zizmor flags pull_request_target as a dangerous trigger by default.
Switch to plain pull_request, guarded to skip fork PRs (which don't
get secrets on this event) -- fork preview cleanup will be handled by
a separate scheduled sweep instead. Also fixes a stray '=' character
that had crept into the file.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Eva Sarafianou
2026-07-13 15:54:27 +03:00
committed by GitHub
co-authored by Cursor
parent d628dbc0ec
commit 33eb5b1a28
5 changed files with 259 additions and 1 deletions
@@ -0,0 +1,43 @@
name: docs-preview-cleanup
on:
pull_request:
types: [closed]
paths:
- 'docs/**'
- 'api/**'
permissions:
contents: read
pull-requests: write
jobs:
cleanup:
name: Delete preview from S3
runs-on: ubuntu-latest
# Fork PRs don't get secrets on plain pull_request events, so this would
# just fail for them -- a separate scheduled sweep handles fork preview
# cleanup instead of switching this to pull_request_target.
if: github.event.pull_request.head.repo.full_name == github.repository
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
with:
aws-access-key-id: ${{ secrets.AWS_DOCS_PR_PREVIEW_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_DOCS_PR_PREVIEW_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Delete preview prefix from S3
env:
PR_NUMBER: ${{ github.event.number }}
BUCKET_NAME: ${{ vars.DOCS_PREVIEW_BUCKET_NAME }}
run: |
aws s3 rm "s3://${BUCKET_NAME}/mattermost/pr-${PR_NUMBER}/" --recursive
- name: Comment on closed PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.number }}
run: |
gh pr comment "$PR_NUMBER" --repo "${{ github.repository }}" \
--body "Docs preview for PR #${PR_NUMBER} has been removed from S3."
+81
View File
@@ -0,0 +1,81 @@
name: docs-preview-fork
on:
workflow_dispatch:
inputs:
PR_NUMBER:
type: string
required: true
description: "PR number (fork PR to build preview for)"
TRIGGERING_ACTOR:
type: string
required: true
description: "GitHub login of the fork PR author"
COMMIT_SHA:
type: string
required: true
description: "Full commit SHA to build"
permissions:
contents: read
statuses: write
pull-requests: write
jobs:
update-initial-status:
runs-on: ubuntu-latest
steps:
- name: Set pending commit status
uses: mattermost/actions/delivery/update-commit-status@fec7b836001c9380d4bfaf28d443945c103a098c
env:
GITHUB_TOKEN: ${{ github.token }}
with:
repository_full_name: ${{ github.repository }}
commit_sha: ${{ inputs.COMMIT_SHA }}
context: "docs-preview-fork / preview"
description: "Docs preview build for ${{ inputs.COMMIT_SHA }} is running"
status: pending
preview:
uses: ./.github/workflows/docs-preview-template.yml
secrets:
AWS_DOCS_PR_PREVIEW_KEY_ID: ${{ secrets.AWS_DOCS_PR_PREVIEW_KEY_ID }}
AWS_DOCS_PR_PREVIEW_SECRET_ACCESS_KEY: ${{ secrets.AWS_DOCS_PR_PREVIEW_SECRET_ACCESS_KEY }}
needs:
- update-initial-status
with:
PR_NUMBER: ${{ inputs.PR_NUMBER }}
TRIGGERING_ACTOR: ${{ inputs.TRIGGERING_ACTOR }}
COMMIT_SHA: ${{ inputs.COMMIT_SHA }}
update-failure-status:
runs-on: ubuntu-latest
if: failure() || cancelled()
needs:
- preview
steps:
- uses: mattermost/actions/delivery/update-commit-status@fec7b836001c9380d4bfaf28d443945c103a098c
env:
GITHUB_TOKEN: ${{ github.token }}
with:
repository_full_name: ${{ github.repository }}
commit_sha: ${{ inputs.COMMIT_SHA }}
context: "docs-preview-fork / preview"
description: "Docs preview build for ${{ inputs.COMMIT_SHA }} failed"
status: failure
update-success-status:
runs-on: ubuntu-latest
if: success()
needs:
- preview
steps:
- uses: mattermost/actions/delivery/update-commit-status@fec7b836001c9380d4bfaf28d443945c103a098c
env:
GITHUB_TOKEN: ${{ github.token }}
with:
repository_full_name: ${{ github.repository }}
commit_sha: ${{ inputs.COMMIT_SHA }}
context: "docs-preview-fork / preview"
description: "Docs preview build for ${{ inputs.COMMIT_SHA }} succeeded"
status: success
+110
View File
@@ -0,0 +1,110 @@
name: docs-preview-template
on:
workflow_call:
inputs:
PR_NUMBER:
type: string
required: true
TRIGGERING_ACTOR:
type: string
required: true
COMMIT_SHA:
type: string
required: true
secrets:
AWS_DOCS_PR_PREVIEW_KEY_ID:
required: true
AWS_DOCS_PR_PREVIEW_SECRET_ACCESS_KEY:
required: true
# Serialize runs per PR so an older, slower build can't overwrite a newer
# upload; different PRs still build concurrently.
concurrency:
group: docs-preview-${{ inputs.PR_NUMBER }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
jobs:
preview:
name: Build and deploy preview
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.COMMIT_SHA }}
submodules: true
persist-credentials: false
- name: Set up Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: docs/site/.nvmrc
cache: npm
cache-dependency-path: docs/site/package-lock.json
- name: Set up Go
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version-file: api/server/go.mod
- name: Install npm dependencies
working-directory: docs/site
run: npm ci
- name: Typecheck
working-directory: docs/site
run: npm run typecheck
- name: Build Docusaurus site (preview)
working-directory: docs/site
env:
BASE_URL: /mattermost/pr-${{ inputs.PR_NUMBER }}/
run: npm run build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
with:
aws-access-key-id: ${{ secrets.AWS_DOCS_PR_PREVIEW_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_DOCS_PR_PREVIEW_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Upload preview to S3
# Repo-scoped prefix (mattermost/) — this bucket is shared
# with mattermost/docs previews during the migration transition, and
# PR numbers are per-repo, not global.
#
# No explicit --cache-control, matching mattermost/docs's existing
# preview workflow (shallwefootball/s3-upload-action doesn't set one
# either).
env:
PR_NUMBER: ${{ inputs.PR_NUMBER }}
BUCKET_NAME: ${{ vars.DOCS_PREVIEW_BUCKET_NAME }}
run: |
if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
echo "PR_NUMBER must be a positive integer, got: $PR_NUMBER" >&2
exit 1
fi
aws s3 sync docs/site/build/ \
"s3://${BUCKET_NAME}/mattermost/pr-${PR_NUMBER}/" \
--delete \
--no-progress
- name: Post preview URL comment
# Plain gh CLI instead of a third-party action -- posts a new comment
# every run (same behavior peter-evans/create-or-update-comment had
# here, since it wasn't given a comment-id/body-includes matcher).
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ inputs.PR_NUMBER }}
TRIGGERING_ACTOR: ${{ inputs.TRIGGERING_ACTOR }}
COMMIT_SHA: ${{ inputs.COMMIT_SHA }}
BUCKET_NAME: ${{ vars.DOCS_PREVIEW_BUCKET_NAME }}
run: |
BODY=$(printf 'Newest code from %s and commit %s has docs preview environment ready:\n\n**[Open preview environment](http://%s.s3-website-us-east-1.amazonaws.com/mattermost/pr-%s/)**' \
"$TRIGGERING_ACTOR" "$COMMIT_SHA" "$BUCKET_NAME" "$PR_NUMBER")
gh pr comment "$PR_NUMBER" --repo "${{ github.repository }}" --body "$BODY"
+24
View File
@@ -0,0 +1,24 @@
name: docs-preview
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
paths:
- 'docs/**'
- 'api/**'
permissions:
contents: read
pull-requests: write
jobs:
deploy:
uses: ./.github/workflows/docs-preview-template.yml
if: github.event.pull_request.head.repo.full_name == github.repository
secrets:
AWS_DOCS_PR_PREVIEW_KEY_ID: ${{ secrets.AWS_DOCS_PR_PREVIEW_KEY_ID }}
AWS_DOCS_PR_PREVIEW_SECRET_ACCESS_KEY: ${{ secrets.AWS_DOCS_PR_PREVIEW_SECRET_ACCESS_KEY }}
with:
PR_NUMBER: ${{ github.event.number }}
TRIGGERING_ACTOR: ${{ github.event.pull_request.user.login }}
COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
+1 -1
View File
@@ -41,7 +41,7 @@ const config: Config = {
},
url: 'https://docs.mattermost.com',
baseUrl: '/',
baseUrl: process.env.BASE_URL ?? '/',
trailingSlash: false,
organizationName: 'mattermost',