Skip to content

Commit d046409

Browse files
authored
feat: Update Algolia index on master merge (#10032)
* feat: Update Algolia index upon master merge * highlight the importance of the `div#main` on docPage * use env vars on Algolia search client * add public Algolia env vars to GH action * delete changelog/feed before build
1 parent dab4f51 commit d046409

File tree

7 files changed

+152
-14
lines changed

7 files changed

+152
-14
lines changed

.env.example

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
# rename this file to .env and supply the values listed below
22
# also make sure they are available to the build tool (e.g. Netlify)
3-
# warning: variables prefixed with GATSBY_ will be made available to client-side code
3+
# warning: variables prefixed with NEXT_PUBLIC_ will be made available to client-side code
44
# be careful not to expose sensitive data (in this case your Algolia admin key)
55

6-
GATSBY_ALGOLIA_APP_ID=OOK48W9UCL
7-
GATSBY_ALGOLIA_SEARCH_KEY=ca98597f559459c216891b75989832f8
8-
GATSBY_ALGOLIA_INDEX_PREFIX=test-sentry-
9-
# ALGOLIA_ADMIN_KEY=
10-
# ALGOLIA_INDEX=0
11-
# SENTRY_AUTH_TOKEN=
6+
NEXT_PUBLIC_ALGOLIA_APP_ID=OOK48W9UCL
7+
NEXT_PUBLIC_ALGOLIA_SEARCH_KEY=ca98597f559459c216891b75989832f8
8+
129
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/changelog
1310
NEXTAUTH_URL=http://localhost:3000
1411
NEXTAUTH_SECRET=secret

.github/workflows/algolia-index.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Update Algolia index
2+
on:
3+
push:
4+
branches:
5+
- master
6+
jobs:
7+
index:
8+
name: Update Algolia index
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v2
12+
- uses: getsentry/action-setup-volta@c52be2ea13cfdc084edb806e81958c13e445941e # v1.2.0
13+
- uses: oven-sh/setup-bun@v1
14+
with:
15+
bun-version: latest
16+
17+
- uses: actions/cache@v4
18+
id: cache
19+
with:
20+
path: ${{ github.workspace }}/node_modules
21+
key: node-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
22+
23+
- run: yarn install --frozen-lockfile
24+
if: steps.cache.outputs.cache-hit != 'true'
25+
# Remove the changelog directory to avoid a build error due to missing `DATABASE_URL`
26+
# and save some build time
27+
- run: rm -r app/changelog/feed.xml
28+
- run: yarn build
29+
# bun seems to be the most straightforward way to run a TypeScript script
30+
# without introducing another dependency like ts-node or tsx for everyone else
31+
- run: bun ./scripts/algolia.ts
32+
env:
33+
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
34+
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
35+
ALGOLIA_INDEX_PREFIX: ${{ secrets.ALGOLIA_INDEX_PREFIX }}
36+
NEXT_PUBLIC_ALGOLIA_APP_ID: ${{ secrets.NEXT_PUBLIC_ALGOLIA_APP_ID }}
37+
NEXT_PUBLIC_ALGOLIA_SEARCH_KEY: ${{ secrets.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY }}

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
"lint": "next lint",
1919
"lint:ts": "tsc --skipLibCheck",
2020
"lint:docs": "bin/lint-docs.ts",
21-
"lint:eslint": "eslint \"{src,app}/**/*.{ts,tsx,js,jsx}\"",
22-
"lint:eslint:fix": "eslint --fix \"{src,app}/**/*.{ts,tsx,js,jsx}\"",
23-
"lint:prettier": "prettier --check \"./{src,app}/**/*.{md,mdx,ts,tsx,js,jsx}\"",
24-
"lint:prettier:fix": "prettier --write \"./{src,app}/**/*.{md,mdx,ts,tsx,js,jsx}\"",
21+
"lint:eslint": "eslint \"{src,app,scripts}/**/*.{ts,tsx,js,jsx}\"",
22+
"lint:eslint:fix": "eslint --fix \"{src,app,scripts}/**/*.{ts,tsx,js,jsx}\"",
23+
"lint:prettier": "prettier --check \"./{src,app,scripts}/**/*.{md,mdx,ts,tsx,js,jsx,mjs}\"",
24+
"lint:prettier:fix": "prettier --write \"./{src,app,scripts}/**/*.{md,mdx,ts,tsx,js,jsx,mjs}\"",
2525
"lint:fix": "yarn run lint:prettier:fix && yarn run lint:eslint:fix",
2626
"sidecar": "yarn spotlight-sidecar",
2727
"test": "jest"
@@ -52,6 +52,7 @@
5252
"@sentry-internal/global-search": "^1.0.0",
5353
"@sentry/nextjs": "^8.0.0-beta.6",
5454
"@types/mdx": "^2.0.9",
55+
"algoliasearch": "^4.23.3",
5556
"esbuild": "^0.19.8",
5657
"framer-motion": "^10.12.16",
5758
"gray-matter": "^4.0.3",

scripts/algolia.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* This script is used to index the static docs HTML files generated by Next.js into Algolia.
3+
*
4+
* It's a migration from the Gatsby solution,
5+
* which relied on the `gatsby-plugin-algolia`: https://github.com/getsentry/sentry-docs/blob/3c1361bdcb23a0fcee1f3019bca7c14a5d632162/src/gatsby/utils/algolia.ts
6+
*
7+
* The record generation logic is reused as is, with *two* notable changes:
8+
* 1. We manually feed the HTML files to the record generation function
9+
* 2. We manually upload the records to Algolia
10+
*
11+
* This script is meant to be run on a GitHub Action (see `.github/workflows/algolia-index.yml`).
12+
*
13+
* If you want to run it locally,
14+
* 1. Make sure you have the required env vars set up
15+
* 2. be careful to change to `ALGOLIA_INDEX_PREFIX` to value different from `sentry-`
16+
* to avoid nuking the production index
17+
* 3. Run a production build of the app before running this script
18+
*/
19+
20+
import fs from 'fs';
21+
import {join} from 'path';
22+
23+
import {extrapolate, htmlToAlgoliaRecord} from '@sentry-internal/global-search';
24+
import algoliasearch from 'algoliasearch';
25+
26+
import {getDocsFrontMatter} from '../src/mdx';
27+
import {FrontMatter} from '../src/types';
28+
29+
// This is the path to the static files generated by Next.js for the app directory
30+
// The directory structure is not documented and could change in the future
31+
// The ideal way to do this is probably to run production server and fetch the HTML from there.
32+
const staticHtmlFilesPath = join(process.cwd(), '.next', 'server', 'app');
33+
34+
const ALGOLIA_APP_ID = process.env.ALGOLIA_APP_ID;
35+
const ALGOLIA_API_KEY = process.env.ALGOLIA_API_KEY;
36+
const ALGOLIA_INDEX_PREFIX = process.env.ALGOLIA_INDEX_PREFIX;
37+
38+
if (!ALGOLIA_APP_ID) {
39+
throw new Error('`ALGOLIA_APP_ID` env var must be configured in repo secrets');
40+
}
41+
if (!ALGOLIA_API_KEY) {
42+
throw new Error('`ALGOLIA_API_KEY` env var must be configured in repo secrets');
43+
}
44+
if (!ALGOLIA_INDEX_PREFIX) {
45+
throw new Error('`ALGOLIA_INDEX_PREFIX` env var must be configured in repo secrets');
46+
}
47+
48+
const indexName = `${ALGOLIA_INDEX_PREFIX}docs`;
49+
const client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_API_KEY);
50+
const index = client.initIndex(indexName);
51+
52+
indexAndUpload();
53+
async function indexAndUpload() {
54+
// the page front matters are the source of truth for the static doc routes
55+
// as they are used directly by generateStaticParams() on [[..path]] page
56+
const pageFrontMatters = await getDocsFrontMatter();
57+
const records = await generateAlogliaRecords(pageFrontMatters);
58+
// eslint-disable-next-line no-console
59+
console.log('🔥 Generated %d Algolia records.', records.length);
60+
// eslint-disable-next-line no-console
61+
console.log('🔥 Saving records ...');
62+
await index
63+
.saveObjects(records, {
64+
batchSize: 10000,
65+
autoGenerateObjectIDIfNotExist: true,
66+
})
67+
.then(result => {
68+
// eslint-disable-next-line no-console
69+
console.log('🔥 Saved %d Algolia records', result.objectIDs.length);
70+
});
71+
}
72+
73+
async function generateAlogliaRecords(pageFrontMatters: FrontMatter[]) {
74+
const records = await Promise.all(
75+
pageFrontMatters
76+
.filter(
77+
frontMatter => !frontMatter.draft && !frontMatter.noindex && frontMatter.title
78+
)
79+
.map(pageFm => {
80+
// eslint-disable-next-line no-console
81+
console.log('processing:', pageFm.slug);
82+
83+
const htmlFile = join(staticHtmlFilesPath, pageFm.slug + '.html');
84+
const html = fs.readFileSync(htmlFile).toString();
85+
86+
const pageRecords = htmlToAlgoliaRecord(
87+
html,
88+
{
89+
title: pageFm.title,
90+
url: '/' + pageFm.slug + '/',
91+
pathSegments: extrapolate(pageFm.slug, '/').map(x => `/${x}/`),
92+
keywords: pageFm.keywords,
93+
},
94+
'#main'
95+
);
96+
97+
return pageRecords;
98+
})
99+
);
100+
101+
return records.flat();
102+
}

src/components/docPage/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export function DocPage({
7070
<h1>{frontMatter.title}</h1>
7171
<h2>{frontMatter.description}</h2>
7272
</hgroup>
73+
{/* This exact id is important for Algolia indexing */}
7374
<div id="main">
7475
<CodeContextProvider>{children}</CodeContextProvider>
7576
</div>

src/components/search/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ function uuidv4() {
3333

3434
// Initialize Algolia Insights
3535
algoliaInsights('init', {
36-
appId: 'OOK48W9UCL',
37-
apiKey: '2d64ec1106519cbc672d863b0d200782',
36+
appId: process.env.NEXT_PUBLIC_ALGOLIA_APP_ID,
37+
apiKey: process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY,
3838
});
3939

4040
// We dont want to track anyone cross page/sessions or use cookies

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3556,7 +3556,7 @@ ajv@^6.12.4:
35563556
json-schema-traverse "^0.4.1"
35573557
uri-js "^4.2.2"
35583558

3559-
algoliasearch@^4.13.1:
3559+
algoliasearch@^4.13.1, algoliasearch@^4.23.3:
35603560
version "4.23.3"
35613561
resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.23.3.tgz#e09011d0a3b0651444916a3e6bbcba064ec44b60"
35623562
integrity sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==

0 commit comments

Comments
 (0)