From e776c17e455d53fed6fb7fd1cab2e69c202c2a6e Mon Sep 17 00:00:00 2001 From: Cal Courtney Date: Mon, 26 Jan 2026 10:32:47 +0000 Subject: [PATCH 1/6] dev: bump node version to 24 --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index 209e3ef4..3fe3b157 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20 +24.13.0 From 4bd7fec11034cc080ec345187da160b9323d1a15 Mon Sep 17 00:00:00 2001 From: Cal Courtney Date: Mon, 26 Jan 2026 10:33:04 +0000 Subject: [PATCH 2/6] feat: add docsearch on deploy plugin --- .github/workflows/algolia.yml | 5 +- netlify.toml | 2 + plugins/trigger-algolia-scraper/index.js | 49 ++++++++++++++++++++ plugins/trigger-algolia-scraper/manifest.yml | 1 + 4 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 netlify.toml create mode 100644 plugins/trigger-algolia-scraper/index.js create mode 100644 plugins/trigger-algolia-scraper/manifest.yml diff --git a/.github/workflows/algolia.yml b/.github/workflows/algolia.yml index ec3112d6..2e5685ee 100644 --- a/.github/workflows/algolia.yml +++ b/.github/workflows/algolia.yml @@ -1,9 +1,6 @@ name: Algolia on: workflow_dispatch: - push: - branches: - - master jobs: update_algolia_job: @@ -14,7 +11,7 @@ jobs: - name: Get Algolia config id: algolia_config - run: echo "::set-output name=config::$(cat algolia_config.json | jq -r tostring)" + run: echo "config=$(cat algolia_config.json | jq -r tostring)" >> "$GITHUB_OUTPUT" - name: Push indices to Algolia uses: signcl/docsearch-scraper-action@master diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 00000000..922147bf --- /dev/null +++ b/netlify.toml @@ -0,0 +1,2 @@ +[[plugins]] + package = "./plugins/trigger-algolia-scraper" diff --git a/plugins/trigger-algolia-scraper/index.js b/plugins/trigger-algolia-scraper/index.js new file mode 100644 index 00000000..cbe03312 --- /dev/null +++ b/plugins/trigger-algolia-scraper/index.js @@ -0,0 +1,49 @@ +export const onSuccess = async function ({ utils }) { + if (process.env.CONTEXT !== "production") { + console.log("Skipping Algolia scraper trigger — not a production deploy."); + return; + } + + const token = process.env.GITHUB_PAT; + if (!token) { + utils.status.show({ + title: "Algolia scraper trigger skipped", + summary: "GITHUB_PAT environment variable is not set.", + }); + return; + } + + console.log("Triggering Algolia scraper workflow..."); + + const response = await fetch( + "https://api.github.com/repos/absmartly/docs/actions/workflows/algolia.yml/dispatches", + { + method: "POST", + headers: { + Accept: "application/vnd.github+json", + Authorization: `Bearer ${token}`, + "X-GitHub-Api-Version": "2022-11-28", + }, + body: JSON.stringify({ ref: "master" }), + }, + ); + + if (response.ok || response.status === 204) { + console.log("Algolia scraper workflow dispatched successfully."); + utils.status.show({ + title: "Algolia scraper triggered", + summary: "The Algolia DocSearch scraper workflow has been dispatched.", + }); + return; + } + + const body = await response.text(); + console.error( + `Failed to trigger Algolia scraper: ${response.status} ${response.statusText}`, + body, + ); + utils.status.show({ + title: "Algolia scraper trigger failed", + summary: `GitHub API responded with ${response.status}: ${body}`, + }); +}; diff --git a/plugins/trigger-algolia-scraper/manifest.yml b/plugins/trigger-algolia-scraper/manifest.yml new file mode 100644 index 00000000..cb9d19dd --- /dev/null +++ b/plugins/trigger-algolia-scraper/manifest.yml @@ -0,0 +1 @@ +name: trigger-algolia-scraper From 1e80a9a3167ce4d49e58f450e68077e39be2dd51 Mon Sep 17 00:00:00 2001 From: Cal Courtney Date: Mon, 26 Jan 2026 10:39:56 +0000 Subject: [PATCH 3/6] ci: bump node to version in .nvmrc --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 780a7fa7..c5e1c3d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: - name: Set Node.js 20.x uses: actions/setup-node@v7 with: - node-version: 20.x + node-version-file: .nvmrc - name: Install Deps run: yarn install --frozen-lockfile From 85707742cf186cebc2c3f765c0fae819f90cae1d Mon Sep 17 00:00:00 2001 From: Cal Courtney Date: Tue, 14 Jul 2026 14:05:09 +0100 Subject: [PATCH 4/6] fix: harden algolia scraper netlify plugin --- plugins/trigger-algolia-scraper/index.js | 41 +++++++++++++------- plugins/trigger-algolia-scraper/package.json | 3 ++ 2 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 plugins/trigger-algolia-scraper/package.json diff --git a/plugins/trigger-algolia-scraper/index.js b/plugins/trigger-algolia-scraper/index.js index cbe03312..06322105 100644 --- a/plugins/trigger-algolia-scraper/index.js +++ b/plugins/trigger-algolia-scraper/index.js @@ -1,4 +1,4 @@ -export const onSuccess = async function ({ utils }) { +export const onSuccess = async ({ utils }) => { if (process.env.CONTEXT !== "production") { console.log("Skipping Algolia scraper trigger — not a production deploy."); return; @@ -15,20 +15,31 @@ export const onSuccess = async function ({ utils }) { console.log("Triggering Algolia scraper workflow..."); - const response = await fetch( - "https://api.github.com/repos/absmartly/docs/actions/workflows/algolia.yml/dispatches", - { - method: "POST", - headers: { - Accept: "application/vnd.github+json", - Authorization: `Bearer ${token}`, - "X-GitHub-Api-Version": "2022-11-28", + let response; + try { + response = await fetch( + "https://api.github.com/repos/absmartly/docs/actions/workflows/algolia.yml/dispatches", + { + method: "POST", + headers: { + Accept: "application/vnd.github+json", + Authorization: `Bearer ${token}`, + "X-GitHub-Api-Version": "2022-11-28", + }, + body: JSON.stringify({ ref: "master" }), + signal: AbortSignal.timeout(10_000), }, - body: JSON.stringify({ ref: "master" }), - }, - ); + ); + } catch (error) { + console.error("Failed to reach the GitHub API:", error.message); + utils.status.show({ + title: "Algolia scraper trigger failed", + summary: `Could not reach the GitHub API: ${error.message}`, + }); + return; + } - if (response.ok || response.status === 204) { + if (response.ok) { console.log("Algolia scraper workflow dispatched successfully."); utils.status.show({ title: "Algolia scraper triggered", @@ -37,13 +48,13 @@ export const onSuccess = async function ({ utils }) { return; } - const body = await response.text(); + const body = (await response.text()).slice(0, 200).replace(/\s+/g, " "); console.error( `Failed to trigger Algolia scraper: ${response.status} ${response.statusText}`, body, ); utils.status.show({ title: "Algolia scraper trigger failed", - summary: `GitHub API responded with ${response.status}: ${body}`, + summary: `GitHub API responded with ${response.status} ${response.statusText}.`, }); }; diff --git a/plugins/trigger-algolia-scraper/package.json b/plugins/trigger-algolia-scraper/package.json new file mode 100644 index 00000000..3dbc1ca5 --- /dev/null +++ b/plugins/trigger-algolia-scraper/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} From 907f2e14d2500babcfd71ee5bdbb469daa5af77f Mon Sep 17 00:00:00 2001 From: Cal Courtney Date: Tue, 14 Jul 2026 14:08:42 +0100 Subject: [PATCH 5/6] fix: drop empty HTTP/2 statusText from scraper failure summary --- .github/workflows/build.yml | 2 +- plugins/trigger-algolia-scraper/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c5e1c3d6..303a937b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v7 - - name: Set Node.js 20.x + - name: Set Node.js uses: actions/setup-node@v7 with: node-version-file: .nvmrc diff --git a/plugins/trigger-algolia-scraper/index.js b/plugins/trigger-algolia-scraper/index.js index 06322105..c87a67c4 100644 --- a/plugins/trigger-algolia-scraper/index.js +++ b/plugins/trigger-algolia-scraper/index.js @@ -55,6 +55,6 @@ export const onSuccess = async ({ utils }) => { ); utils.status.show({ title: "Algolia scraper trigger failed", - summary: `GitHub API responded with ${response.status} ${response.statusText}.`, + summary: `GitHub API responded with status ${response.status}. See build log for details.`, }); }; From e4a835359a6df519b5914170b9c61e7a813af7d8 Mon Sep 17 00:00:00 2001 From: Cal Courtney Date: Tue, 14 Jul 2026 15:36:06 +0100 Subject: [PATCH 6/6] fix: retry scraper dispatch and fail the plugin on failure --- plugins/trigger-algolia-scraper/index.js | 104 ++++++++++++++--------- 1 file changed, 62 insertions(+), 42 deletions(-) diff --git a/plugins/trigger-algolia-scraper/index.js b/plugins/trigger-algolia-scraper/index.js index c87a67c4..dbddfa28 100644 --- a/plugins/trigger-algolia-scraper/index.js +++ b/plugins/trigger-algolia-scraper/index.js @@ -1,3 +1,8 @@ +const MAX_ATTEMPTS = 3; +const RETRY_DELAY_MS = 2_000; + +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + export const onSuccess = async ({ utils }) => { if (process.env.CONTEXT !== "production") { console.log("Skipping Algolia scraper trigger — not a production deploy."); @@ -6,55 +11,70 @@ export const onSuccess = async ({ utils }) => { const token = process.env.GITHUB_PAT; if (!token) { - utils.status.show({ - title: "Algolia scraper trigger skipped", - summary: "GITHUB_PAT environment variable is not set.", - }); + utils.build.failPlugin("GITHUB_PAT environment variable is not set."); return; } console.log("Triggering Algolia scraper workflow..."); - let response; - try { - response = await fetch( - "https://api.github.com/repos/absmartly/docs/actions/workflows/algolia.yml/dispatches", - { - method: "POST", - headers: { - Accept: "application/vnd.github+json", - Authorization: `Bearer ${token}`, - "X-GitHub-Api-Version": "2022-11-28", + for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) { + let response; + try { + response = await fetch( + "https://api.github.com/repos/absmartly/docs/actions/workflows/algolia.yml/dispatches", + { + method: "POST", + headers: { + Accept: "application/vnd.github+json", + Authorization: `Bearer ${token}`, + "X-GitHub-Api-Version": "2022-11-28", + }, + body: JSON.stringify({ ref: "master" }), + signal: AbortSignal.timeout(10_000), }, - body: JSON.stringify({ ref: "master" }), - signal: AbortSignal.timeout(10_000), - }, - ); - } catch (error) { - console.error("Failed to reach the GitHub API:", error.message); - utils.status.show({ - title: "Algolia scraper trigger failed", - summary: `Could not reach the GitHub API: ${error.message}`, - }); - return; - } + ); + } catch (error) { + if (attempt < MAX_ATTEMPTS) { + console.warn( + `GitHub API unreachable (attempt ${attempt}/${MAX_ATTEMPTS}): ${error.message}. Retrying...`, + ); + await sleep(RETRY_DELAY_MS); + continue; + } + utils.build.failPlugin( + `Could not reach the GitHub API after ${MAX_ATTEMPTS} attempts.`, + { error }, + ); + return; + } - if (response.ok) { - console.log("Algolia scraper workflow dispatched successfully."); - utils.status.show({ - title: "Algolia scraper triggered", - summary: "The Algolia DocSearch scraper workflow has been dispatched.", - }); + if (response.ok) { + console.log("Algolia scraper workflow dispatched successfully."); + utils.status.show({ + title: "Algolia scraper triggered", + summary: "The Algolia DocSearch scraper workflow has been dispatched.", + }); + return; + } + + const body = (await response.text()).slice(0, 200).replace(/\s+/g, " "); + const transient = response.status === 429 || response.status >= 500; + if (transient && attempt < MAX_ATTEMPTS) { + console.warn( + `GitHub API returned ${response.status} (attempt ${attempt}/${MAX_ATTEMPTS}). Retrying...`, + body, + ); + await sleep(RETRY_DELAY_MS); + continue; + } + + console.error( + `Failed to trigger Algolia scraper: ${response.status} ${response.statusText}`, + body, + ); + utils.build.failPlugin( + `GitHub API responded with status ${response.status}. See build log for details.`, + ); return; } - - const body = (await response.text()).slice(0, 200).replace(/\s+/g, " "); - console.error( - `Failed to trigger Algolia scraper: ${response.status} ${response.statusText}`, - body, - ); - utils.status.show({ - title: "Algolia scraper trigger failed", - summary: `GitHub API responded with status ${response.status}. See build log for details.`, - }); };