Skip to content

Commit af8251d

Browse files
committed
fix: fixes after prod test
Signed-off-by: Mouad BANI <mouad-mb@outlook.com>
1 parent 8de3017 commit af8251d

5 files changed

Lines changed: 78 additions & 40 deletions

File tree

services/apps/packages_worker/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
"trigger-bootstrap:local": "set -a && . ../../../backend/.env.dist.local && . ../../../backend/.env.override.local && set +a && SERVICE=bq-dataset-ingest tsx src/scripts/triggerBootstrap.ts",
2424
"trigger-go": "SERVICE=go-worker tsx src/scripts/triggerGoEnrich.ts",
2525
"trigger-go:local": "set -a && . ../../../backend/.env.dist.local && . ../../../backend/.env.override.local && set +a && SERVICE=go-worker tsx src/scripts/triggerGoEnrich.ts",
26+
"trigger-nuget": "SERVICE=nuget-worker tsx src/scripts/triggerNuGetSync.ts",
27+
"trigger-nuget:local": "set -a && . ../../../backend/.env.dist.local && . ../../../backend/.env.override.local && set +a && SERVICE=nuget-worker tsx src/scripts/triggerNuGetSync.ts",
2628
"start:npm-worker": "CROWD_TEMPORAL_TASKQUEUE=npm-worker SERVICE=npm-worker tsx src/bin/npm-worker.ts",
2729
"dev:npm-worker": "CROWD_TEMPORAL_TASKQUEUE=npm-worker SERVICE=npm-worker LOG_LEVEL=trace nodemon --watch src --watch ../../libs --ext ts --exec tsx --inspect=0.0.0.0:9236 src/bin/npm-worker.ts",
2830
"dev:npm-worker:local": "set -a && . ../../../backend/.env.dist.local && . ../../../backend/.env.override.local && set +a && CROWD_TEMPORAL_TASKQUEUE=npm-worker SERVICE=npm-worker LOG_LEVEL=trace nodemon --watch src --watch ../../libs --ext ts --exec tsx --inspect=0.0.0.0:9236 src/bin/npm-worker.ts",

services/apps/packages_worker/src/nuget/client.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,7 @@ function classifyError(err: unknown): NuGetFetchError | null {
6565
return null
6666
}
6767

68-
export async function fetchSearch(
69-
packageId: string,
70-
): Promise<NuGetSearchItem | NuGetFetchError> {
68+
export async function fetchSearch(packageId: string): Promise<NuGetSearchItem | NuGetFetchError> {
7169
const { searchBaseUrl } = await resolveEndpoints()
7270
const lowerPackageId = packageId.toLowerCase()
7371

@@ -97,12 +95,21 @@ export async function fetchSearch(
9795
}
9896
}
9997

100-
async function fetchRegistrationPage(pageId: string): Promise<NuGetRegistrationPage> {
101-
const resp = await axios.get<NuGetRegistrationPage>(pageId, {
102-
headers: { 'Accept-Encoding': 'gzip' },
103-
timeout: 15000,
104-
})
105-
return resp.data
98+
async function fetchRegistrationPage(
99+
pageId: string,
100+
maxAttempts = 2,
101+
): Promise<NuGetRegistrationPage> {
102+
for (let attempt = 1; ; attempt++) {
103+
try {
104+
const resp = await axios.get<NuGetRegistrationPage>(pageId, {
105+
headers: { 'Accept-Encoding': 'gzip' },
106+
timeout: 15000,
107+
})
108+
return resp.data
109+
} catch (err) {
110+
if (attempt >= maxAttempts) throw err
111+
}
112+
}
106113
}
107114

108115
export async function fetchRegistration(
@@ -125,12 +132,8 @@ export async function fetchRegistration(
125132
for (let i = 0; i < index.items.length; i++) {
126133
const page = index.items[i]
127134
if (!page.items) {
128-
try {
129-
const fullPage = await fetchRegistrationPage(page['@id'])
130-
index.items[i] = { ...page, items: fullPage.items ?? [] }
131-
} catch {
132-
index.items[i] = { ...page, items: [] }
133-
}
135+
const fullPage = await fetchRegistrationPage(page['@id'])
136+
index.items[i] = { ...page, items: fullPage.items ?? [] }
134137
}
135138
}
136139

services/apps/packages_worker/src/nuget/normalize.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ function isPrerelease(version: string): boolean {
1919
return version.includes('-')
2020
}
2121

22+
const SCM_HOSTS = ['github.com', 'gitlab.com', 'bitbucket.org']
23+
2224
function normalizeRepoUrl(url: string | undefined): string | null {
2325
if (!url) return null
2426
return url
@@ -29,6 +31,15 @@ function normalizeRepoUrl(url: string | undefined): string | null {
2931
.replace(/^http:\/\/github\.com\//, 'https://github.com/')
3032
}
3133

34+
function isScmUrl(url: string | undefined): boolean {
35+
if (!url) return false
36+
try {
37+
return SCM_HOSTS.some((h) => new URL(url).hostname.endsWith(h))
38+
} catch {
39+
return false
40+
}
41+
}
42+
3243
function parseLicense(
3344
licenseExpression: string | undefined,
3445
licenseUrl: string | undefined,
@@ -69,10 +80,19 @@ export function normalizeNuGetPackage(
6980

7081
const homepage = searchResult?.projectUrl || latestListedEntry?.projectUrl || null
7182

72-
const declaredRepositoryUrl = latestEntry4License?.repository?.url
73-
? latestEntry4License.repository.url
74-
: null
75-
const repositoryUrl = normalizeRepoUrl(declaredRepositoryUrl)
83+
// Scan all entries (prefer latest listed, then any) for a nuspec <repository> url.
84+
// Fall back to a SCM-shaped projectUrl/homepage when no nuspec repository is present.
85+
const entriesForRepo = latestListedEntry
86+
? [
87+
latestListedEntry,
88+
...listedEntries.slice(0, -1).reverse(),
89+
...(latestEntry ? [latestEntry] : []),
90+
]
91+
: [...allEntries].reverse()
92+
const nuspecRepoUrl = entriesForRepo.find((e) => e.repository?.url)?.repository?.url
93+
const declaredRepositoryUrl = nuspecRepoUrl ?? null
94+
const repoCandidate = nuspecRepoUrl ?? (isScmUrl(homepage) ? homepage : null)
95+
const repositoryUrl = normalizeRepoUrl(repoCandidate ?? undefined)
7696

7797
const keywords = searchResult?.tags && searchResult.tags.length > 0 ? searchResult.tags : null
7898

@@ -85,16 +105,25 @@ export function normalizeNuGetPackage(
85105
status = 'active'
86106
}
87107

108+
// NuGet stamps unlisted versions with 1900-01-01T00:00:00Z as a sentinel — exclude them.
88109
const publishedDates = allEntries
89110
.filter((e) => e.published)
90111
.map((e) => new Date(e.published as string))
91-
.filter((d) => !isNaN(d.getTime()))
112+
.filter((d) => !isNaN(d.getTime()) && d.getUTCFullYear() > 1900)
92113
.sort((a, b) => a.getTime() - b.getTime())
93114

94115
const firstReleaseAt = publishedDates.length > 0 ? publishedDates[0] : null
95116

96117
const latestEntry4Date = latestListedEntry ?? latestEntry
97-
const latestReleaseAt = latestEntry4Date?.published ? new Date(latestEntry4Date.published) : null
118+
const latestReleaseAtRaw = latestEntry4Date?.published
119+
? new Date(latestEntry4Date.published)
120+
: null
121+
const latestReleaseAt =
122+
latestReleaseAtRaw &&
123+
!isNaN(latestReleaseAtRaw.getTime()) &&
124+
latestReleaseAtRaw.getUTCFullYear() > 1900
125+
? latestReleaseAtRaw
126+
: null
98127

99128
const totalDownloads = searchResult?.totalDownloads ?? 0
100129

@@ -136,7 +165,7 @@ export function normalizeNuGetPackage(
136165
latestVersion,
137166
versionsCount: allEntries.length,
138167
firstReleaseAt,
139-
latestReleaseAt: latestReleaseAt && !isNaN(latestReleaseAt.getTime()) ? latestReleaseAt : null,
168+
latestReleaseAt,
140169
totalDownloads,
141170
owners,
142171
authors,

services/apps/packages_worker/src/nuget/runNuGetEnrichmentLoop.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
QueryExecutor,
55
listNuGetPackagesToSync,
66
logAuditFieldChange,
7+
markNuGetPackageError,
78
recordNuGetDownloadSnapshot,
89
replacePackageMaintainers,
910
upsertMaintainer,
@@ -79,14 +80,13 @@ async function processPackage(
7980
latestReleaseAt: null,
8081
registryUrl: nugetRegistryUrl(packageId),
8182
ingestionSource: 'nuget_not_found',
82-
dependentPackagesCount: pkg.dependentPackagesCount,
83-
dependentReposCount: pkg.dependentReposCount,
8483
})
8584
log.warn({ purl: pkg.purl }, 'Package not found on NuGet registry — writing minimal record')
8685
return 'skipped'
8786
}
8887
if (registrationResult.kind === 'RATE_LIMIT') {
8988
log.warn({ purl: pkg.purl }, 'Rate limited by NuGet registry — will retry next pass')
89+
await markNuGetPackageError(qx, pkg.purl)
9090
return 'error'
9191
}
9292
throw new Error(
@@ -119,8 +119,6 @@ async function processPackage(
119119
latestReleaseAt: normalized.latestReleaseAt,
120120
registryUrl: nugetRegistryUrl(packageId),
121121
ingestionSource: 'nuget-registry',
122-
dependentPackagesCount: pkg.dependentPackagesCount,
123-
dependentReposCount: pkg.dependentReposCount,
124122
})
125123
pkgChanged.forEach((f) => changed.add(f))
126124

@@ -224,6 +222,11 @@ export async function processBatch(
224222
} catch (err) {
225223
const message = err instanceof Error ? err.message : String(err)
226224
log.error({ purl: pkg.purl, error: message }, 'Unexpected error processing package')
225+
try {
226+
await markNuGetPackageError(qx, pkg.purl)
227+
} catch {
228+
// best-effort — don't let a failed mark crash the batch
229+
}
227230
counts.error++
228231
}
229232
}),

services/libs/data-access-layer/src/osspckgs/nuget.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ export type NuGetPackageToSync = {
88
id: number
99
purl: string
1010
name: string
11-
dependentPackagesCount: number | null
12-
dependentReposCount: number | null
1311
latestVersion: string | null
1412
}
1513

@@ -30,8 +28,6 @@ export type IDbNuGetPackageUpsert = {
3028
latestReleaseAt: Date | null
3129
registryUrl: string | null
3230
ingestionSource: string
33-
dependentPackagesCount: number | null
34-
dependentReposCount: number | null
3531
}
3632

3733
export type IDbNuGetVersionUpsert = {
@@ -67,21 +63,22 @@ export async function upsertNuGetPackage(
6763
description, homepage, declared_repository_url, repository_url,
6864
licenses, licenses_raw, keywords, status,
6965
latest_version, versions_count, first_release_at, latest_release_at,
70-
registry_url, ingestion_source, dependent_count, dependent_repos_count,
66+
registry_url, ingestion_source,
7167
last_synced_at, created_at
7268
) VALUES (
7369
$(purl), 'nuget', NULL, $(name),
7470
$(description), $(homepage), $(declaredRepositoryUrl), $(repositoryUrl),
7571
$(licenses)::text[], $(licensesRaw), $(keywords)::text[], $(status),
7672
$(latestVersion), $(versionsCount), $(firstReleaseAt), $(latestReleaseAt),
77-
$(registryUrl), $(ingestionSource), $(dependentPackagesCount), $(dependentReposCount),
73+
$(registryUrl), $(ingestionSource),
7874
NOW(), NOW()
7975
)
8076
ON CONFLICT (purl) DO UPDATE SET
8177
description = COALESCE(EXCLUDED.description, packages.description),
8278
homepage = COALESCE(EXCLUDED.homepage, packages.homepage),
8379
declared_repository_url = COALESCE(EXCLUDED.declared_repository_url, packages.declared_repository_url),
84-
repository_url = COALESCE(EXCLUDED.repository_url, packages.repository_url),
80+
repository_url = COALESCE(EXCLUDED.repository_url, packages.repository_url,
81+
packages.declared_repository_url),
8582
licenses = COALESCE(EXCLUDED.licenses, packages.licenses),
8683
licenses_raw = COALESCE(EXCLUDED.licenses_raw, packages.licenses_raw),
8784
keywords = COALESCE(EXCLUDED.keywords, packages.keywords),
@@ -92,8 +89,6 @@ export async function upsertNuGetPackage(
9289
latest_release_at = COALESCE(EXCLUDED.latest_release_at, packages.latest_release_at),
9390
registry_url = COALESCE(EXCLUDED.registry_url, packages.registry_url),
9491
ingestion_source = EXCLUDED.ingestion_source,
95-
dependent_count = COALESCE(EXCLUDED.dependent_count, packages.dependent_count),
96-
dependent_repos_count = COALESCE(EXCLUDED.dependent_repos_count, packages.dependent_repos_count),
9792
last_synced_at = NOW()
9893
RETURNING id, description, homepage, declared_repository_url, repository_url,
9994
licenses, licenses_raw, keywords, status,
@@ -136,8 +131,6 @@ export async function upsertNuGetPackage(
136131
latestReleaseAt: item.latestReleaseAt ?? null,
137132
registryUrl: item.registryUrl ?? null,
138133
ingestionSource: item.ingestionSource,
139-
dependentPackagesCount: item.dependentPackagesCount ?? null,
140-
dependentReposCount: item.dependentReposCount ?? null,
141134
},
142135
)
143136
return { id: row.id as number, changedFields: row.changed_fields as string[] }
@@ -246,9 +239,7 @@ export async function listNuGetPackagesToSync(
246239
p.id,
247240
p.purl,
248241
p.name,
249-
p.dependent_count AS "dependentPackagesCount",
250-
p.dependent_repos_count AS "dependentReposCount",
251-
p.latest_version AS "latestVersion"
242+
p.latest_version AS "latestVersion"
252243
FROM packages p
253244
WHERE
254245
p.ecosystem = 'nuget'
@@ -267,6 +258,16 @@ export async function listNuGetPackagesToSync(
267258
)
268259
}
269260

261+
// ─── Error outcome ────────────────────────────────────────────────────────────
262+
263+
export async function markNuGetPackageError(qx: QueryExecutor, purl: string): Promise<void> {
264+
await qx.result(
265+
`UPDATE packages SET ingestion_source = 'nuget_error', last_synced_at = NOW()
266+
WHERE purl = $(purl)`,
267+
{ purl },
268+
)
269+
}
270+
270271
// ─── Download snapshot ────────────────────────────────────────────────────────
271272

272273
export async function recordNuGetDownloadSnapshot(

0 commit comments

Comments
 (0)