diff --git a/.github/workflows/preview-docs.yml b/.github/workflows/preview-docs.yml index e326885d7..f85699654 100644 --- a/.github/workflows/preview-docs.yml +++ b/.github/workflows/preview-docs.yml @@ -10,26 +10,21 @@ jobs: run: runs-on: ubuntu-latest permissions: - pull-requests: write # Only for commenting - contents: read # For checking out code + pull-requests: write + contents: read steps: - name: Checkout repository uses: actions/checkout@v4 with: - fetch-depth: 0 # Fetch full history for git diff + fetch-depth: 0 - name: Checkout PR run: | git fetch origin pull/${{ github.event.pull_request.number }}/head:pr-${{ github.event.pull_request.number }} git checkout pr-${{ github.event.pull_request.number }} - - name: Install Fern and CML - run: | - npm install -g fern-api@latest - npm install -g @dvcorg/cml - - - name: Install Chrome for Puppeteer - run: npx puppeteer browsers install chrome@141.0.7390.54 + - name: Install Fern + run: npm install -g fern-api@latest - name: Generate preview URL id: generate-docs @@ -42,8 +37,8 @@ jobs: echo "preview_url=$URL" >> $GITHUB_OUTPUT echo "Preview URL: $URL" - - name: Run fern docs diff for changed MDX files - id: docs-diff + - name: Get changed MDX files and slugs + id: changed-files env: FERN_TOKEN: ${{ secrets.FERN_TOKEN }} run: | @@ -51,100 +46,76 @@ jobs: CHANGED_FILES=$(git diff --name-only origin/main...HEAD -- '*.mdx' 2>/dev/null || echo "") if [ -z "$CHANGED_FILES" ] || [ -z "$PREVIEW_URL" ]; then - echo "has_diffs=false" >> $GITHUB_OUTPUT + echo "has_changes=false" >> $GITHUB_OUTPUT exit 0 fi - # Convert newlines to space-separated list for the command - FILES_LIST=$(echo "$CHANGED_FILES" | tr '\n' ' ') - - # Create output directory - mkdir -p .fern/diff + echo "has_changes=true" >> $GITHUB_OUTPUT - # Run fern docs diff and capture output - # Progress messages go to stderr, JSON goes to stdout - # We capture everything to a temp file, then extract just the JSON - npx fern-api@latest docs diff "$PREVIEW_URL" $FILES_LIST --output .fern/diff > .fern/diff/output.txt 2>&1 || true - - # Extract just the JSON part (starts with { and ends with }) - # The JSON is the last thing output, so we find the line starting with { and take everything from there - sed -n '/^{/,$p' .fern/diff/output.txt > .fern/diff/diff.json - - # Debug: show what we captured - echo "=== Raw output ===" - cat .fern/diff/output.txt - echo "=== Extracted JSON ===" - cat .fern/diff/diff.json + # Call the API to get slugs for the changed files + FILES_PARAM=$(echo "$CHANGED_FILES" | tr '\n' ',' | sed 's/,$//') + RESPONSE=$(curl -sf -H "FERN_TOKEN: $FERN_TOKEN" "${PREVIEW_URL}/api/fern-docs/get-slug-for-file?files=${FILES_PARAM}" 2>/dev/null) || { + echo "api_response=" >> $GITHUB_OUTPUT + exit 0 + } - # Check if diff JSON file exists and has valid content - if [ -f ".fern/diff/diff.json" ] && grep -q '"diffs"' .fern/diff/diff.json 2>/dev/null; then - echo "has_diffs=true" >> $GITHUB_OUTPUT - else - echo "has_diffs=false" >> $GITHUB_OUTPUT - echo "No diffs found or diff.json is invalid" - fi + # Store the API response for the next step + echo "api_response<> $GITHUB_OUTPUT + echo "$RESPONSE" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT - - name: Upload diff images as artifacts - if: steps.docs-diff.outputs.has_diffs == 'true' - uses: actions/upload-artifact@v4 + - name: Post PR comment + uses: actions/github-script@v7 with: - name: docs-diff-images - path: .fern/diff/*.png - retention-days: 7 - - - name: Upload images and create comment - if: steps.docs-diff.outputs.has_diffs == 'true' - env: - REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PREVIEW_URL: ${{ steps.generate-docs.outputs.preview_url }} - run: | - # Parse diff.json and upload images using cml - PREVIEW_URL="${{ steps.generate-docs.outputs.preview_url }}" - BASE_URL=$(echo "$PREVIEW_URL" | grep -oP 'https?://[^/]+') - - echo ":herb: **Preview your docs:** <${PREVIEW_URL}>" > comment.md - echo "" >> comment.md - echo "### Visual changes detected:" >> comment.md - echo "" >> comment.md - - # Process each diff entry - jq -c '.diffs[]' .fern/diff/diff.json | while read -r diff; do - FILE=$(echo "$diff" | jq -r '.file') - SLUG=$(echo "$diff" | jq -r '.slug') - COMPARISON=$(echo "$diff" | jq -r '.comparison') - CHANGE_PERCENT=$(echo "$diff" | jq -r '.changePercent // "N/A"') - IS_NEW_PAGE=$(echo "$diff" | jq -r '.isNewPage') + script: | + const previewUrl = '${{ steps.generate-docs.outputs.preview_url }}'; + const hasChanges = '${{ steps.changed-files.outputs.has_changes }}' === 'true'; + const apiResponseRaw = `${{ steps.changed-files.outputs.api_response }}`; - echo "#### \`${FILE}\`" >> comment.md - echo "" >> comment.md - echo "**Page:** [${SLUG}](${BASE_URL}/${SLUG}) | **Change:** ${CHANGE_PERCENT}%" >> comment.md - if [ "$IS_NEW_PAGE" = "true" ]; then - echo " | *New page*" >> comment.md - fi - echo "" >> comment.md + let body = `:herb: **Preview your docs:** <${previewUrl}>\n\n`; - # Upload image using cml and get URL - if [ -f "$COMPARISON" ]; then - IMAGE_URL=$(cml publish "$COMPARISON" 2>/dev/null || echo "") - if [ -n "$IMAGE_URL" ]; then - echo "![${FILE} comparison](${IMAGE_URL})" >> comment.md - else - echo "*Comparison image available in [workflow artifacts](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})*" >> comment.md - fi - fi - echo "" >> comment.md - done - - cat comment.md - - - name: Create comment without diffs - if: steps.docs-diff.outputs.has_diffs != 'true' - run: | - echo ":herb: **Preview your docs:** <${{ steps.generate-docs.outputs.preview_url }}>" > comment.md - - - name: Post PR comment - uses: thollander/actions-comment-pull-request@v2.4.3 - with: - filePath: comment.md - comment_tag: preview-docs - mode: upsert + if (hasChanges && apiResponseRaw.trim()) { + try { + const response = JSON.parse(apiResponseRaw); + const mappings = response.mappings || []; + const validMappings = mappings.filter(m => m.slug != null); + + if (validMappings.length > 0) { + body += `### Changed pages:\n\n`; + for (const mapping of validMappings) { + const pageUrl = `${previewUrl}/${mapping.slug}`; + body += `- \`${mapping.file}\` → [${mapping.slug}](${pageUrl})\n`; + } + } + } catch (e) { + console.log('Failed to parse API response:', e); + } + } + + // Find existing comment to update + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const botComment = comments.find(comment => + comment.user.type === 'Bot' && + comment.body.includes(':herb: **Preview your docs:**') + ); + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: body + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: body + }); + }