From df5b0c97c437bc06a0a2f2599155847fc15c68e1 Mon Sep 17 00:00:00 2001 From: "A.Arnold" Date: Fri, 24 Jul 2026 20:33:47 +0100 Subject: [PATCH 1/2] Update Signed-off-by: A.Arnold --- .github/workflows/pr-preview-cleanup.yml | 63 ++++++++++ .github/workflows/pr-preview.yml | 144 +++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 .github/workflows/pr-preview-cleanup.yml create mode 100644 .github/workflows/pr-preview.yml diff --git a/.github/workflows/pr-preview-cleanup.yml b/.github/workflows/pr-preview-cleanup.yml new file mode 100644 index 00000000000..79596e2704a --- /dev/null +++ b/.github/workflows/pr-preview-cleanup.yml @@ -0,0 +1,63 @@ +name: PR Preview Cleanup + +on: + pull_request: + types: [closed] + +permissions: + contents: write + pull-requests: write + +jobs: + cleanup: + name: Remove Preview + runs-on: ubuntu-latest + + steps: + - name: Checkout gh-pages branch + uses: actions/checkout@v4 + with: + ref: gh-pages + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 1 + + - name: Delete preview directory + env: + PR_NUMBER: ${{ github.event.number }} + run: | + PREVIEW_DIR="pr-previews/${PR_NUMBER}" + if [ -d "${PREVIEW_DIR}" ]; then + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git rm -rf "${PREVIEW_DIR}" + git commit -m "chore: remove preview for PR #${PR_NUMBER}" + git push origin gh-pages + else + echo "No preview directory found for PR #${PR_NUMBER}, nothing to do." + fi + + - name: Update preview comment to reflect closure + uses: actions/github-script@v7 + env: + PR_NUMBER: ${{ github.event.number }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = process.env.PR_NUMBER; + const owner = context.repo.owner; + const repo = context.repo.repo; + + const marker = ''; + + const { data: comments } = await github.rest.issues.listComments({ + owner, repo, issue_number: prNumber, + }); + + const existing = comments.find(c => c.body.includes(marker)); + if (existing) { + await github.rest.issues.updateComment({ + owner, repo, + comment_id: existing.id, + body: marker + '\n## 📄 Documentation Preview\n\n> This preview has been removed because the PR was closed.', + }); + } diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml new file mode 100644 index 00000000000..a201bb80d79 --- /dev/null +++ b/.github/workflows/pr-preview.yml @@ -0,0 +1,144 @@ +name: PR Preview + +on: + pull_request: + types: [opened, synchronize, reopened] + paths: + - 'docs/**' + - 'assemblies/**' + - 'topics/**' + - '_includes/**' + - '_layouts/**' + - '_sass/**' + - 'assets/**' + - '_config.yml' + - 'Gemfile' + - 'index.md' + - '.github/workflows/pr-preview.yml' + +# Needed so the deploy job can push to gh-pages and post comments +permissions: + contents: write + pull-requests: write + +jobs: + build-and-deploy: + name: Build & Deploy Preview + runs-on: ubuntu-latest + + steps: + - name: Checkout PR branch + uses: actions/checkout@v4 + with: + # Full history lets Jekyll see git metadata (last-modified dates etc.) + fetch-depth: 0 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2' + bundler-cache: true # caches gems between runs automatically + + - name: Install system dependencies + run: sudo apt-get install -y asciidoctor + + - name: Build site with preview base URL + env: + JEKYLL_ENV: production + PR_NUMBER: ${{ github.event.number }} + run: | + bundle exec jekyll build \ + --baseurl "/mta-documentation/pr-previews/${PR_NUMBER}" \ + --destination _site + + - name: Deploy preview to gh-pages + env: + PR_NUMBER: ${{ github.event.number }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Configure git identity for the push + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Stash the build output before switching branches + mkdir -p /tmp/pr-preview + cp -r _site/. /tmp/pr-preview/ + + # Fetch or create the gh-pages branch without touching the working tree + git fetch origin gh-pages 2>/dev/null || true + if git show-ref --verify --quiet refs/remotes/origin/gh-pages; then + git checkout -B gh-pages origin/gh-pages + else + git checkout --orphan gh-pages + git rm -rf . --quiet + echo "GitHub Pages for mta-documentation" > README.md + git add README.md + git commit -m "chore: initialise gh-pages branch" + fi + + # Place the preview under a predictable subdirectory + PREVIEW_DIR="pr-previews/${PR_NUMBER}" + rm -rf "${PREVIEW_DIR}" + mkdir -p "${PREVIEW_DIR}" + cp -r /tmp/pr-preview/. "${PREVIEW_DIR}/" + + # Commit and push only if there are changes + git add "${PREVIEW_DIR}" + if git diff --staged --quiet; then + echo "No changes to deploy." + else + COMMIT_SHA="${{ github.event.pull_request.head.sha }}" + git commit -m "preview: PR #${PR_NUMBER} @ ${COMMIT_SHA::7}" + git push origin gh-pages + fi + + - name: Post or update preview comment + uses: actions/github-script@v7 + env: + PR_NUMBER: ${{ github.event.number }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = process.env.PR_NUMBER; + const sha = context.payload.pull_request.head.sha.substring(0, 7); + const owner = context.repo.owner; + const repo = context.repo.repo; + + const previewUrl = + `https://${owner}.github.io/${repo}/pr-previews/${prNumber}/`; + + const body = [ + `## 📄 Documentation Preview`, + ``, + `| | |`, + `|---|---|`, + `| **Preview URL** | ${previewUrl} |`, + `| **Commit** | \`${sha}\` |`, + `| **Updated** | ${new Date().toUTCString()} |`, + ``, + `> Preview updates automatically on every push to this PR.`, + `> It will be removed when the PR is closed.`, + ].join('\n'); + + const marker = ''; + const markedBody = marker + '\n' + body; + + const { data: comments } = await github.rest.issues.listComments({ + owner, repo, issue_number: prNumber, + }); + + const existing = comments.find(c => c.body.includes(marker)); + + if (existing) { + await github.rest.issues.updateComment({ + owner, repo, + comment_id: existing.id, + body: markedBody, + }); + } else { + await github.rest.issues.createComment({ + owner, repo, + issue_number: prNumber, + body: markedBody, + }); + } From de02140833d6314ca1ae93f5c11ee3297fe1f120 Mon Sep 17 00:00:00 2001 From: "A.Arnold" Date: Mon, 27 Jul 2026 11:55:18 +0100 Subject: [PATCH 2/2] Update Signed-off-by: A.Arnold --- .github/workflows/pr-preview-cleanup.yml | 20 +++++++++++++-- .github/workflows/pr-preview.yml | 32 ++++++++++++++++-------- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/.github/workflows/pr-preview-cleanup.yml b/.github/workflows/pr-preview-cleanup.yml index 79596e2704a..a3ce6112dd0 100644 --- a/.github/workflows/pr-preview-cleanup.yml +++ b/.github/workflows/pr-preview-cleanup.yml @@ -8,10 +8,17 @@ permissions: contents: write pull-requests: write +# Use the same concurrency group as the deploy workflow so cleanup and +# deploy pushes to gh-pages are always serialized. +concurrency: + group: gh-pages-deploy + cancel-in-progress: false + jobs: cleanup: name: Remove Preview runs-on: ubuntu-latest + if: github.event.pull_request.head.repo.full_name == github.repository steps: - name: Checkout gh-pages branch @@ -20,6 +27,7 @@ jobs: ref: gh-pages token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 1 + persist-credentials: true - name: Delete preview directory env: @@ -31,7 +39,14 @@ jobs: git config user.email "github-actions[bot]@users.noreply.github.com" git rm -rf "${PREVIEW_DIR}" git commit -m "chore: remove preview for PR #${PR_NUMBER}" - git push origin gh-pages + + for attempt in 1 2 3; do + git fetch origin gh-pages + git rebase origin/gh-pages 2>/dev/null || { git rebase --abort; true; } + git push origin gh-pages && break + echo "Push attempt ${attempt} failed, retrying..." + sleep $((attempt * 5)) + done else echo "No preview directory found for PR #${PR_NUMBER}, nothing to do." fi @@ -49,7 +64,8 @@ jobs: const marker = ''; - const { data: comments } = await github.rest.issues.listComments({ + // Paginate to handle PRs with more than 30 comments + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number: prNumber, }); diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml index a201bb80d79..b316884eeae 100644 --- a/.github/workflows/pr-preview.yml +++ b/.github/workflows/pr-preview.yml @@ -16,28 +16,35 @@ on: - 'index.md' - '.github/workflows/pr-preview.yml' -# Needed so the deploy job can push to gh-pages and post comments permissions: contents: write pull-requests: write +# Serialize all gh-pages pushes across both workflows to prevent +# non-fast-forward failures when two PRs deploy at the same time. +concurrency: + group: gh-pages-deploy + cancel-in-progress: false + jobs: build-and-deploy: name: Build & Deploy Preview runs-on: ubuntu-latest + # Skip fork PRs — GITHUB_TOKEN is read-only for forks and the push would fail. + if: github.event.pull_request.head.repo.full_name == github.repository steps: - name: Checkout PR branch uses: actions/checkout@v4 with: - # Full history lets Jekyll see git metadata (last-modified dates etc.) fetch-depth: 0 + persist-credentials: true - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: '3.2' - bundler-cache: true # caches gems between runs automatically + bundler-cache: true - name: Install system dependencies run: sudo apt-get install -y asciidoctor @@ -54,17 +61,13 @@ jobs: - name: Deploy preview to gh-pages env: PR_NUMBER: ${{ github.event.number }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - # Configure git identity for the push git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - # Stash the build output before switching branches mkdir -p /tmp/pr-preview cp -r _site/. /tmp/pr-preview/ - # Fetch or create the gh-pages branch without touching the working tree git fetch origin gh-pages 2>/dev/null || true if git show-ref --verify --quiet refs/remotes/origin/gh-pages; then git checkout -B gh-pages origin/gh-pages @@ -76,20 +79,26 @@ jobs: git commit -m "chore: initialise gh-pages branch" fi - # Place the preview under a predictable subdirectory PREVIEW_DIR="pr-previews/${PR_NUMBER}" rm -rf "${PREVIEW_DIR}" mkdir -p "${PREVIEW_DIR}" cp -r /tmp/pr-preview/. "${PREVIEW_DIR}/" - # Commit and push only if there are changes git add "${PREVIEW_DIR}" if git diff --staged --quiet; then echo "No changes to deploy." else COMMIT_SHA="${{ github.event.pull_request.head.sha }}" git commit -m "preview: PR #${PR_NUMBER} @ ${COMMIT_SHA::7}" - git push origin gh-pages + + # Retry push up to 3 times in case of a concurrent non-fast-forward + for attempt in 1 2 3; do + git fetch origin gh-pages + git rebase origin/gh-pages 2>/dev/null || { git rebase --abort; true; } + git push origin gh-pages && break + echo "Push attempt ${attempt} failed, retrying..." + sleep $((attempt * 5)) + done fi - name: Post or update preview comment @@ -123,7 +132,8 @@ jobs: const marker = ''; const markedBody = marker + '\n' + body; - const { data: comments } = await github.rest.issues.listComments({ + // Paginate to handle PRs with more than 30 comments + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number: prNumber, });