Skip to content

Commit ccfff3a

Browse files
committed
fix(manifest): judge a lenient JVM failure per project
Failures were merged by coordinate across projects before checking whether the module resolved, so one project that resolved it kept the failure lenient for another project whose SBOM lacked it. Blocking is now decided per failure, and failures aggregate by coordinate, category and that decision.
1 parent 46f530c commit ccfff3a

2 files changed

Lines changed: 32 additions & 19 deletions

File tree

‎src/commands/manifest/scripts/resolution-report-render.mts‎

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -218,28 +218,41 @@ export function renderResolutionReport(
218218
const isBlocking = (cat: FailureCategory): boolean =>
219219
specOf.get(cat)?.blocking ?? true
220220

221-
// Aggregate by (coord, category): one module can fail with different causes
222-
// across configs. Keep first-seen detail, union the configs and projects.
221+
// A non-blocking kind is only benign when its module resolved anyway: one
222+
// that resolved in no configuration of its project is missing from that
223+
// project's SBOM.
224+
const resolved = opts.resolvedModules
225+
const isFailureBlocking = (
226+
category: FailureCategory,
227+
f: ResolutionFailure,
228+
): boolean =>
229+
isBlocking(category) ||
230+
(!!resolved && !resolved.has(moduleKey(f.project, f.coord)))
231+
232+
// Aggregate by (coord, category, blocking): one module can fail with
233+
// different causes across configs, and block in only some projects. Keep
234+
// first-seen detail, union the configs and projects.
223235
type CoordInfo = {
224236
coord: string
225237
category: FailureCategory
238+
blocking: boolean
226239
detail: string
227240
configs: Set<string>
228241
projects: Set<string>
229242
// Some failure of this coord belongs to no configuration.
230243
configless: boolean
231244
}
232245
const byKey = new Map<string, CoordInfo>()
233-
const keyOf = (coord: string, category: FailureCategory): string =>
234-
`${coord} ${category}`
235246
for (const f of failures) {
236247
const category = dialect.classify(f.detail)
237-
const key = keyOf(f.coord, category)
248+
const blocking = isFailureBlocking(category, f)
249+
const key = `${f.coord} ${category} ${blocking}`
238250
let info = byKey.get(key)
239251
if (!info) {
240252
info = {
241253
coord: f.coord,
242254
category,
255+
blocking,
243256
detail: f.detail,
244257
configs: new Set(),
245258
projects: new Set(),
@@ -257,20 +270,7 @@ export function renderResolutionReport(
257270
}
258271
}
259272
const allInfos = [...byKey.values()]
260-
// A non-blocking kind is only benign when its module resolved anyway: one
261-
// that resolved in no configuration of its project is missing from the SBOM.
262-
const resolved = opts.resolvedModules
263-
const blockingInfos = new Set(
264-
allInfos.filter(
265-
info =>
266-
isBlocking(info.category) ||
267-
(!!resolved &&
268-
![...info.projects].some(p =>
269-
resolved.has(moduleKey(p, info.coord)),
270-
)),
271-
),
272-
)
273-
const isInfoBlocking = (info: CoordInfo): boolean => blockingInfos.has(info)
273+
const isInfoBlocking = (info: CoordInfo): boolean => info.blocking
274274

275275
// A whole-config throw is classified by the same cause rules as a per-dep
276276
// failure: ambiguity stays lenient, every other cause is fail-closed.

‎src/commands/manifest/scripts/resolution-report-render.test.mts‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,19 @@ describe('failures checked against what resolved', () => {
424424
expect(r.nonBlockingNotice).toBe('')
425425
})
426426

427+
it('blocks on variant ambiguity in the project where the module resolved nowhere', () => {
428+
const r = renderResolutionErrorReport(
429+
[ambiguous, { ...ambiguous, project: ':lib' }],
430+
['runtimeClasspath'],
431+
'gradle',
432+
{ resolvedModules: new Set([':app\tcom.example:amb']) },
433+
)
434+
expect(r.hasBlockingFailures).toBe(true)
435+
expect(r.summary).toContain(' - com.example:amb:1.0 (in `:lib`)')
436+
expect(r.summary).not.toContain('`:app`')
437+
expect(r.nonBlockingNotice).toContain('ambiguous variant selection')
438+
})
439+
427440
it('keeps a bare module failure from another project than its versioned form', () => {
428441
const r = renderResolutionErrorReport(
429442
[

0 commit comments

Comments
 (0)