diff --git a/.github/workflows/pr-docs-preview-publish.yml b/.github/workflows/pr-docs-preview-publish.yml
new file mode 100644
index 000000000..2e7597e29
--- /dev/null
+++ b/.github/workflows/pr-docs-preview-publish.yml
@@ -0,0 +1,118 @@
+name: Publish docs preview on PR
+
+# Publish what pr-docs-preview.yml built, and remove it when the PR closes.
+# This workflow has a write token, so it never runs code from the PR:
+# it only copies files and treats the artifact as untrusted data.
+# The author of a fork PR controls the artifact, including any HTML and
+# JavaScript. So publish only for PR authors with write or member access.
+
+on:
+ workflow_run:
+ workflows: ["Create docs preview on PR"]
+ types: [completed]
+
+permissions:
+ contents: write
+ pull-requests: write
+ actions: read
+
+# One group per PR branch: a newer run replaces a pending older run.
+concurrency:
+ group: docs-preview-${{ github.event.workflow_run.head_repository.full_name }}-${{ github.event.workflow_run.head_branch }}
+ cancel-in-progress: false
+
+jobs:
+ publish:
+ if: github.event.workflow_run.conclusion == 'success'
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Download preview
+ uses: actions/download-artifact@v8
+ with:
+ name: docs-preview
+ path: preview
+ run-id: ${{ github.event.workflow_run.id }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+
+ # Accept only a PR whose head is the commit that was built.
+ # Remove the preview when the PR is not open.
+ - name: Check preview metadata
+ id: meta
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
+ run: |
+ set -euo pipefail
+ PR=$(head -c 16 preview/pr | tr -d '\n')
+ ACTION=$(head -c 16 preview/action | tr -d '\n')
+ [[ "${PR}" =~ ^[0-9]+$ ]] || { echo "::error::Invalid PR number"; exit 1; }
+ [[ "${ACTION}" =~ ^(publish|remove)$ ]] || { echo "::error::Invalid action"; exit 1; }
+ read -r PR_SHA PR_STATE PR_ASSOC < <(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR}" \
+ --jq '"\(.head.sha) \(.state) \(.author_association)"')
+ if [[ "${PR_SHA}" != "${HEAD_SHA}" ]]; then
+ # A newer push or a wrong PR number. A newer run publishes instead.
+ echo "::notice::PR #${PR} head is not ${HEAD_SHA}. Skip."
+ ACTION=skip
+ elif [[ "${PR_STATE}" != "open" ]]; then
+ ACTION=remove
+ elif [[ "${ACTION}" == "publish" && ! "${PR_ASSOC}" =~ ^(OWNER|MEMBER|COLLABORATOR)$ ]]; then
+ echo "::notice::PR #${PR} author is ${PR_ASSOC}. No preview."
+ ACTION=skip
+ fi
+ echo "pr=${PR}" >> "$GITHUB_OUTPUT"
+ echo "action=${ACTION}" >> "$GITHUB_OUTPUT"
+
+ - name: Checkout `www` branch
+ if: steps.meta.outputs.action != 'skip'
+ uses: actions/checkout@v7
+ with:
+ ref: www
+ path: www
+
+ # Remove previews of closed PRs, so www stays below the 1 GB Pages limit.
+ - name: Update `www` branch
+ if: steps.meta.outputs.action != 'skip'
+ working-directory: www
+ env:
+ PR: ${{ steps.meta.outputs.pr }}
+ ACTION: ${{ steps.meta.outputs.action }}
+ run: |
+ set -euo pipefail
+ git config user.name github-actions
+ git config user.email github-actions@github.com
+ if [[ "${ACTION}" == "publish" ]]; then
+ mkdir -p "${PR}"
+ rsync -a --delete --no-links --exclude='.git*' ../preview/site/ "${PR}/"
+ git add -A "${PR}"
+ git commit -q -m "Update docs preview for PR #${PR}" || { echo "No changes."; exit 0; }
+ else
+ [[ -d "${PR}" ]] || { echo "No preview for PR #${PR}."; exit 0; }
+ git rm -r -q "${PR}"
+ git commit -q -m "www: remove docs preview for closed PR #${PR}"
+ fi
+ for attempt in 1 2 3; do
+ git push origin www && exit 0
+ echo "Push failed (attempt ${attempt}). Rebase on the latest www." >&2
+ git pull -q --rebase origin www
+ done
+ echo "::error::Push to www failed after 3 attempts"
+ exit 1
+
+ # One comment per PR. Later runs leave it as it is.
+ - name: Comment on PR with preview link
+ if: steps.meta.outputs.action == 'publish'
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ PR: ${{ steps.meta.outputs.pr }}
+ run: |
+ set -euo pipefail
+ MARKER=""
+ COMMENTS=$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR}/comments" --paginate --jq '.[].body')
+ if grep -qF "${MARKER}" <<< "${COMMENTS}"; then
+ exit 0
+ fi
+ gh pr comment "${PR}" -R "${GITHUB_REPOSITORY}" --body "${MARKER}
+ Documentation preview, available a few minutes after each push:
+
+
Open WWW preview
"
diff --git a/.github/workflows/pr-docs-preview.yml b/.github/workflows/pr-docs-preview.yml
index 9b9433c7f..4928aa172 100644
--- a/.github/workflows/pr-docs-preview.yml
+++ b/.github/workflows/pr-docs-preview.yml
@@ -1,39 +1,35 @@
name: Create docs preview on PR
+# Build only, with a read-only token, so fork PRs work too.
+# pr-docs-preview-publish.yml publishes the result and comments on the PR.
+
on:
pull_request:
types: [opened, reopened, synchronize, labeled, closed]
- workflow_dispatch:
permissions:
- contents: write
- pull-requests: write
+ contents: read
jobs:
docs-preview:
- # Build/publish a preview only while the PR is active; on close we clean up instead.
- if: github.event.action != 'closed'
runs-on: ubuntu-latest
steps:
- name: Checkout PR branch (documentation)
+ if: github.event.pull_request.state == 'open'
uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.head.sha }}
path: documentation
- - name: Checkout `www` branch
- uses: actions/checkout@v7
- with:
- ref: www
- path: www
-
- - name: Set up Python 3.8
+ - name: Set up Python
+ if: github.event.pull_request.state == 'open'
uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Cache pip dependencies
+ if: github.event.pull_request.state == 'open'
uses: actions/cache@v6
with:
path: ~/.cache/pip
@@ -42,6 +38,7 @@ jobs:
${{ runner.os }}-pip-
- name: Install dependencies
+ if: github.event.pull_request.state == 'open'
working-directory: documentation
run: |
python -m pip install --upgrade pip
@@ -49,72 +46,23 @@ jobs:
pip install -r requirements.txt
- name: Build docs with MkDocs
+ if: github.event.pull_request.state == 'open'
working-directory: documentation
- run: mkdocs build --clean
-
- # Publishing the preview and commenting need a writable GITHUB_TOKEN,
- # which pull_request from a fork does not get (token is read-only there).
- # Skip these steps for fork PRs so the check stays green after the build
- # step has already validated the docs; internal branches still publish.
- - name: Publish to `www` branch
- if: github.event.pull_request.head.repo.full_name == github.repository
- working-directory: www
- run: |
- mkdir -p "${{ github.event.number }}"
- rsync -av ../documentation/site/. "${{ github.event.number }}"
- git config user.name github-actions
- git config user.email github-actions@github.com
- git add .
- git commit -m "Update docs preview for PR #${{ github.event.number }}" || true
- git push origin www || true
-
- - name: Comment on PR with preview link
- if: github.event.pull_request.head.repo.full_name == github.repository
- uses: devindford/Append_PR_Comment@32dd2619cd96ac8da9907c416c992fe265233ca8 # v1.1.3
- with:
- repo-token: ${{ secrets.GITHUB_TOKEN }}
- body-template: |
- [](https://github.com/armbian/documentation/actions/workflows/pr-docs-preview.yml)
-
- Documentation website preview will be available shortly:
+ run: mkdocs build --clean --site-dir ../preview/site
-
Open WWW preview
- body-update-action: suffix
-
- # When a PR is closed (merged or not), delete its preview build from the `www`
- # branch so previews stop accumulating there. Without this, every PR left a
- # full site copy under www// forever, which grew the GitHub Pages source
- # past its 1 GB limit and broke deployment.
- cleanup-preview:
- if: github.event.action == 'closed' && github.event.pull_request.head.repo.full_name == github.repository
- runs-on: ubuntu-latest
- steps:
- - name: Checkout `www` branch
- uses: actions/checkout@v7
- with:
- ref: www
-
- - name: Remove preview for closed PR
+ # On a closed PR, only the metadata goes up, so the publish workflow removes the preview.
+ - name: Write preview metadata
env:
PR: ${{ github.event.number }}
+ ACTION: ${{ github.event.pull_request.state == 'open' && 'publish' || 'remove' }}
run: |
- set -euo pipefail
- if [ ! -d "${PR}" ]; then
- echo "No preview directory for PR #${PR}; nothing to do."
- exit 0
- fi
- git config user.name github-actions
- git config user.email github-actions@github.com
- git rm -r --quiet "${PR}"
- git commit -m "www: remove docs preview for closed PR #${PR}"
- # Retry against concurrent preview pushes from other PRs.
- for attempt in 1 2 3; do
- if git push origin www; then
- echo "Removed preview for PR #${PR}."
- exit 0
- fi
- echo "Push failed (attempt ${attempt}); rebasing on latest www..." >&2
- git pull --rebase origin www
- done
- echo "Failed to push preview removal for PR #${PR} after 3 attempts." >&2
- exit 1
\ No newline at end of file
+ mkdir -p preview
+ echo "${PR}" > preview/pr
+ echo "${ACTION}" > preview/action
+
+ - name: Upload preview
+ uses: actions/upload-artifact@v7
+ with:
+ name: docs-preview
+ path: preview/
+ retention-days: 1