Skip to content

Commit b0812f9

Browse files
committed
feat: pom extractor
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
1 parent b49de68 commit b0812f9

6 files changed

Lines changed: 46 additions & 33 deletions

File tree

scripts/cli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1060,7 +1060,7 @@ while test $# -gt 0; do
10601060
exit
10611061
;;
10621062
clean-start-dev)
1063-
# IGNORED_SERVICES=("python-worker" "job-generator" "discord-ws" "webhook-api" "profiles-worker" "organizations-enrichment-worker" "merge-suggestions-worker" "members-enrichment-worker" "exports-worker" "entity-merging-worker")
1063+
IGNORED_SERVICES=("python-worker" "job-generator" "discord-ws" "webhook-api" "profiles-worker" "organizations-enrichment-worker" "merge-suggestions-worker" "members-enrichment-worker" "exports-worker" "entity-merging-worker")
10641064
CLEAN_START=1
10651065
DEV=1
10661066
start

scripts/services/packages-worker.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ x-env-args: &env-args
66
SERVICE: packages-worker
77
SHELL: /bin/sh
88
SUPPRESS_NO_CONFIG_WARNING: 'true'
9+
POM_FETCHER_BATCH_SIZE: '50'
10+
POM_FETCHER_CONCURRENCY: '3'
11+
POM_FETCHER_STALE_DAYS: '7'
12+
POM_FETCHER_IDLE_SLEEP_SEC: '3600'
913

1014
services:
1115
packages-worker:

scripts/services/pom-fetcher.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ x-env-args: &env-args
66
SERVICE: pom-fetcher
77
SHELL: /bin/sh
88
SUPPRESS_NO_CONFIG_WARNING: 'true'
9-
POM_FETCHER_BATCH_SIZE: '200'
10-
POM_FETCHER_CONCURRENCY: '10'
9+
LOG_LEVEL: 'info'
10+
POM_FETCHER_BATCH_SIZE: '50'
11+
POM_FETCHER_CONCURRENCY: '3'
1112
POM_FETCHER_STALE_DAYS: '7'
1213
POM_FETCHER_IDLE_SLEEP_SEC: '3600'
1314

services/apps/packages_worker/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
"start:pom-fetcher": "SERVICE=pom-fetcher tsx src/bin/pom-fetcher.ts",
88
"dev:packages-worker": "SERVICE=packages-worker LOG_LEVEL=trace nodemon --watch src --watch ../../libs --ext ts --exec tsx --inspect=0.0.0.0:9233 src/bin/packages-worker.ts",
99
"dev:github-repos-enricher": "SERVICE=github-repos-enricher LOG_LEVEL=trace nodemon --watch src --watch ../../libs --ext ts --exec tsx --inspect=0.0.0.0:9234 src/bin/github-repos-enricher.ts",
10-
"dev:pom-fetcher": "SERVICE=pom-fetcher LOG_LEVEL=trace nodemon --watch src --watch ../../libs --ext ts --exec tsx --inspect=0.0.0.0:9235 src/bin/pom-fetcher.ts",
10+
"dev:pom-fetcher": "SERVICE=pom-fetcher LOG_LEVEL=info nodemon --watch src --watch ../../libs --ext ts --exec tsx --inspect=0.0.0.0:9235 src/bin/pom-fetcher.ts",
1111
"dev:packages-worker:local": "set -a && . ../../../backend/.env.dist.local && . ../../../backend/.env.override.local && set +a && SERVICE=packages-worker LOG_LEVEL=trace nodemon --watch src --watch ../../libs --ext ts --exec tsx --inspect=0.0.0.0:9233 src/bin/packages-worker.ts",
1212
"dev:github-repos-enricher:local": "set -a && . ../../../backend/.env.dist.local && . ../../../backend/.env.override.local && set +a && SERVICE=github-repos-enricher LOG_LEVEL=trace nodemon --watch src --watch ../../libs --ext ts --exec tsx --inspect=0.0.0.0:9234 src/bin/github-repos-enricher.ts",
13-
"dev:pom-fetcher:local": "set -a && . ../../../backend/.env.dist.local && . ../../../backend/.env.override.local && set +a && SERVICE=pom-fetcher LOG_LEVEL=trace nodemon --watch src --watch ../../libs --ext ts --exec tsx --inspect=0.0.0.0:9235 src/bin/pom-fetcher.ts",
13+
"dev:pom-fetcher:local": "set -a && . ../../../backend/.env.dist.local && . ../../../backend/.env.override.local && set +a && SERVICE=pom-fetcher LOG_LEVEL=info nodemon --watch src --watch ../../libs --ext ts --exec tsx --inspect=0.0.0.0:9235 src/bin/pom-fetcher.ts",
1414
"lint": "npx eslint --ext .ts src --max-warnings=0",
1515
"format": "npx prettier --write \"src/**/*.ts\"",
1616
"format-check": "npx prettier --check .",

services/apps/packages_worker/src/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ export function getEnricherConfig() {
3232

3333
export function getPomFetcherConfig() {
3434
return {
35-
batchSize: parseInt(process.env.POM_FETCHER_BATCH_SIZE ?? '200', 10),
36-
concurrency: parseInt(process.env.POM_FETCHER_CONCURRENCY ?? '10', 10),
35+
batchSize: parseInt(process.env.POM_FETCHER_BATCH_SIZE ?? '50', 10),
36+
concurrency: parseInt(process.env.POM_FETCHER_CONCURRENCY ?? '3', 10),
3737
staleDays: parseInt(process.env.POM_FETCHER_STALE_DAYS ?? '7', 10),
3838
idleSleepSec: parseInt(process.env.POM_FETCHER_IDLE_SLEEP_SEC ?? '3600', 10),
3939
}

services/apps/packages_worker/src/pom-fetcher/runPomEnrichmentLoop.ts

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,24 @@ interface BatchResult {
2727

2828
async function processBatch(
2929
qx: QueryExecutor,
30-
offset: number,
3130
config: ReturnType<typeof getPomFetcherConfig>,
3231
): Promise<BatchResult> {
3332
const packages = await listMavenPackagesToEnrich(qx, {
3433
limit: config.batchSize,
35-
offset,
34+
offset: 0,
3635
staleDays: config.staleDays,
3736
})
3837

3938
if (packages.length === 0) {
4039
return { processed: 0, skipped: 0, errors: 0 }
4140
}
4241

43-
log.info({ offset, count: packages.length }, 'Processing POM batch...')
42+
log.info({ count: packages.length }, 'Processing POM batch...')
4443

4544
let processed = 0
4645
let skipped = 0
4746
let errors = 0
47+
const PROGRESS_EVERY = 25
4848

4949
// Process in small concurrent groups to be polite to Maven Central
5050
for (let i = 0; i < packages.length; i += config.concurrency) {
@@ -62,16 +62,30 @@ async function processBatch(
6262
}
6363

6464
try {
65-
log.info({ groupId, artifactId }, 'Fetching POM...')
66-
6765
// Step 1: resolve latest version from maven-metadata.xml
6866
const version = await resolveLatestVersion(groupId, artifactId)
6967
if (!version) {
7068
log.warn({ groupId, artifactId }, 'Could not resolve latest version, skipping')
69+
// Upsert a minimal record so last_synced_at is set — prevents this package
70+
// from re-appearing in every batch within the same pass.
71+
// ingestionSource 'pom_fetcher_no_version' marks that it was tried but had no
72+
// resolvable version on Maven Central (404 on maven-metadata.xml).
73+
await upsertPackage(qx, {
74+
purl: `pkg:maven/${groupId}/${artifactId}`,
75+
ecosystem: 'maven',
76+
namespace: groupId,
77+
name: artifactId,
78+
description: null,
79+
homepage: null,
80+
declaredRepositoryUrl: null,
81+
licenses: null,
82+
licensesRaw: null,
83+
latestVersion: null,
84+
ingestionSource: 'pom_fetcher_no_version',
85+
})
7186
skipped++
7287
return
7388
}
74-
log.info({ groupId, artifactId, version }, 'Version resolved, extracting POM...')
7589

7690
// Step 2: fetch + resolve POM (follows parent chain)
7791
const result = await extractArtifact(groupId, artifactId, version, (msg) => {
@@ -83,19 +97,6 @@ async function processBatch(
8397
errors++
8498
return
8599
}
86-
log.info(
87-
{
88-
groupId,
89-
artifactId,
90-
version,
91-
licenses: result.licenses,
92-
scmUrl: result.scmUrl,
93-
developers: result.developers.length,
94-
contributors: result.contributors.length,
95-
parentHops: result.parentHops,
96-
},
97-
'POM extracted, upserting...',
98-
)
99100

100101
// Step 3: upsert into `packages`
101102
// purl at package level has no version (package-level identifier)
@@ -151,6 +152,17 @@ async function processBatch(
151152
}
152153
}),
153154
)
155+
156+
// done = packages processed so far (based on loop index, always accurate)
157+
const done = i + group.length
158+
const prevDone = i
159+
const crossedBoundary = Math.floor(done / PROGRESS_EVERY) > Math.floor(prevDone / PROGRESS_EVERY)
160+
if (crossedBoundary || done === packages.length) {
161+
log.info(
162+
{ done, total: packages.length, processed, skipped, errors },
163+
`Progress: ${done}/${packages.length}`,
164+
)
165+
}
154166
}
155167

156168
return { processed, skipped, errors }
@@ -170,21 +182,20 @@ export async function runPomEnrichmentLoop(
170182
config: ReturnType<typeof getPomFetcherConfig>,
171183
isShuttingDown: () => boolean,
172184
): Promise<void> {
173-
let offset = 0
174185
let totalProcessed = 0
175186
let totalSkipped = 0
176187
let totalErrors = 0
177188
let passNumber = 0
178189
let passStartedAt = Date.now()
179190

180191
while (!isShuttingDown()) {
181-
if (offset === 0) {
192+
if (totalProcessed + totalSkipped + totalErrors === 0) {
182193
passNumber++
183194
passStartedAt = Date.now()
184195
log.info({ pass: passNumber }, 'Starting pass')
185196
}
186197

187-
const result = await processBatch(qx, offset, config)
198+
const result = await processBatch(qx, config)
188199

189200
if (result.processed + result.skipped + result.errors === 0) {
190201
// Nothing left in this pass — log summary and sleep
@@ -200,21 +211,18 @@ export async function runPomEnrichmentLoop(
200211
`Pass complete. Sleeping ${config.idleSleepSec}s before next pass.`,
201212
)
202213
await new Promise((r) => setTimeout(r, config.idleSleepSec * 1000))
203-
offset = 0
204214
totalProcessed = 0
205215
totalSkipped = 0
206216
totalErrors = 0
207-
passStartedAt = Date.now()
208217
continue
209218
}
210219

211220
totalProcessed += result.processed
212221
totalSkipped += result.skipped
213222
totalErrors += result.errors
214-
offset += config.batchSize
215223

216224
log.info(
217-
{ offset, processed: result.processed, skipped: result.skipped, errors: result.errors },
225+
{ processed: result.processed, skipped: result.skipped, errors: result.errors, totalProcessed, totalSkipped, totalErrors },
218226
'Batch complete',
219227
)
220228
}

0 commit comments

Comments
 (0)