Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,26 @@ jobs:
run: |
git checkout ${{ inputs.bcrCommitHash }}

- name: Find latest successful run of fetch_repo_metadata workflow
id: find-run
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
run_id=$(gh run list \
--workflow "fetch_repo_metadata.yml" \
--branch main \
--json databaseId,status,conclusion \
--jq 'map(select(.status=="completed" and .conclusion=="success")) | first | .databaseId')
echo "run_id=$run_id" >> $GITHUB_OUTPUT

- name: Download and extract metadata artifact
env:
GH_TOKEN: ${{ github.token }}
run: |
gh run download ${{ steps.find-run.outputs.run_id }} \
--name github_metadata \
--dir data/github_metadata

- 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.
run: |
pnpm install --frozen-lockfile
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/fetch_repo_metadata.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
# Note, in this case we only need the latest commit, so it's okay with does a depth=1
with:
submodules: true
- name: Checkout latest commit of BCR submodule
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,26 @@ jobs:
run: |
git checkout ${{ inputs.bcrCommitHash }}

- name: Find latest successful run of fetch_repo_metadata workflow
id: find-run
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
run_id=$(gh run list \
--workflow "fetch_repo_metadata.yml" \
--branch main \
--json databaseId,status,conclusion \
--jq 'map(select(.status=="completed" and .conclusion=="success")) | first | .databaseId')
echo "run_id=$run_id" >> $GITHUB_OUTPUT

- name: Download and extract metadata artifact
env:
GH_TOKEN: ${{ github.token }}
run: |
gh run download ${{ steps.find-run.outputs.run_id }} \
--name github_metadata \
--dir data/github_metadata

- name: Build 🔧 # Outputs the result to the 'build' folder.
run: pnpm run build
env:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ yarn-error.log*
.netlify

/bazel-*
/data/github_metadata/*.json
55 changes: 55 additions & 0 deletions data/githubMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as fs from 'fs'
import * as path from 'path'

export interface GithubRepositoryMetadata {
description: string | null
license: { spdx_id: string; name: string; url: string } | null
topics: string[] | null
stargazers: number | null
}

/**
* Reads GitHub metadata from static JSON files generated by GitHub Actions.
* This replaces client-side API calls with build-time static data.
*/
export async function getGithubRepositoryMetadata(
moduleName: string
): Promise<GithubRepositoryMetadata | null> {
try {
const metadataPath = path.join(
process.cwd(),
'data',
'github_metadata',
`${moduleName}.github_metadata.json`
)

if (!fs.existsSync(metadataPath)) {
return null
}

const fileContent = fs.readFileSync(metadataPath, 'utf-8')
const rawData = JSON.parse(fileContent)

// Transform the GitHub API response format to our interface
return {
description: rawData.description || null,
license: rawData.licenseInfo
? {
spdx_id: rawData.licenseInfo.key || '',
name: rawData.licenseInfo.name || '',
url: rawData.licenseInfo.key
? `https://opensource.org/licenses/${rawData.licenseInfo.key}`
: '',
}
: null,
topics:
rawData.repositoryTopics?.nodes?.map(
(topic: { name: string }) => topic.name
) || null,
stargazers: rawData.stargazerCount || null,
}
} catch (error) {
console.warn(`Failed to read GitHub metadata for ${moduleName}:`, error)
return null
}
}
2 changes: 2 additions & 0 deletions data/github_metadata/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This folder is populated during CI runs by downloading a GitHub Actions artifact.
See .github/workflows/fetch_repo_metadata.yml
8 changes: 8 additions & 0 deletions data/moduleStaticProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ModuleInfo,
reverseDependencies,
} from './utils'
import { getGithubRepositoryMetadata } from './githubMetadata'

export interface VersionInfo {
version: string
Expand Down Expand Up @@ -44,12 +45,19 @@ export const getStaticPropsModulePage = async (
const latestVersion = versions[0]
const selectedVersion = version || latestVersion

// Get GitHub metadata from static JSON files
const githubMetadata = await getGithubRepositoryMetadata(module)
if (!githubMetadata) {
console.warn(`No GitHub metadata found for module ${module}`)
}

return {
props: {
metadata,
versionInfos,
selectedVersion,
reverseDependencies: await reverseDependencies(module),
githubMetadata,
},
}
}
Expand Down
70 changes: 8 additions & 62 deletions pages/modules/[module].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
getStaticPropsModulePage,
VersionInfo,
} from '../../data/moduleStaticProps'
import { GithubRepositoryMetadata } from '../../data/githubMetadata'
import { formatDistance, parseISO } from 'date-fns'
import { faGlobe, faScaleBalanced } from '@fortawesome/free-solid-svg-icons'

Expand All @@ -25,6 +26,7 @@ interface ModulePageProps {
versionInfos: VersionInfo[]
selectedVersion: string
reverseDependencies: string[]
githubMetadata: GithubRepositoryMetadata | null
}

const GITHUB_API_USER_AGENT = 'Bazel Central Registry UI'
Expand All @@ -40,6 +42,7 @@ const ModulePage: NextPage<ModulePageProps> = ({
versionInfos,
selectedVersion,
reverseDependencies,
githubMetadata,
}) => {
const router = useRouter()
const { module } = router.query
Expand All @@ -61,12 +64,11 @@ const ModulePage: NextPage<ModulePageProps> = ({
selectedVersion
)

const {
description: repoDescription,
license: repoLicense,
topics: repoTopics,
stargazers: repoStargazers,
} = useGithubMetadata(firstGithubRepository)
// Use GitHub metadata from static build-time data instead of client-side hook
const repoDescription = githubMetadata?.description || undefined
const repoLicense = githubMetadata?.license || undefined
const repoTopics = githubMetadata?.topics || undefined
const repoStargazers = githubMetadata?.stargazers || undefined

const isQualifiedForShowAllVersions =
versionInfos.length > NUM_VERSIONS_ON_PAGE_LOAD
Expand Down Expand Up @@ -621,60 +623,4 @@ const useDetectReleaseFormatViaGithubApi = (
return releaseTagFormat
}

const useGithubMetadata = (metadataRepository: string | undefined) => {
const githubOwnerAndRepo = metadataRepository?.replace('github:', '')
const [description, setDescription] = useState<string | undefined>(undefined)
const [license, setLicense] = useState<
{ spdx_id: string; name: string; url: string } | undefined
>()
const [topics, setTopics] = useState<string[]>([])
const [stargazers, setStargazers] = useState<number | undefined>(undefined)

useEffect(() => {
const fetchRepoDescription = async () => {
if (!githubOwnerAndRepo) {
return
}

try {
const response = await fetch(
`https://api.github.com/repos/${githubOwnerAndRepo}`,
{
method: 'GET',
headers: {
Accept: 'application/vnd.github+json',
'User-Agent': GITHUB_API_USER_AGENT,
'X-GitHub-Api-Version': GITHUB_API_VERSION,
},
}
)

if (response.ok) {
const repoData = await response.json()
setStargazers(repoData.stargazers_count)
setDescription(repoData.description)
if (repoData.license) {
setLicense(repoData.license)
}

if (Array.isArray(repoData.topics)) {
setTopics(repoData.topics)
}
}
} catch (error) {
console.error('Failed to fetch repository description:', error)
}
}

fetchRepoDescription()
}, [githubOwnerAndRepo])

return {
description,
license,
topics,
stargazers,
}
}

export default ModulePage