Skip to content

Commit 5528deb

Browse files
authored
Render github repo metadata with staticProps (bazel-contrib#184)
By avoiding a client-side useEffect, we get static content that the search crawler will be able to index. Then the full-text search can match on content of the repo descriptions Repeat of bazel-contrib#180 with fixes
1 parent 78dcb95 commit 5528deb

File tree

8 files changed

+115
-62
lines changed

8 files changed

+115
-62
lines changed

.github/workflows/deploy.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,26 @@ jobs:
4949
run: |
5050
git checkout ${{ inputs.bcrCommitHash }}
5151
52+
- name: Find latest successful run of fetch_repo_metadata workflow
53+
id: find-run
54+
env:
55+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
56+
run: |
57+
run_id=$(gh run list \
58+
--workflow "fetch_repo_metadata.yml" \
59+
--branch main \
60+
--json databaseId,status,conclusion \
61+
--jq 'map(select(.status=="completed" and .conclusion=="success")) | first | .databaseId')
62+
echo "run_id=$run_id" >> $GITHUB_OUTPUT
63+
64+
- name: Download and extract metadata artifact
65+
env:
66+
GH_TOKEN: ${{ github.token }}
67+
run: |
68+
gh run download ${{ steps.find-run.outputs.run_id }} \
69+
--name github_metadata \
70+
--dir data/github_metadata
71+
5272
- name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built.
5373
run: |
5474
pnpm install --frozen-lockfile

.github/workflows/fetch_repo_metadata.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
steps:
1313
- uses: actions/checkout@v5
14+
# Note, in this case we only need the latest commit, so it's okay with does a depth=1
1415
with:
1516
submodules: true
1617
- name: Checkout latest commit of BCR submodule

.github/workflows/test.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,26 @@ jobs:
4545
run: |
4646
git checkout ${{ inputs.bcrCommitHash }}
4747
48+
- name: Find latest successful run of fetch_repo_metadata workflow
49+
id: find-run
50+
env:
51+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52+
run: |
53+
run_id=$(gh run list \
54+
--workflow "fetch_repo_metadata.yml" \
55+
--branch main \
56+
--json databaseId,status,conclusion \
57+
--jq 'map(select(.status=="completed" and .conclusion=="success")) | first | .databaseId')
58+
echo "run_id=$run_id" >> $GITHUB_OUTPUT
59+
60+
- name: Download and extract metadata artifact
61+
env:
62+
GH_TOKEN: ${{ github.token }}
63+
run: |
64+
gh run download ${{ steps.find-run.outputs.run_id }} \
65+
--name github_metadata \
66+
--dir data/github_metadata
67+
4868
- name: Build 🔧 # Outputs the result to the 'build' folder.
4969
run: pnpm run build
5070
env:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ yarn-error.log*
4040
.netlify
4141

4242
/bazel-*
43+
/data/github_metadata/*.json

data/githubMetadata.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import * as fs from 'fs'
2+
import * as path from 'path'
3+
4+
export interface GithubRepositoryMetadata {
5+
description: string | null
6+
license: { spdx_id: string; name: string; url: string } | null
7+
topics: string[] | null
8+
stargazers: number | null
9+
}
10+
11+
/**
12+
* Reads GitHub metadata from static JSON files generated by GitHub Actions.
13+
* This replaces client-side API calls with build-time static data.
14+
*/
15+
export async function getGithubRepositoryMetadata(
16+
moduleName: string
17+
): Promise<GithubRepositoryMetadata | null> {
18+
try {
19+
const metadataPath = path.join(
20+
process.cwd(),
21+
'data',
22+
'github_metadata',
23+
`${moduleName}.github_metadata.json`
24+
)
25+
26+
if (!fs.existsSync(metadataPath)) {
27+
return null
28+
}
29+
30+
const fileContent = fs.readFileSync(metadataPath, 'utf-8')
31+
const rawData = JSON.parse(fileContent)
32+
33+
// Transform the GitHub API response format to our interface
34+
return {
35+
description: rawData.description || null,
36+
license: rawData.licenseInfo
37+
? {
38+
spdx_id: rawData.licenseInfo.key || '',
39+
name: rawData.licenseInfo.name || '',
40+
url: rawData.licenseInfo.key
41+
? `https://opensource.org/licenses/${rawData.licenseInfo.key}`
42+
: '',
43+
}
44+
: null,
45+
topics:
46+
rawData.repositoryTopics?.nodes?.map(
47+
(topic: { name: string }) => topic.name
48+
) || null,
49+
stargazers: rawData.stargazerCount || null,
50+
}
51+
} catch (error) {
52+
console.warn(`Failed to read GitHub metadata for ${moduleName}:`, error)
53+
return null
54+
}
55+
}

data/github_metadata/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
This folder is populated during CI runs by downloading a GitHub Actions artifact.
2+
See .github/workflows/fetch_repo_metadata.yml

data/moduleStaticProps.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
ModuleInfo,
88
reverseDependencies,
99
} from './utils'
10+
import { getGithubRepositoryMetadata } from './githubMetadata'
1011

1112
export interface VersionInfo {
1213
version: string
@@ -44,12 +45,19 @@ export const getStaticPropsModulePage = async (
4445
const latestVersion = versions[0]
4546
const selectedVersion = version || latestVersion
4647

48+
// Get GitHub metadata from static JSON files
49+
const githubMetadata = await getGithubRepositoryMetadata(module)
50+
if (!githubMetadata) {
51+
console.warn(`No GitHub metadata found for module ${module}`)
52+
}
53+
4754
return {
4855
props: {
4956
metadata,
5057
versionInfos,
5158
selectedVersion,
5259
reverseDependencies: await reverseDependencies(module),
60+
githubMetadata,
5361
},
5462
}
5563
}

pages/modules/[module].tsx

Lines changed: 8 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
getStaticPropsModulePage,
1818
VersionInfo,
1919
} from '../../data/moduleStaticProps'
20+
import { GithubRepositoryMetadata } from '../../data/githubMetadata'
2021
import { formatDistance, parseISO } from 'date-fns'
2122
import { faGlobe, faScaleBalanced } from '@fortawesome/free-solid-svg-icons'
2223

@@ -25,6 +26,7 @@ interface ModulePageProps {
2526
versionInfos: VersionInfo[]
2627
selectedVersion: string
2728
reverseDependencies: string[]
29+
githubMetadata: GithubRepositoryMetadata | null
2830
}
2931

3032
const GITHUB_API_USER_AGENT = 'Bazel Central Registry UI'
@@ -40,6 +42,7 @@ const ModulePage: NextPage<ModulePageProps> = ({
4042
versionInfos,
4143
selectedVersion,
4244
reverseDependencies,
45+
githubMetadata,
4346
}) => {
4447
const router = useRouter()
4548
const { module } = router.query
@@ -61,12 +64,11 @@ const ModulePage: NextPage<ModulePageProps> = ({
6164
selectedVersion
6265
)
6366

64-
const {
65-
description: repoDescription,
66-
license: repoLicense,
67-
topics: repoTopics,
68-
stargazers: repoStargazers,
69-
} = useGithubMetadata(firstGithubRepository)
67+
// Use GitHub metadata from static build-time data instead of client-side hook
68+
const repoDescription = githubMetadata?.description || undefined
69+
const repoLicense = githubMetadata?.license || undefined
70+
const repoTopics = githubMetadata?.topics || undefined
71+
const repoStargazers = githubMetadata?.stargazers || undefined
7072

7173
const isQualifiedForShowAllVersions =
7274
versionInfos.length > NUM_VERSIONS_ON_PAGE_LOAD
@@ -621,60 +623,4 @@ const useDetectReleaseFormatViaGithubApi = (
621623
return releaseTagFormat
622624
}
623625

624-
const useGithubMetadata = (metadataRepository: string | undefined) => {
625-
const githubOwnerAndRepo = metadataRepository?.replace('github:', '')
626-
const [description, setDescription] = useState<string | undefined>(undefined)
627-
const [license, setLicense] = useState<
628-
{ spdx_id: string; name: string; url: string } | undefined
629-
>()
630-
const [topics, setTopics] = useState<string[]>([])
631-
const [stargazers, setStargazers] = useState<number | undefined>(undefined)
632-
633-
useEffect(() => {
634-
const fetchRepoDescription = async () => {
635-
if (!githubOwnerAndRepo) {
636-
return
637-
}
638-
639-
try {
640-
const response = await fetch(
641-
`https://api.github.com/repos/${githubOwnerAndRepo}`,
642-
{
643-
method: 'GET',
644-
headers: {
645-
Accept: 'application/vnd.github+json',
646-
'User-Agent': GITHUB_API_USER_AGENT,
647-
'X-GitHub-Api-Version': GITHUB_API_VERSION,
648-
},
649-
}
650-
)
651-
652-
if (response.ok) {
653-
const repoData = await response.json()
654-
setStargazers(repoData.stargazers_count)
655-
setDescription(repoData.description)
656-
if (repoData.license) {
657-
setLicense(repoData.license)
658-
}
659-
660-
if (Array.isArray(repoData.topics)) {
661-
setTopics(repoData.topics)
662-
}
663-
}
664-
} catch (error) {
665-
console.error('Failed to fetch repository description:', error)
666-
}
667-
}
668-
669-
fetchRepoDescription()
670-
}, [githubOwnerAndRepo])
671-
672-
return {
673-
description,
674-
license,
675-
topics,
676-
stargazers,
677-
}
678-
}
679-
680626
export default ModulePage

0 commit comments

Comments
 (0)