diff --git a/.github/prompts/copilot-visual-review.md b/.github/prompts/copilot-visual-review.md index 2ce9de154..7cc8f0982 100644 --- a/.github/prompts/copilot-visual-review.md +++ b/.github/prompts/copilot-visual-review.md @@ -21,14 +21,64 @@ For each preview URL, verify: - [ ] **Navigation correct** — Sidebar entries link to the right pages and the page appears in the expected location -## Output +## Output Format + +**IMPORTANT:** Your response must follow this exact format for clear completion signaling. + +### Response Header + +Start your response with this header (fill in the values): + +```markdown +## 🔍 Visual Review Complete — Copilot + +| Status | Details | +|--------|---------| +| **Result** | ✅ APPROVED / ⚠️ CHANGES REQUESTED / 🔍 NEEDS HUMAN REVIEW | +| **Pages Reviewed** | X page(s) | +| **Issues Found** | X BLOCKING, X WARNING, X INFO | +| **Reviewed** | YYYY-MM-DD HH:MM UTC | +``` + +### Result Rules + +- **APPROVED** — Zero BLOCKING issues found +- **CHANGES REQUESTED** — One or more BLOCKING issues found +- **NEEDS HUMAN REVIEW** — Cannot determine severity or page didn't load + +### Issue Format + +List any issues found using this format: + +```markdown +### Issues Found + +#### BLOCKING + +- **[page-url]** — Description of the issue + - Suggested fix: ... + +#### WARNING + +- **[page-url]** — Description of the issue + +#### INFO + +- **[page-url]** — Observation +``` + +If no issues are found, write: + +```markdown +### Issues Found + +No issues found. All pages pass visual review. +``` + +## Severity Definitions Follow the shared review comment format, severity definitions, and label mapping in [templates/review-comment.md](../templates/review-comment.md). -Adapt the "Files Reviewed" section to list preview URLs instead of file -paths. - ## Preview URLs - diff --git a/.github/scripts/preview-comment.js b/.github/scripts/preview-comment.js index 1895eeb79..8c3966600 100644 --- a/.github/scripts/preview-comment.js +++ b/.github/scripts/preview-comment.js @@ -44,12 +44,14 @@ export function generatePreviewComment(options) { const timestamp = new Date().toISOString().replace('T', ' ').split('.')[0] + ' UTC'; - let body = `${COMMENT_MARKER}\n## PR Preview\n\n`; + // Agent persona header for clear identification + let body = `${COMMENT_MARKER}\n## 📦 PR Preview — Preview Bot\n\n`; switch (status) { case 'success': body += `| Status | Details |\n`; body += `|--------|----------|\n`; + body += `| **Result** | ✅ DEPLOYED |\n`; body += `| **Preview** | [View preview](${previewUrl}) |\n`; body += `| **Pages** | ${pages.length} page(s) deployed |\n`; if (buildTime) { @@ -74,30 +76,42 @@ export function generatePreviewComment(options) { case 'pending': if (needsInput) { + body += `| Status | Details |\n`; + body += `|--------|----------|\n`; + body += `| **Result** | ⏳ NEEDS INPUT |\n`; + body += `| **Checked** | ${timestamp} |\n\n`; body += `### Preview pages needed\n\n`; body += `This PR changes layout/asset files but doesn't specify which pages to preview.\n\n`; body += `**To generate a preview**, add documentation URLs to your PR description, for example:\n`; body += `\`\`\`\nPlease review:\n- https://docs.influxdata.com/influxdb3/core/get-started/\n- /telegraf/v1/plugins/\n\`\`\`\n\n`; - body += `Then re-run the workflow or push a new commit.\n\n`; - body += `---\nLast checked: ${timestamp}`; + body += `Then re-run the workflow or push a new commit.`; } else { - body += `⏳ **Preview building...**\n\n`; - body += `---\nStarted: ${timestamp}`; + body += `| Status | Details |\n`; + body += `|--------|----------|\n`; + body += `| **Result** | ⏳ BUILDING |\n`; + body += `| **Started** | ${timestamp} |\n\n`; + body += `Preview is building...`; } break; case 'failed': - body += `### Preview failed\n\n`; - body += `The preview build encountered an error:\n\n`; + body += `| Status | Details |\n`; + body += `|--------|----------|\n`; + body += `| **Result** | ❌ FAILED |\n`; + body += `| **Failed** | ${timestamp} |\n\n`; + body += `### Build Error\n\n`; body += `\`\`\`\n${sanitizeForCodeBlock(errorMessage)}\n\`\`\`\n\n`; - body += `[View workflow logs](https://github.com/influxdata/docs-v2/actions)\n\n`; - body += `---\nFailed: ${timestamp}`; + body += `[View workflow logs](https://github.com/influxdata/docs-v2/actions)`; break; case 'skipped': - body += `### Preview skipped\n\n`; - body += `${sanitizeForCodeBlock(skipReason || 'No previewable changes detected.', 500)}\n\n`; - body += `---\nChecked: ${timestamp}`; + // Skip reasons are controlled strings from the workflow, plain text sanitization is sufficient + const safeSkipReason = (skipReason || 'No previewable changes detected.').substring(0, 200); + body += `| Status | Details |\n`; + body += `|--------|----------|\n`; + body += `| **Result** | ⏭️ SKIPPED |\n`; + body += `| **Reason** | ${safeSkipReason} |\n`; + body += `| **Checked** | ${timestamp} |`; break; } diff --git a/.github/scripts/resolve-review-urls.js b/.github/scripts/resolve-review-urls.js index 886679d35..173f18f4f 100644 --- a/.github/scripts/resolve-review-urls.js +++ b/.github/scripts/resolve-review-urls.js @@ -7,6 +7,8 @@ * Outputs (for GitHub Actions): * - urls: JSON array of URL paths * - url-count: Number of URLs + * - skipped: Boolean indicating if review was skipped + * - skip-reason: Reason for skipping (if applicable) */ import { appendFileSync } from 'fs'; @@ -58,4 +60,17 @@ const urls = [...new Set([...homePageUrls, ...contentUrls])].slice( appendFileSync(GITHUB_OUTPUT, `urls=${JSON.stringify(urls)}\n`); appendFileSync(GITHUB_OUTPUT, `url-count=${urls.length}\n`); +// Output skip status for downstream jobs +if (urls.length === 0) { + appendFileSync(GITHUB_OUTPUT, `skipped=true\n`); + const skipReason = + changed.length === 0 + ? 'No content files changed in this PR' + : 'Changed files do not map to previewable URLs'; + appendFileSync(GITHUB_OUTPUT, `skip-reason=${skipReason}\n`); + console.log(`Visual review skipped: ${skipReason}`); +} else { + appendFileSync(GITHUB_OUTPUT, `skipped=false\n`); +} + console.log(`Detected ${urls.length} preview URLs`); diff --git a/.github/workflows/doc-review.yml b/.github/workflows/doc-review.yml index b6f79cffa..11c807b2a 100644 --- a/.github/workflows/doc-review.yml +++ b/.github/workflows/doc-review.yml @@ -38,6 +38,8 @@ jobs: outputs: urls: ${{ steps.detect.outputs.urls }} url-count: ${{ steps.detect.outputs.url-count }} + skipped: ${{ steps.detect.outputs.skipped }} + skip-reason: ${{ steps.detect.outputs.skip-reason }} steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: @@ -176,11 +178,6 @@ jobs: const prNumber = context.issue.number || Number(process.env.PR_NUMBER); const previewBase = `https://influxdata.github.io/docs-v2/pr-preview/pr-${prNumber}`; - // Build preview URL list - const urlList = urls - .map(u => `- [${u}](${previewBase}${u})`) - .join('\n'); - // Read the Copilot visual review template const template = fs.readFileSync( '.github/prompts/copilot-visual-review.md', @@ -188,26 +185,33 @@ jobs: ); const marker = ''; + const timestamp = new Date().toISOString().replace('T', ' ').split('.')[0] + ' UTC'; const body = [ marker, - '## Preview Pages for Review', + '## 🔍 Visual Review — Doc Review Bot', '', - `${urls.length} page(s) changed in this PR:`, + `| Status | Details |`, + `|--------|---------|`, + `| **Pages** | ${urls.length} page(s) to review |`, + `| **Preview** | See PR Preview comment above for full URL list |`, + `| **Requested** | ${timestamp} |`, '', - '
', - 'Preview URLs', - '', - urlList, - '', - '
', + '> **Note:** Preview URLs are listed in the **PR Preview** comment to avoid duplication.', + '> Click the preview link there to view the deployed pages.', '', '---', '', - `@github-copilot please review the preview pages listed above using the template below:`, + `@github-copilot please review the ${urls.length} preview pages for this PR using the checklist below:`, '', template.trim(), '', - urlList, + '### Pages to Review', + '', + ...urls.slice(0, 10).map(u => { + const pageUrl = `${previewBase}${u}`; + return `- \`${u}\` → [preview](${pageUrl})`; + }), + ...(urls.length > 10 ? [`- ... and ${urls.length - 10} more (see PR Preview comment)`] : []), ].join('\n'); // Update existing comment or create new one @@ -245,15 +249,28 @@ jobs: script: | const prNumber = context.issue.number || Number(process.env.PR_NUMBER); const marker = ''; + const timestamp = new Date().toISOString().replace('T', ' ').split('.')[0] + ' UTC'; const body = [ marker, - '## Visual Review Skipped', + '## 🔍 Visual Review — Doc Review Bot', '', - 'The PR preview deployment did not become available within 10 minutes.', - 'Visual review was skipped. The Copilot code review (Job 2) still ran.', + `| Status | Details |`, + `|--------|---------|`, + `| **Result** | ⏱️ TIMED OUT |`, + `| **Reason** | Preview deployment not available within 10 minutes |`, + `| **Checked** | ${timestamp} |`, '', - 'To trigger visual review manually, re-run this workflow after the', - 'preview is deployed.', + '### What This Means', + '', + '- Visual review was **skipped** because the preview was not deployed in time', + '- Copilot code review (diff-based) still ran independently', + '- Human reviewers should check the preview manually when available', + '', + '### Next Steps', + '', + 'To trigger visual review:', + '1. Wait for the PR Preview to deploy (check the PR Preview comment)', + '2. Re-run this workflow from the Actions tab', ].join('\n'); const { data: comments } = await github.rest.issues.listComments({ @@ -278,3 +295,72 @@ jobs: body, }); } + + # ----------------------------------------------------------------- + # Job 4: Report when visual review is skipped (no URLs to review) + # ----------------------------------------------------------------- + report-skipped: + runs-on: ubuntu-latest + permissions: + pull-requests: write + needs: resolve-urls + # GitHub Actions outputs are always strings; 'true' string comparison is intentional + if: needs.resolve-urls.result == 'success' && needs.resolve-urls.outputs.skipped == 'true' + steps: + - name: Post skipped notice + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + env: + PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }} + SKIP_REASON: ${{ needs.resolve-urls.outputs.skip-reason }} + with: + script: | + const prNumber = context.issue.number || Number(process.env.PR_NUMBER); + const skipReason = process.env.SKIP_REASON || 'No previewable content changes detected'; + const marker = ''; + const timestamp = new Date().toISOString().replace('T', ' ').split('.')[0] + ' UTC'; + const body = [ + marker, + '## 🔍 Visual Review — Doc Review Bot', + '', + `| Status | Details |`, + `|--------|---------|`, + `| **Result** | ⏭️ SKIPPED |`, + `| **Reason** | ${skipReason} |`, + `| **Checked** | ${timestamp} |`, + '', + '### What This Means', + '', + '- No preview pages were identified for visual review', + '- This is expected for PRs that only change non-content files', + '- Copilot code review (diff-based) still runs independently', + '', + '### If You Expected Visual Review', + '', + 'Check that your PR includes changes to files in `content/` that map to', + 'published documentation pages.', + ].join('\n'); + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + }); + const existing = comments.find(c => c.body.includes(marker)); + + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body, + }); + } + + core.info(`Posted visual review skipped notice: ${skipReason}`); diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml index 85aaf11d1..191a52720 100644 --- a/.github/workflows/pr-preview.yml +++ b/.github/workflows/pr-preview.yml @@ -58,11 +58,12 @@ jobs: const existing = comments.find(c => c.body.includes(marker)); if (!existing) { + const timestamp = new Date().toISOString().replace('T', ' ').split('.')[0] + ' UTC'; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, - body: `${marker}\n## 📝 PR Preview Not Available\n\nPR previews are not available for pull requests from forks due to GitHub Actions security restrictions.\n\nTo preview your changes locally, run:\n\`\`\`bash\nnpx hugo server\n\`\`\`\n\nOnce merged, your changes will be visible on the production site.` + body: `${marker}\n## 📦 PR Preview — Preview Bot\n\n| Status | Details |\n|--------|----------|\n| **Result** | ⏭️ NOT AVAILABLE |\n| **Reason** | Fork PR (security restriction) |\n| **Checked** | ${timestamp} |\n\n### Local Preview\n\nPR previews are not available for pull requests from forks due to GitHub Actions security restrictions.\n\nTo preview your changes locally, run:\n\`\`\`bash\nnpx hugo server\n\`\`\`\n\nOnce merged, your changes will be visible on the production site.` }); }