From 67b1ff8229e5ae93af0c96a85cd2c81f4462f373 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 04:21:42 +0000 Subject: [PATCH 1/4] Initial plan From adde8010b6d1c4a31fc7ed9be8f66843949fb276 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 04:49:58 +0000 Subject: [PATCH 2/4] ci: add documentation preview workflow for PRs Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com> --- .github/workflows/docs-preview.yml | 192 +++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 .github/workflows/docs-preview.yml diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml new file mode 100644 index 000000000..afcd11eda --- /dev/null +++ b/.github/workflows/docs-preview.yml @@ -0,0 +1,192 @@ +name: Documentation Preview + +on: + pull_request: + branches: [main] + paths: + - 'docs-site/**' + - 'docs/**' + - '.github/workflows/docs-preview.yml' + pull_request_target: + types: [closed] + paths: + - 'docs-site/**' + - 'docs/**' + +permissions: + contents: read + pull-requests: write + deployments: write + +concurrency: + group: docs-preview-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +jobs: + build-and-preview: + name: Build and Deploy Preview + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' && github.event.action != 'closed' + timeout-minutes: 10 + + steps: + - name: Checkout + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 + + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: docs-site/package-lock.json + + - name: Install dependencies + run: | + cd docs-site + npm ci + + - name: Build documentation for preview + run: | + cd docs-site + npm run build + env: + # Use PR-specific base path for preview + NODE_ENV: production + + - name: Upload preview artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + id: upload-artifact + with: + name: docs-preview-pr-${{ github.event.pull_request.number }} + path: docs-site/dist + retention-days: 7 + + - name: Create deployment + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + id: deployment + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { data: deployment } = await github.rest.repos.createDeployment({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: context.payload.pull_request.head.sha, + environment: `docs-preview-pr-${context.payload.pull_request.number}`, + auto_merge: false, + required_contexts: [], + transient_environment: true, + production_environment: false, + description: `Documentation preview for PR #${context.payload.pull_request.number}` + }); + + // Set deployment status to success with artifact link + const artifactUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}/artifacts`; + + await github.rest.repos.createDeploymentStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: deployment.id, + state: 'success', + environment_url: artifactUrl, + log_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + description: 'Documentation preview is ready' + }); + + return { deploymentId: deployment.id, artifactUrl }; + + - name: Comment on PR + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = context.payload.pull_request.number; + const runId = context.runId; + const artifactUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/artifacts`; + + const commentBody = `## 📚 Documentation Preview + + A preview of your documentation changes has been built and is available for download. + + | Resource | Link | + |----------|------| + | 📦 **Preview Artifact** | [Download docs-preview-pr-${prNumber}](${artifactUrl}) | + | 🔧 **Workflow Run** | [View Build Logs](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}) | + +
+ How to view the preview + + 1. Download the artifact from the link above + 2. Extract the zip file + 3. Open \`index.html\` in your browser, or serve locally with: + \`\`\`bash + npx serve ./dist + \`\`\` + +
+ + --- + *Preview generated from commit ${context.payload.pull_request.head.sha.substring(0, 7)}*`; + + // Find existing preview comment + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + }); + + const botComment = comments.find(comment => + comment.user.type === 'Bot' && + comment.body.includes('Documentation Preview') + ); + + if (botComment) { + // Update existing comment + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: commentBody + }); + } else { + // Create new comment + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: commentBody + }); + } + + cleanup: + name: Cleanup Preview Deployment + runs-on: ubuntu-latest + if: github.event_name == 'pull_request_target' && github.event.action == 'closed' + + steps: + - name: Deactivate deployment + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = context.payload.pull_request.number; + const environmentName = `docs-preview-pr-${prNumber}`; + + // List deployments for this PR's environment + const { data: deployments } = await github.rest.repos.listDeployments({ + owner: context.repo.owner, + repo: context.repo.repo, + environment: environmentName + }); + + // Set all deployments to inactive + for (const deployment of deployments) { + await github.rest.repos.createDeploymentStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: deployment.id, + state: 'inactive', + description: `PR #${prNumber} was closed` + }); + } + + console.log(`Deactivated ${deployments.length} deployment(s) for environment: ${environmentName}`); From b1cca332ad44ea101476d7329e96448bdb6d36ce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 04:51:51 +0000 Subject: [PATCH 3/4] ci: address review comments for docs preview workflow Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com> --- .github/workflows/docs-preview.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml index afcd11eda..f15f43de3 100644 --- a/.github/workflows/docs-preview.yml +++ b/.github/workflows/docs-preview.yml @@ -9,9 +9,6 @@ on: - '.github/workflows/docs-preview.yml' pull_request_target: types: [closed] - paths: - - 'docs-site/**' - - 'docs/**' permissions: contents: read @@ -30,7 +27,7 @@ jobs: timeout-minutes: 10 steps: - - name: Checkout + - name: Checkout PR source code uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 - name: Setup Node.js @@ -119,7 +116,7 @@ jobs: 2. Extract the zip file 3. Open \`index.html\` in your browser, or serve locally with: \`\`\`bash - npx serve ./dist + npx serve . \`\`\` From eef90e18affccfe497c4d90ff3aec8dbf0379b03 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 09:15:22 +0000 Subject: [PATCH 4/4] ci: convert docs preview to agentic workflow with playwright Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com> --- .github/workflows/docs-preview.yml | 137 +++++++++++++++++++++++++---- 1 file changed, 118 insertions(+), 19 deletions(-) diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml index f15f43de3..927676012 100644 --- a/.github/workflows/docs-preview.yml +++ b/.github/workflows/docs-preview.yml @@ -3,10 +3,6 @@ name: Documentation Preview on: pull_request: branches: [main] - paths: - - 'docs-site/**' - - 'docs/**' - - '.github/workflows/docs-preview.yml' pull_request_target: types: [closed] @@ -20,11 +16,13 @@ concurrency: cancel-in-progress: true jobs: - build-and-preview: - name: Build and Deploy Preview + build-preview: + name: Build Documentation Preview runs-on: ubuntu-latest if: github.event_name == 'pull_request' && github.event.action != 'closed' timeout-minutes: 10 + outputs: + artifact-url: ${{ steps.upload-artifact.outputs.artifact-url }} steps: - name: Checkout PR source code @@ -47,7 +45,6 @@ jobs: cd docs-site npm run build env: - # Use PR-specific base path for preview NODE_ENV: production - name: Upload preview artifact @@ -76,7 +73,6 @@ jobs: description: `Documentation preview for PR #${context.payload.pull_request.number}` }); - // Set deployment status to success with artifact link const artifactUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}/artifacts`; await github.rest.repos.createDeploymentStatus({ @@ -91,38 +87,129 @@ jobs: return { deploymentId: deployment.id, artifactUrl }; - - name: Comment on PR + preview-with-copilot: + name: Generate Documentation Preview with Copilot + runs-on: ubuntu-latest + needs: build-preview + timeout-minutes: 15 + + steps: + - name: Checkout PR source code + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 + + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: '20' + + - name: Download preview artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: docs-preview-pr-${{ github.event.pull_request.number }} + path: docs-preview + + - name: Install serve + run: npm install -g serve + + - name: Start local preview server + run: | + serve docs-preview -l 3000 & + echo "SERVER_PID=$!" >> "$GITHUB_ENV" + # Wait for server to be ready + sleep 3 + + - name: Generate preview screenshot and comment uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | + const { execSync } = require('child_process'); + const fs = require('fs'); + const path = require('path'); + + // Install Playwright + execSync('npx playwright install chromium', { stdio: 'inherit' }); + + // Create screenshot script + const screenshotScript = ` + const { chromium } = require('playwright'); + + (async () => { + const browser = await chromium.launch(); + const page = await browser.newPage(); + await page.setViewportSize({ width: 1280, height: 800 }); + + // Navigate to the local preview + await page.goto('http://localhost:3000/gh-aw-firewall/', { + waitUntil: 'networkidle', + timeout: 30000 + }); + + // Wait for content to load + await page.waitForTimeout(2000); + + // Take screenshot + await page.screenshot({ + path: 'docs-preview-screenshot.png', + fullPage: false + }); + + // Get page title for the comment + const title = await page.title(); + console.log('PAGE_TITLE=' + title); + + await browser.close(); + })(); + `; + + fs.writeFileSync('take-screenshot.js', screenshotScript); + + // Run screenshot script + const output = execSync('node take-screenshot.js', { encoding: 'utf-8' }); + const titleMatch = output.match(/PAGE_TITLE=(.+)/); + const pageTitle = titleMatch ? titleMatch[1] : 'Documentation Preview'; + const prNumber = context.payload.pull_request.number; const runId = context.runId; const artifactUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/artifacts`; + const commitSha = context.payload.pull_request.head.sha.substring(0, 7); + // Create comment with link to screenshot artifact const commentBody = `## 📚 Documentation Preview - A preview of your documentation changes has been built and is available for download. + A preview of the documentation has been built and reviewed using Playwright. + + ### 📸 Preview Screenshot + + The documentation homepage screenshot has been captured and is available as an artifact. + + **Page Title:** ${pageTitle} + + > 💡 Download the \`docs-preview-screenshot-pr-${prNumber}\` artifact to view the screenshot. + + ### Resources | Resource | Link | |----------|------| - | 📦 **Preview Artifact** | [Download docs-preview-pr-${prNumber}](${artifactUrl}) | + | 📷 **Screenshot** | [Download Screenshot](${artifactUrl}) | + | 📦 **Full Preview** | [Download docs-preview-pr-${prNumber}](${artifactUrl}) | | 🔧 **Workflow Run** | [View Build Logs](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}) |
- How to view the preview + How to view the full preview locally - 1. Download the artifact from the link above + 1. Download the \`docs-preview-pr-${prNumber}\` artifact from the link above 2. Extract the zip file - 3. Open \`index.html\` in your browser, or serve locally with: + 3. Serve locally with: \`\`\`bash npx serve . \`\`\` + 4. Open http://localhost:3000/gh-aw-firewall/ in your browser
--- - *Preview generated from commit ${context.payload.pull_request.head.sha.substring(0, 7)}*`; + *Preview generated from commit ${commitSha} using Playwright*`; // Find existing preview comment const { data: comments } = await github.rest.issues.listComments({ @@ -137,7 +224,6 @@ jobs: ); if (botComment) { - // Update existing comment await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, @@ -145,7 +231,6 @@ jobs: body: commentBody }); } else { - // Create new comment await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, @@ -153,6 +238,22 @@ jobs: body: commentBody }); } + + console.log('Documentation preview comment posted successfully!'); + + - name: Upload screenshot artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: docs-preview-screenshot-pr-${{ github.event.pull_request.number }} + path: docs-preview-screenshot.png + retention-days: 7 + + - name: Stop preview server + if: always() + run: | + if [ -n "$SERVER_PID" ]; then + kill "$SERVER_PID" || true + fi cleanup: name: Cleanup Preview Deployment @@ -168,14 +269,12 @@ jobs: const prNumber = context.payload.pull_request.number; const environmentName = `docs-preview-pr-${prNumber}`; - // List deployments for this PR's environment const { data: deployments } = await github.rest.repos.listDeployments({ owner: context.repo.owner, repo: context.repo.repo, environment: environmentName }); - // Set all deployments to inactive for (const deployment of deployments) { await github.rest.repos.createDeploymentStatus({ owner: context.repo.owner,