Skip to content
5 changes: 5 additions & 0 deletions .github/actions/setup-deps/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ inputs:
node-version-file:
description: File to read Node.js version from
default: .nvmrc
node-version:
description: Explicit Node.js version; takes precedence over node-version-file when set
required: false
default: ""
pnpm-version:
description: pnpm version to use if pnpm is detected
default: "9.15.2"
Expand Down Expand Up @@ -47,6 +51,7 @@ runs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
node-version-file: ${{ inputs.node-version-file }}
cache: ${{ steps.detect.outputs.manager }}

Expand Down
46 changes: 44 additions & 2 deletions .github/workflows/search-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ on:
description: 'Base URL to test (required)'
required: true
type: string
NEXT_PUBLIC_BASE_PATH:
description: 'Base path for the site (e.g. /tinadocs)'
required: false
default: ''
type: string
workflow_call:
inputs:
base_url:
Expand All @@ -23,6 +28,7 @@ jobs:
test-search:
name: Test Search Functionality
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Checkout code
Expand All @@ -31,9 +37,45 @@ jobs:
- name: Setup dependencies
id: setup
uses: ./.github/actions/setup-deps
with:
# .nvmrc is `lts/*`, which now resolves to Node 24 β€” and Playwright
# 1.54's browser-archive extraction hangs deterministically on Node 24.
# Pin this job to Node 22 (previous LTS, known-good) so the install can
# extract chromium. Scoped to this workflow only.
node-version: "22"

- name: Install Playwright browsers
run: npx playwright install chromium --with-deps
# System deps (apt) and the browser download are split because they fail
# for different reasons. apt previously hung on an interactive frontend, so
# force debconf non-interactive in the db before the sudo apt-get Playwright
# runs β€” this now completes in seconds.
- name: Install Playwright system dependencies
timeout-minutes: 6
run: |
echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections
npx playwright install-deps chromium

# The browser archive downloads fully (hits 100%) and then Playwright stalls
# indefinitely with no output. Cap each attempt with `timeout` so a stall is
# killed (a hang is not a non-zero exit) and retry; per-attempt cap Γ— attempts
# stays under the step timeout. DEBUG=pw:install surfaces exactly what
# Playwright is doing if it stalls again.
- name: Install Playwright browser
timeout-minutes: 10
env:
DEBUG: pw:install
run: |
for attempt in 1 2 3; do
echo "::group::playwright browser install (attempt $attempt)"
if timeout 150 npx playwright install chromium; then
echo "::endgroup::"
exit 0
fi
echo "::endgroup::"
echo "Attempt $attempt stalled or failed; retrying in 10s..."
sleep 10
done
echo "Playwright browser install failed after 3 attempts."
exit 1

- name: Run search tests
run: ${{ steps.setup.outputs.runner }} test
Expand Down
24 changes: 17 additions & 7 deletions src/components/search-docs/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,25 @@ export function Search({ className }: { className?: string }) {
if (typeof window !== "undefined") {
let pagefindModule: any;
try {
// Using eval to import pagefind.js is a workaround since the script isn't available during the build process.
// This also improves performance by loading the script only when needed, reducing initial page load time.
// A direct import would require committing the file with the codebase, which would change frequently
// with every content update.

pagefindModule = await (window as any).eval(
`import("${pagefindPath}/pagefind.js")`
// pagefind.js is generated at build time and lives outside the bundle. The
// webpackIgnore/turbopackIgnore comments tell both bundlers to leave this dynamic
// import alone (the reason the old code resorted to `window.eval`), emitting it as a
// runtime import. Avoiding `eval` means no CSP 'unsafe-eval' is required, which is
// what was breaking search under Chromium's stricter enforcement.
const pagefindUrl = new URL(
`${pagefindPath}/pagefind.js`,
window.location.origin
).href;

pagefindModule = await import(
/* webpackIgnore: true */
/* turbopackIgnore: true */
pagefindUrl
);
} catch (importError) {
// Surface the real cause (CSP, MIME type, or 404) instead of swallowing it.
// biome-ignore lint/suspicious/noConsole: needed to diagnose load failures in prod
console.error("Pagefind failed to load:", importError);
setError(
"Unable to load search functionality. For more information, please check this README: https://github.com/tinacms/tina-docs?tab=readme-ov-file#search-functionality and refresh the page."
);
Expand Down
Loading