Skip to content

Commit 9988032

Browse files
authored
Merge branch 'main' into chore/installs-update-ghcr-note
2 parents a74c098 + 40ade85 commit 9988032

4 files changed

Lines changed: 60 additions & 6 deletions

File tree

.vitepress/theme/installs-cache.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,5 @@
9191
"ghPackages": 2951,
9292
"maven": 0,
9393
"total": 9048,
94-
"fetchedAt": "2026-05-05T14:15:00.000Z"
94+
"fetchedAt": "2026-05-05T14:49:53.940Z"
9595
}

.vitepress/theme/installs.data.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -373,10 +373,29 @@ async function fetchGithubReleaseDownloads(
373373
}
374374

375375
// ── Maven Central ────────────────────────────────────────────────────
376-
async function fetchMavenDownloads(): Promise<number> {
377-
// Maven Central has no public downloads API. The search API only
378-
// exposes versionCount, not download stats.
379-
return 0
376+
// Maven Central has no public downloads API. The search API only
377+
// exposes versionCount, not download stats. Sonatype Central exposes
378+
// downloads to publishers via an authenticated CSV export, but that's
379+
// not available at build time.
380+
//
381+
// Until publisher-credential access lands, we use the same manual-file
382+
// pattern as ghPackages: maintainers paste current download counts from
383+
// the Sonatype Central publisher portal into manual-package-counts.json
384+
// under `maven` (keyed by `groupId:artifactId`). HWM still applies, so
385+
// values only grow.
386+
//
387+
// TODO: replace with an authenticated Sonatype Central API call once a
388+
// service-account credential is available — search the portal for
389+
// "download statistics export" or use the s01.oss.sonatype.org REST API
390+
// if the artifact is hosted there.
391+
function fetchMavenDownloads(): number {
392+
try {
393+
const raw = JSON.parse(readFileSync(MANUAL_PATH, 'utf-8'))
394+
const map = raw.maven ?? {}
395+
return Object.values(map).reduce<number>((a, b) => a + (typeof b === 'number' ? b : 0), 0)
396+
} catch {
397+
return 0
398+
}
380399
}
381400

382401
// ── Loader ───────────────────────────────────────────────────────────
@@ -397,7 +416,7 @@ export default {
397416
fetchCratesDownloads(),
398417
fetchGithubClones(cached.clonesByRepo),
399418
fetchGithubReleaseDownloads(cached.releasesByRepo),
400-
fetchMavenDownloads(),
419+
Promise.resolve(fetchMavenDownloads()),
401420
])
402421

403422
// Per-source high-water marks: each source never decreases independently.

.vitepress/theme/manual-package-counts.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,11 @@
77
"cycles-server": 917,
88
"cycles-dashboard": 517,
99
"cycles-server-events": 319
10+
},
11+
"_mavenNote": "Maven Central has no public downloads API. Sonatype Central exposes per-artifact download counts to the publisher via the Central Portal (https://central.sonatype.com/) under 'Statistics'. Update these numbers manually after each release (typical cadence: monthly), keyed by groupId:artifactId. Leave 0 if the artifact has not been published yet.",
12+
"_mavenSource": "https://central.sonatype.com/ → Statistics → Downloads",
13+
"mavenLastVerifiedAt": "2026-05-05",
14+
"maven": {
15+
"io.runcycles:cycles-spring-boot-starter": 0
1016
}
1117
}

scripts/update-github-counts.mjs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,34 @@ async function updateClones(repos, byRepo) {
126126
return { byRepo: updated, total }
127127
}
128128

129+
// ── Stale-cursor guard ───────────────────────────────────────────────
130+
// /traffic/clones is a 14-day rolling window. If this script doesn't run
131+
// for >14 days, days fall off the end of the API window and are lost
132+
// permanently. Warn loudly when any repo's cursor slips beyond a safety
133+
// threshold (default 7 days) so the issue is visible before data loss.
134+
const STALE_DAYS_THRESHOLD = 7
135+
function checkStaleClones(byRepo) {
136+
const today = new Date().toISOString().slice(0, 10)
137+
const todayMs = Date.parse(today + 'T00:00:00Z')
138+
const stale = []
139+
for (const [repo, info] of Object.entries(byRepo)) {
140+
if (!info?.lastSeenDay || info.lastSeenDay === '1970-01-01') continue
141+
const lastMs = Date.parse(info.lastSeenDay + 'T00:00:00Z')
142+
const daysBehind = Math.floor((todayMs - lastMs) / 86_400_000)
143+
if (daysBehind > STALE_DAYS_THRESHOLD) {
144+
stale.push({ repo, lastSeenDay: info.lastSeenDay, daysBehind })
145+
}
146+
}
147+
if (stale.length === 0) {
148+
console.log(`stale-cursor check: all ${Object.keys(byRepo).length} repos within ${STALE_DAYS_THRESHOLD}d of today (${today})`)
149+
return
150+
}
151+
for (const { repo, lastSeenDay, daysBehind } of stale) {
152+
console.warn(`::warning::stale clones cursor: ${repo} lastSeenDay=${lastSeenDay} (${daysBehind}d behind today=${today})`)
153+
}
154+
console.warn(`::warning::${stale.length}/${Object.keys(byRepo).length} repo(s) have stale clones cursors. /traffic/clones is a 14d rolling window — fix the scheduled workflow before days fall off.`)
155+
}
156+
129157
// ── Releases: HWM on asset download_count per repo ───────────────────
130158
// For runcycles, every release exists as a tag-only release with no
131159
// attached assets (Java services publish to GHCR; SDKs publish to PyPI/
@@ -189,6 +217,7 @@ async function main() {
189217
console.log(` repos: ${repos.length}`)
190218

191219
const clonesResult = await updateClones(repos, cache.clonesByRepo)
220+
checkStaleClones(clonesResult.byRepo)
192221
const releasesResult = await updateReleases(repos, cache.releasesByRepo)
193222
const ghpkgResult = await updateGhPackages()
194223

0 commit comments

Comments
 (0)