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
7 changes: 7 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,14 @@ jobs:
with:
node-version: 24

- name: Fetch homepage metrics
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: bash scripts/fetch-homepage-metrics.sh

- name: Install dependencies and build
env:
REQUIRE_LIVE_HOMEPAGE_METRICS: "true"
run: |
pnpm install --no-frozen-lockfile
pnpm run build
Expand Down
26 changes: 22 additions & 4 deletions lib/docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,29 @@
* This function is called at build time on the server side
* @returns Promise<number> Docker pull count or fallback value
*/
const DOCKER_FETCH_TIMEOUT_MS = 10_000;
const DOCKER_PULLS_FALLBACK = 7_166_727;
const REQUIRE_LIVE_HOMEPAGE_METRICS = process.env.REQUIRE_LIVE_HOMEPAGE_METRICS === 'true';

export async function getDockerPulls(): Promise<number> {
const injectedPullsValue = process.env.HOMEPAGE_DOCKER_PULLS;
if (injectedPullsValue !== undefined) {
const injectedPulls = Number(injectedPullsValue);
if (Number.isInteger(injectedPulls) && injectedPulls > 0) {
return injectedPulls;
}
Comment on lines +13 to +16

throw new Error('Invalid injected Docker Hub homepage metrics');
}

const endpoints = [
'https://hub.docker.com/v2/namespaces/rustfs/repositories/rustfs',
'https://hub.docker.com/v2/repositories/rustfs/rustfs/',
'https://hub.docker.com/v2/namespaces/rustfs/repositories/rustfs',
];

for (const endpoint of endpoints) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 3500);
const timeoutId = setTimeout(() => controller.abort(), DOCKER_FETCH_TIMEOUT_MS);

try {
const response = await fetch(endpoint, {
Expand All @@ -25,7 +39,7 @@ export async function getDockerPulls(): Promise<number> {

if (response.ok) {
const json = await response.json() as { pull_count?: number };
if (typeof json.pull_count === 'number') {
if (typeof json.pull_count === 'number' && Number.isFinite(json.pull_count)) {
return json.pull_count;
}
}
Expand All @@ -40,5 +54,9 @@ export async function getDockerPulls(): Promise<number> {
}
}

return 6371731;
if (REQUIRE_LIVE_HOMEPAGE_METRICS) {
throw new Error('Unable to fetch live Docker Hub homepage metrics');
}

return DOCKER_PULLS_FALLBACK;
}
50 changes: 39 additions & 11 deletions lib/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ export interface GitHubMetrics {
commits: number;
}

const GITHUB_FETCH_TIMEOUT_MS = 3500;
const GITHUB_FETCH_TIMEOUT_MS = 10_000;

const GITHUB_METRICS_FALLBACK: GitHubMetrics = {
stars: 30_530,
forks: 1_349,
commits: 5_089,
};

async function fetchGitHub(
url: string,
Expand Down Expand Up @@ -140,11 +146,29 @@ export async function getLatestCliRelease(): Promise<GitHubRelease | null> {
* @returns Promise<GitHubMetrics>
*/
export async function getGitHubMetrics(): Promise<GitHubMetrics> {
const fallback: GitHubMetrics = {
stars: 30000,
forks: 1300,
commits: 4800,
};
const injectedMetricValues = [
process.env.HOMEPAGE_GITHUB_STARS,
process.env.HOMEPAGE_GITHUB_FORKS,
process.env.HOMEPAGE_GITHUB_COMMITS,
];

if (injectedMetricValues.some((value) => value !== undefined)) {
if (injectedMetricValues.some((value) => value === undefined)) {
throw new Error('Incomplete injected GitHub homepage metrics');
}

const injectedMetrics = {
stars: Number(injectedMetricValues[0]),
forks: Number(injectedMetricValues[1]),
commits: Number(injectedMetricValues[2]),
};

if (Object.values(injectedMetrics).every((value) => Number.isInteger(value) && value > 0)) {
return injectedMetrics;
}

throw new Error('Invalid injected GitHub homepage metrics');
}

try {
const [repoRes, commitsRes] = await Promise.all([
Expand All @@ -165,7 +189,7 @@ export async function getGitHubMetrics(): Promise<GitHubMetrics> {
]);

if (!repoRes.ok || !commitsRes.ok) {
return fallback;
throw new Error(`GitHub metrics API returned ${repoRes.status}/${commitsRes.status}`);
}

const repo = await repoRes.json() as { stargazers_count?: number; forks_count?: number };
Expand All @@ -181,9 +205,9 @@ export async function getGitHubMetrics(): Promise<GitHubMetrics> {
}

return {
stars: repo.stargazers_count ?? fallback.stars,
forks: repo.forks_count ?? fallback.forks,
commits: Number.isFinite(commits) && commits > 0 ? commits : fallback.commits,
stars: repo.stargazers_count ?? GITHUB_METRICS_FALLBACK.stars,
forks: repo.forks_count ?? GITHUB_METRICS_FALLBACK.forks,
commits: Number.isFinite(commits) && commits > 0 ? commits : GITHUB_METRICS_FALLBACK.commits,
};
} catch (error) {
if (isAbortError(error)) {
Expand All @@ -192,7 +216,11 @@ export async function getGitHubMetrics(): Promise<GitHubMetrics> {
console.warn('Failed to fetch GitHub metrics:', error);
}

return fallback;
if (process.env.REQUIRE_LIVE_HOMEPAGE_METRICS === 'true') {
throw new Error('Unable to fetch live GitHub homepage metrics', { cause: error });
}

return GITHUB_METRICS_FALLBACK;
}
}

Expand Down
34 changes: 34 additions & 0 deletions scripts/fetch-homepage-metrics.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash

set -euo pipefail

: "${GITHUB_TOKEN:?GITHUB_TOKEN is required}"
: "${GITHUB_ENV:?GITHUB_ENV is required}"

github_metrics="$({
curl --fail --silent --show-error \
--retry 3 --retry-all-errors --connect-timeout 10 --max-time 30 \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-H "User-Agent: RustFS-Website" \
--data-binary '{"query":"query { repository(owner: \"rustfs\", name: \"rustfs\") { stargazerCount forkCount defaultBranchRef { target { ... on Commit { history { totalCount } } } } } }"}' \
Comment on lines +11 to +14
https://api.github.com/graphql
})"

docker_metrics="$({
curl --fail --silent --show-error \
--retry 3 --retry-all-errors --connect-timeout 10 --max-time 30 \
'https://hub.docker.com/v2/namespaces/rustfs/repositories?page_size=100&name=rustfs'
})"

stars="$(jq -er '.data.repository.stargazerCount | if type == "number" and . > 0 then . else error("invalid stars") end' <<<"${github_metrics}")"
forks="$(jq -er '.data.repository.forkCount | if type == "number" and . > 0 then . else error("invalid forks") end' <<<"${github_metrics}")"
commits="$(jq -er '.data.repository.defaultBranchRef.target.history.totalCount | if type == "number" and . > 0 then . else error("invalid commits") end' <<<"${github_metrics}")"
docker_pulls="$(jq -er '.results[] | select(.name == "rustfs") | .pull_count | if type == "number" and . > 0 then . else error("invalid Docker pulls") end' <<<"${docker_metrics}")"

{
echo "HOMEPAGE_GITHUB_STARS=${stars}"
echo "HOMEPAGE_GITHUB_FORKS=${forks}"
echo "HOMEPAGE_GITHUB_COMMITS=${commits}"
echo "HOMEPAGE_DOCKER_PULLS=${docker_pulls}"
} >>"${GITHUB_ENV}"