Replace Algolia search with built-in search #3599
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| on: | |
| pull_request: | |
| branches: [main, prerelease] | |
| paths-ignore: | |
| - '.github/**' | |
| issue_comment: | |
| types: [created] | |
| name: Deploy Preview | |
| concurrency: | |
| # Use github.event.pull_request.number on pull requests, so it's unique per pull request | |
| # Use github.event.issue.number on issue comments, so it's unique per comment | |
| # Use github.ref on other branches, so it's unique per branch | |
| group: ${{ github.workflow }}-${{ (github.event.pull_request && format('PR-{0}', github.event.pull_request.number)) || ( github.event.issue && format('comment-{0}', github.event.issue.number) ) || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| is-external-pr: | |
| # Be helpful with reviewer and remind them to trigger a deploy preview if the PR is from a fork. | |
| if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Error with message for manual deploy | |
| run: | | |
| echo "::error title=Manual action required for preview::PR from fork can't be deployed as preview to Netlify automatically. Use '/deploy-preview' command in comments to trigger the preview manually." | |
| shell: bash | |
| role-of-commenter: | |
| if: github.event.issue.pull_request | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check if commenter is a member, owner or collaborator | |
| id: commenter-check | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| commenter_role=$(gh api repos/$GITHUB_REPOSITORY/collaborators/${{ github.event.comment.user.login }}/permission --jq '.permission') | |
| echo "commenter_role=$commenter_role" >> "$GITHUB_OUTPUT" | |
| echo "author_association=${{ github.event.comment.author_association }}" >> "$GITHUB_OUTPUT" | |
| shell: bash | |
| build-deploy-preview: | |
| # Deploy a preview only if | |
| # - the PR is not from a fork, | |
| # - requested by PR comment /deploy-preview, from a repo user or github action bot (user id 41898282) | |
| # FIXME: We need to change the way we filter because somehow some MEMBER in API are seen as CONTRIBUTOR in CI | |
| if: > | |
| ( | |
| github.event_name == 'pull_request' && | |
| github.event.pull_request.head.repo.fork != true | |
| ) || | |
| ( | |
| github.event.issue.pull_request && | |
| ( | |
| github.event.comment.user.id == '41898282' || | |
| github.event.comment.user.login == 'gordonwoodhull' || | |
| github.event.comment.author_association == 'MEMBER' || | |
| github.event.comment.author_association == 'OWNER' || | |
| github.event.comment.author_association == 'COLLABORATOR' | |
| ) && | |
| startsWith(github.event.comment.body, '/deploy-preview') | |
| ) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: refs/pull/${{ github.event.pull_request.number || github.event.issue.number }}/merge | |
| # On issue_comment events (e.g. /deploy-preview), github.event.pull_request | |
| # is not populated so tj-actions/changed-files can't determine the base commit | |
| # to diff against. Fetch it from the API so we can pass it explicitly. | |
| - name: Get PR base SHA | |
| if: github.event_name != 'pull_request' | |
| id: pr-info | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| pr_number=${{ github.event.issue.number }} | |
| base_sha=$(gh -R $GITHUB_REPOSITORY pr view $pr_number --json baseRefOid --jq '.baseRefOid') | |
| echo "base_sha=$base_sha" >> "$GITHUB_OUTPUT" | |
| shell: bash | |
| - name: Get latest pre-release from github | |
| id: github-release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| echo version=$(gh api repos/quarto-dev/quarto-cli/releases | jq -r 'map(select(.prerelease)) | first | .tag_name | sub("^v";"")') >> "$GITHUB_OUTPUT" | |
| - name: Set up Quarto | |
| uses: quarto-dev/quarto-actions/setup@v2 | |
| with: | |
| version: ${{ steps.github-release.outputs.version }} | |
| - name: Is it for prerelease website ? | |
| id: prerelease-docs-check | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| if [ "${{ github.event.pull_request.base.ref }}" == "prerelease" ]; then | |
| echo "is_prerelease_docs=true" >> "$GITHUB_OUTPUT" | |
| elif [ -n "${{ github.event.issue.pull_request.url }}" ]; then | |
| # This will trigger when not pull request event, so a PR comment event. | |
| # we need to get the base info from PR number the comment is made in | |
| base_ref=$(gh -R $GITHUB_REPOSITORY pr view ${{ github.event.issue.number }} --json baseRefName --jq '.baseRefName') | |
| if [ "$base_ref" == "prerelease" ]; then | |
| echo "is_prerelease_docs=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "is_prerelease_docs=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| else | |
| echo "is_prerelease_docs=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| shell: bash | |
| - name: Render | |
| uses: quarto-dev/quarto-actions/render@v2 | |
| env: | |
| QUARTO_PROFILE: ${{ steps.prerelease-docs-check.outputs.is_prerelease_docs == 'true' && 'prerelease-docs,pr-preview' || 'pr-preview' }} | |
| - name: Deploy Preview to Netlify as preview | |
| id: netlify-deploy | |
| uses: nwtgck/actions-netlify@v3 | |
| env: | |
| NETLIFY_SITE_ID: 2a3da659-672b-4e5b-8785-e10ebf79a962 | |
| NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} | |
| with: | |
| publish-dir: './_site' | |
| production-deploy: false | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| deploy-message: | | |
| Deploy from GHA: ${{ github.event.pull_request.title || format('manual from PR {0}', github.event.issue.number) }} | |
| alias: deploy-preview-${{ github.event.pull_request.number || github.event.issue.number }} | |
| # these all default to 'true' | |
| enable-pull-request-comment: false | |
| enable-commit-comment: false | |
| enable-commit-status: true | |
| overwrites-pull-request-comment: false | |
| timeout-minutes: 1 | |
| - name: Get changed files | |
| id: changed-files | |
| uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 | |
| with: | |
| base_sha: ${{ github.event.pull_request.base.sha || steps.pr-info.outputs.base_sha }} | |
| files: | | |
| # don't consider modifications on file used for includes for now. | |
| license.qmd | |
| docs/**/[^_]*.qmd | |
| docs/**/[^_]*.md | |
| docs/extensions/listings/*.yml | |
| docs/reference/**/*.json | |
| docs/cli/*.json | |
| docs/**/images/*.png | |
| files_ignore: | | |
| **/CLAUDE.md | |
| json: true | |
| escape_json: false | |
| - name: Map changed images to doc pages | |
| id: image-pages | |
| run: | | |
| # Build JSON mapping from image output paths to doc pages using manifest | |
| MANIFEST="_tools/screenshots/manifest.json" | |
| CHANGED='${{ steps.changed-files.outputs.all_changed_files }}' | |
| if [ -f "$MANIFEST" ]; then | |
| # Extract {output: doc.file} pairs, match against changed files | |
| IMAGE_PAGES=$(echo "$CHANGED" | jq -r --slurpfile manifest "$MANIFEST" ' | |
| ($manifest[0].screenshots | map(select(.doc.file) | {(.output): .doc.file}) | add // {}) as $map | | |
| [.[] | select(test("[.]png$")) | | |
| # Normalize dark variants (foo-dark.png -> foo.png) for manifest lookup | |
| sub("-dark[.]png$"; ".png") as $base | | |
| ($map[$base] // empty) | sub("[.]qmd$"; ".html") | |
| ] | unique | .[] | |
| ') | |
| echo "pages<<EOF" >> "$GITHUB_OUTPUT" | |
| echo "$IMAGE_PAGES" >> "$GITHUB_OUTPUT" | |
| echo "EOF" >> "$GITHUB_OUTPUT" | |
| fi | |
| shell: bash | |
| - name: Detect draft pages | |
| id: detect-drafts | |
| uses: ./.github/workflows/actions/detect-drafts | |
| with: | |
| changed-files: ${{ steps.changed-files.outputs.all_changed_files }} | |
| - name: Create custom PR comment | |
| if: github.event.pull_request || github.event.issue.pull_request | |
| uses: actions/github-script@v8 | |
| env: | |
| DEPLOY_URL: ${{ steps.netlify-deploy.outputs.deploy-url }} | |
| CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} | |
| IMAGE_PAGES: ${{ steps.image-pages.outputs.pages }} | |
| DRAFT_FILES: ${{ steps.detect-drafts.outputs.files }} | |
| HAS_DRAFTS: ${{ steps.detect-drafts.outputs.found }} | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const prNumber = context.payload.pull_request?.number || context.payload.issue.number; | |
| const deployUrl = process.env.DEPLOY_URL; | |
| const changedFiles = JSON.parse(process.env.CHANGED_FILES || '[]'); | |
| const draftFilesList = (process.env.DRAFT_FILES || '').trim().split('\n').filter(Boolean); | |
| const draftSet = new Set(draftFilesList); | |
| let commentBody = `## 📝 Preview Deployment\n\n`; | |
| commentBody += `🔍 Full site preview: [${deployUrl}](${deployUrl})\n\n`; | |
| if (changedFiles.length > 0) { | |
| // Explicit mapping for files that don't follow standard naming conventions | |
| const specialFileMapping = { | |
| 'docs/extensions/listings/shortcodes-and-filters.yml': 'docs/extensions/index.html', | |
| 'docs/extensions/listings/journal-articles.yml': 'docs/extensions/index.html', | |
| 'docs/extensions/listings/custom-formats.yml': 'docs/extensions/index.html', | |
| 'docs/extensions/listings/revealjs-formats.yml': 'docs/extensions/index.html', | |
| 'docs/extensions/listings/revealjs.yml': 'docs/extensions/index.html', | |
| 'docs/cli/cli-info.json': 'docs/reference/index.html', | |
| }; | |
| // Projects/ JSON files have many-to-one mapping to .qmd pages | |
| const projectsJsonMapping = { | |
| 'book': 'docs/reference/projects/books.html', | |
| 'manuscript': 'docs/reference/projects/manuscripts.html', | |
| 'project': 'docs/reference/projects/options.html', | |
| 'preview': 'docs/reference/projects/options.html', | |
| 'serve': 'docs/reference/projects/options.html', | |
| }; | |
| const projectsDefault = 'docs/reference/projects/websites.html'; | |
| // Resolve each changed file to its preview URL path | |
| // Group files by their target page for deduplication | |
| const pageToFiles = new Map(); | |
| changedFiles.forEach(file => { | |
| let fileUrlPath; | |
| if (specialFileMapping[file]) { | |
| fileUrlPath = specialFileMapping[file]; | |
| } else if (file.endsWith('.qmd') || file.endsWith('.md')) { | |
| fileUrlPath = file.replace(/\.(qmd|md)$/, '.html'); | |
| } else if (file.startsWith('docs/reference/projects/') && file.endsWith('.json')) { | |
| const stem = file.split('/').pop().replace('.json', ''); | |
| fileUrlPath = projectsJsonMapping[stem] || projectsDefault; | |
| } else if (file.endsWith('.json')) { | |
| // Generic: formats/, cells/, metadata/ JSON have sibling .qmd files | |
| fileUrlPath = file.replace(/\.json$/, '.html'); | |
| } | |
| if (fileUrlPath) { | |
| if (!pageToFiles.has(fileUrlPath)) { | |
| pageToFiles.set(fileUrlPath, []); | |
| } | |
| pageToFiles.get(fileUrlPath).push(file); | |
| } | |
| }); | |
| if (pageToFiles.size > 0) { | |
| commentBody += `### 🔄 Modified Documents\n\n`; | |
| for (const [page, files] of pageToFiles) { | |
| const fileUrl = `${deployUrl}/${page}`; | |
| const isDraft = files.some(f => draftSet.has(f)); | |
| const draftTag = isDraft ? ' — ⚠️ `draft`' : ''; | |
| if (files.length === 1) { | |
| commentBody += `- [${page}](${fileUrl})${draftTag}\n`; | |
| } else { | |
| // Multiple source files map to one page - show page with file summary | |
| const basenames = files.map(f => f.split('/').pop()); | |
| const shown = basenames.slice(0, 3).join(', '); | |
| const rest = basenames.length > 3 ? `, +${basenames.length - 3} more` : ''; | |
| commentBody += `- [${page}](${fileUrl}) (${shown}${rest})${draftTag}\n`; | |
| } | |
| } | |
| } | |
| } | |
| // Add pages affected by changed screenshots | |
| const imagePages = (process.env.IMAGE_PAGES || '').trim(); | |
| if (imagePages) { | |
| const pages = [...new Set(imagePages.split('\n').filter(Boolean))]; | |
| if (pages.length > 0) { | |
| commentBody += `\n### 🖼️ Pages with Updated Screenshots\n\n`; | |
| for (const page of pages) { | |
| const fileUrl = `${deployUrl}/${page}`; | |
| commentBody += `- [${page}](${fileUrl})\n`; | |
| } | |
| } | |
| } | |
| // Add draft warning callout (no file list — already tagged inline above) | |
| const hasDrafts = process.env.HAS_DRAFTS === 'true'; | |
| if (hasDrafts) { | |
| commentBody += `\n> [!WARNING]\n`; | |
| commentBody += `> This PR contains pages with \`draft: true\`. Remove the draft status before merging if the content is ready to publish.\n`; | |
| } | |
| await github.rest.issues.createComment({ | |
| issue_number: prNumber, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: commentBody | |
| }); | |