diff --git a/.circleci/config.yml b/.circleci/config.yml index d2164e913..42dcf390a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,7 +28,7 @@ jobs: command: cd api-docs && yarn install - run: name: Build API documentation scripts - command: npx tsc --project api-docs/scripts/tsconfig.json + command: yarn build:api-docs:scripts - run: name: Generate API documentation command: cd api-docs && bash generate-api-docs.sh diff --git a/.github/scripts/detect-preview-pages.js b/.github/scripts/detect-preview-pages.js index ac412a6ca..afaf5f0c5 100644 --- a/.github/scripts/detect-preview-pages.js +++ b/.github/scripts/detect-preview-pages.js @@ -122,7 +122,9 @@ function detectApiPages(apiDocFiles) { pages.add(urlPath); } } catch (err) { - console.log(` ⚠️ Could not read or parse ${configPath}: ${err.message}`); + console.log( + ` ⚠️ Could not read or parse ${configPath}: ${err.message}` + ); } } @@ -187,10 +189,14 @@ function main() { // Strategy 2: API doc changes - auto-detect affected API pages if (changes.apiDocs.length > 0) { - console.log('📋 API doc changes detected, auto-detecting affected pages...'); + console.log( + '📋 API doc changes detected, auto-detecting affected pages...' + ); const apiPages = detectApiPages(changes.apiDocs); if (apiPages.length > 0) { - console.log(` Found ${apiPages.length} affected API page(s): ${apiPages.join(', ')}`); + console.log( + ` Found ${apiPages.length} affected API page(s): ${apiPages.join(', ')}` + ); pagesToDeploy = [...new Set([...pagesToDeploy, ...apiPages])]; } } diff --git a/.github/workflows/doc-review.yml b/.github/workflows/doc-review.yml index b6f79cffa..8d9227bb0 100644 --- a/.github/workflows/doc-review.yml +++ b/.github/workflows/doc-review.yml @@ -119,6 +119,7 @@ jobs: permissions: contents: read pull-requests: write + statuses: read needs: resolve-urls if: needs.resolve-urls.result == 'success' && fromJson(needs.resolve-urls.outputs.url-count) > 0 steps: @@ -130,30 +131,48 @@ jobs: - name: Wait for preview deployment id: wait - env: - PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }} - run: | - PREVIEW_URL="https://influxdata.github.io/docs-v2/pr-preview/pr-${PR_NUMBER}/" - TIMEOUT=600 # 10 minutes - INTERVAL=15 - ELAPSED=0 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + script: | + const TIMEOUT_MS = 180_000; // 3 minutes + const INTERVAL_MS = 15_000; // 15 seconds + const CONTEXT = 'preview/deploy'; + const sha = context.sha; + const start = Date.now(); - echo "Waiting for preview at ${PREVIEW_URL}" + while (Date.now() - start < TIMEOUT_MS) { + const { data: statuses } = await github.rest.repos.listCommitStatusesForRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: sha, + }); - while [ "$ELAPSED" -lt "$TIMEOUT" ]; do - STATUS=$(curl -s -o /dev/null -L -w "%{http_code}" "$PREVIEW_URL" || echo "000") - if [ "$STATUS" = "200" ]; then - echo "Preview is live" - echo "available=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - echo "Status: ${STATUS} (${ELAPSED}s / ${TIMEOUT}s)" - sleep "$INTERVAL" - ELAPSED=$((ELAPSED + INTERVAL)) - done + const status = statuses.find(s => s.context === CONTEXT); - echo "Preview deployment timed out after ${TIMEOUT}s" - echo "available=false" >> "$GITHUB_OUTPUT" + if (status) { + if (status.state === 'success') { + core.info(`Preview deployed: ${status.target_url}`); + core.setOutput('available', 'true'); + core.setOutput('preview-url', status.target_url); + return; + } + if (status.state === 'failure' || status.state === 'error') { + core.info(`Preview failed: ${status.description}`); + core.setOutput('available', 'false'); + core.setOutput('reason', status.description); + return; + } + core.info(`Preview building... (${Math.round((Date.now() - start) / 1000)}s)`); + } else { + core.info(`No preview status yet (${Math.round((Date.now() - start) / 1000)}s)`); + } + + await new Promise(r => setTimeout(r, INTERVAL_MS)); + } + + core.info('Preview status check timed out'); + core.setOutput('available', 'false'); + core.setOutput('reason', 'Timed out waiting for preview status'); - name: Post visual review request if: steps.wait.outputs.available == 'true' @@ -244,12 +263,14 @@ jobs: with: script: | const prNumber = context.issue.number || Number(process.env.PR_NUMBER); + const reason = '${{ steps.wait.outputs.reason }}' || 'Unknown'; const marker = ''; const body = [ marker, '## Visual Review Skipped', '', - 'The PR preview deployment did not become available within 10 minutes.', + `Reason: ${reason}`, + '', 'Visual review was skipped. The Copilot code review (Job 2) still ran.', '', 'To trigger visual review manually, re-run this workflow after the', diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml index 9fd528cf0..d7a3fa7fa 100644 --- a/.github/workflows/pr-preview.yml +++ b/.github/workflows/pr-preview.yml @@ -14,6 +14,7 @@ on: permissions: contents: write pull-requests: write + statuses: write concurrency: group: pr-preview-${{ github.event.number }} @@ -90,12 +91,33 @@ jobs: - name: Install dependencies run: yarn install --frozen-lockfile + - name: Set preview status (pending) + uses: actions/github-script@v7 + with: + script: | + await github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: context.sha, + state: 'pending', + context: 'preview/deploy', + description: 'Building preview...', + }); + - name: Detect preview pages id: detect env: PR_BODY: ${{ github.event.pull_request.body }} BASE_REF: origin/${{ github.base_ref }} - run: node .github/scripts/detect-preview-pages.js + run: | + if ! node .github/scripts/detect-preview-pages.js; then + echo "::warning::detect-preview-pages.js failed — skipping preview." + echo "pages-to-deploy=[]" >> "$GITHUB_OUTPUT" + echo "has-api-doc-changes=false" >> "$GITHUB_OUTPUT" + echo "has-layout-changes=false" >> "$GITHUB_OUTPUT" + echo "needs-author-input=false" >> "$GITHUB_OUTPUT" + echo "change-summary=Detection failed (possible merge conflicts)" >> "$GITHUB_OUTPUT" + fi - name: Post pending comment (needs input) if: steps.detect.outputs.needs-author-input == 'true' @@ -117,7 +139,7 @@ jobs: - name: Build API documentation scripts if: steps.detect.outputs.has-api-doc-changes == 'true' && steps.detect.outputs.pages-to-deploy != '[]' - run: npx tsc --project api-docs/scripts/tsconfig.json + run: yarn build:api-docs:scripts - name: Build API docs if: steps.detect.outputs.has-api-doc-changes == 'true' && steps.detect.outputs.pages-to-deploy != '[]' @@ -175,6 +197,44 @@ jobs: echo "status=ok" >> "$GITHUB_OUTPUT" + - name: Set preview status (success) + if: steps.validate-deploy.outputs.status == 'ok' + uses: actions/github-script@v7 + with: + script: | + const previewUrl = `https://influxdata.github.io/docs-v2/pr-preview/pr-${context.issue.number}/`; + await github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: context.sha, + state: 'success', + context: 'preview/deploy', + description: 'Preview deployed', + target_url: previewUrl, + }); + + - name: Set preview status (skipped/failed) + if: >- + always() && + steps.detect.outcome == 'success' && + (steps.validate-deploy.outputs.status != 'ok' || + steps.detect.outputs.pages-to-deploy == '[]') + uses: actions/github-script@v7 + with: + script: | + const description = + '${{ steps.detect.outputs.pages-to-deploy }}' === '[]' + ? 'No pages to preview' + : 'Preview deployment failed'; + await github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: context.sha, + state: 'failure', + context: 'preview/deploy', + description, + }); + - name: Post success comment if: steps.detect.outputs.pages-to-deploy != '[]' && steps.validate-deploy.outputs.status == 'ok' uses: actions/github-script@v7 diff --git a/lefthook.yml b/lefthook.yml index 7f88047e9..09579f895 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -123,6 +123,9 @@ pre-commit: glob: "assets/js/*.ts" run: yarn build:ts stage_fixed: true + build-api-docs-scripts: + glob: "api-docs/scripts/**/*.ts" + run: yarn build:api-docs:scripts prettier: tags: [frontend, style] glob: '*.{css,js,ts,jsx,tsx}' diff --git a/package.json b/package.json index 54667ff8a..18b58fff1 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "docs:audit": "node scripts/docs-cli/docs-cli.js audit", "docs:release-notes": "node scripts/docs-cli/docs-cli.js release-notes", "build:api-docs": "cd api-docs && sh generate-api-docs.sh", + "build:api-docs:scripts": "tsc --project api-docs/scripts/tsconfig.json", "build:pytest:image": "docker build -t influxdata/docs-pytest:latest -f Dockerfile.pytest .", "build:agent:instructions": "node ./helper-scripts/build-agent-instructions.js", "build:ts": "tsc --project tsconfig.json --outDir dist",