Skip to content

Commit a8d0322

Browse files
committed
feat(260518-lwd): SOCKET_BAZEL_FORCE_QUERY_FALLBACK env-var gate
When truthy (1/true/yes, case-insensitive), skip the unsorted_deps.json fast path and parse via the bazel-query regex fallback. Internal diagnostic toggle; not a user-facing CLI flag.
1 parent 1597740 commit a8d0322

2 files changed

Lines changed: 200 additions & 3 deletions

File tree

src/commands/manifest/bazel/extract_bazel_to_maven.mts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,21 @@ function bazelExternalDir(
234234
}
235235
}
236236

237+
// Internal diagnostic: when truthy, skip the unsorted_deps.json fast path
238+
// and force the bazel-query regex fallback. Used by bazel-bench to
239+
// deterministically exercise parseBazelBuildOutput on every CI run. Truthy
240+
// values are '1', 'true', 'yes' (case-insensitive); anything else (unset,
241+
// '', '0', 'false') is treated as off. Not exposed as a user-facing CLI
242+
// flag, so it is read here rather than added to constants.mts.
243+
function isForceQueryFallbackEnabled(): boolean {
244+
const raw = process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK']
245+
if (!raw) {
246+
return false
247+
}
248+
const normalized = raw.toLowerCase()
249+
return normalized === '1' || normalized === 'true' || normalized === 'yes'
250+
}
251+
237252
// Tries `external/<repo>/unsorted_deps.json` first; falls back to parsing the
238253
// probe stdout the caller already captured during discovery. Discovery runs
239254
// the same `kind("jvm_import rule|aar_import rule", @<repo>//:*)` query that
@@ -256,9 +271,17 @@ async function extractFromOneRepo(
256271
externalDir ?? '(unresolved — bazel-out symlink absent)',
257272
)
258273
}
259-
const candidates = externalDir
260-
? [path.join(externalDir, repoName, 'unsorted_deps.json')]
261-
: []
274+
const forceFallback = isForceQueryFallbackEnabled()
275+
if (forceFallback && verbose) {
276+
logger.log(
277+
`[VERBOSE] @${repoName}: SOCKET_BAZEL_FORCE_QUERY_FALLBACK set; skipping unsorted_deps.json fast path.`,
278+
)
279+
}
280+
const candidates = forceFallback
281+
? []
282+
: externalDir
283+
? [path.join(externalDir, repoName, 'unsorted_deps.json')]
284+
: []
262285
for (const c of candidates) {
263286
if (existsSync(c)) {
264287
// Bound the read to 1GB to prevent OOM on hostile content while allowing large real-world lockfiles.

src/commands/manifest/bazel/extract_bazel_to_maven.test.mts

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import {
22
existsSync,
3+
mkdirSync,
34
mkdtempSync,
45
readFileSync,
56
readdirSync,
67
rmSync,
8+
writeFileSync,
79
} from 'node:fs'
810
import os from 'node:os'
911
import path from 'node:path'
@@ -435,3 +437,175 @@ describe('extractBazelToMaven', () => {
435437
}
436438
})
437439
})
440+
441+
describe('SOCKET_BAZEL_FORCE_QUERY_FALLBACK', () => {
442+
// These tests pit two parsers against each other by giving each a
443+
// coordinate the other does not produce, then assert which one ran by
444+
// checking which coordinate landed in the manifest.
445+
// - unsorted_deps.json (fast path) → `com.example:from-json:9.9.9`
446+
// - cached probe stdout (regex fallback) → `com.example:from-regex:1.0.0`
447+
const FAST_PATH_JSON = JSON.stringify({
448+
artifacts: [
449+
{
450+
coordinates: 'com.example:from-json:9.9.9',
451+
url: 'https://example.invalid/from-json-9.9.9.jar',
452+
sha256:
453+
'1111111111111111111111111111111111111111111111111111111111111111',
454+
deps: [],
455+
},
456+
],
457+
})
458+
459+
const FALLBACK_PROBE_STDOUT = [
460+
'jvm_import(',
461+
' name = "com_example_from_regex",',
462+
' jars = ["@maven//:from-regex-1.0.0.jar"],',
463+
' maven_coordinates = "com.example:from-regex:1.0.0",',
464+
' deps = [],',
465+
')',
466+
'',
467+
].join('\n')
468+
469+
let tmp: string
470+
let originalEnv: string | undefined
471+
472+
beforeEach(() => {
473+
tmp = mkdtempSync(path.join(os.tmpdir(), 'bazel-extract-fallback-'))
474+
// Place unsorted_deps.json under <bazelOutputBase>/external/maven/.
475+
// This is what bazelExternalDir resolves to when bazelOutputBase is set.
476+
const externalRepoDir = path.join(tmp, 'external', 'maven')
477+
mkdirSync(externalRepoDir, { recursive: true })
478+
writeFileSync(
479+
path.join(externalRepoDir, 'unsorted_deps.json'),
480+
FAST_PATH_JSON,
481+
'utf8',
482+
)
483+
vi.mocked(detectWorkspaceMode).mockReturnValue({
484+
bzlmod: true,
485+
workspace: false,
486+
})
487+
vi.mocked(discoverMavenRepos).mockResolvedValue(
488+
new Map([['maven', FALLBACK_PROBE_STDOUT]]),
489+
)
490+
originalEnv = process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK']
491+
process.exitCode = 0
492+
})
493+
494+
afterEach(() => {
495+
if (originalEnv === undefined) {
496+
delete process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK']
497+
} else {
498+
process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] = originalEnv
499+
}
500+
rmSync(tmp, { recursive: true, force: true })
501+
vi.resetAllMocks()
502+
process.exitCode = 0
503+
})
504+
505+
it('uses the unsorted_deps.json fast path when the env var is unset', async () => {
506+
delete process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK']
507+
508+
const result = await extractBazelToMaven({
509+
bazelFlags: undefined,
510+
bazelOutputBase: tmp,
511+
bazelRc: undefined,
512+
bin: undefined,
513+
cwd: tmp,
514+
out: tmp,
515+
verbose: false,
516+
})
517+
518+
expect(result.ok).toBe(true)
519+
const manifest = JSON.parse(
520+
readFileSync(path.join(tmp, '_whole_repo', 'maven_install.json'), 'utf8'),
521+
)
522+
// The JSON parser ran: from-json coord is present, from-regex is absent.
523+
expect(manifest.artifacts['com.example:from-json']).toBeDefined()
524+
expect(manifest.artifacts['com.example:from-regex']).toBeUndefined()
525+
})
526+
527+
it('skips the unsorted_deps.json fast path and uses the regex fallback when the env var is "1"', async () => {
528+
process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] = '1'
529+
530+
const result = await extractBazelToMaven({
531+
bazelFlags: undefined,
532+
bazelOutputBase: tmp,
533+
bazelRc: undefined,
534+
bin: undefined,
535+
cwd: tmp,
536+
out: tmp,
537+
verbose: false,
538+
})
539+
540+
expect(result.ok).toBe(true)
541+
const manifest = JSON.parse(
542+
readFileSync(path.join(tmp, '_whole_repo', 'maven_install.json'), 'utf8'),
543+
)
544+
// The regex parser ran: from-regex coord is present, from-json is absent.
545+
expect(manifest.artifacts['com.example:from-regex']).toBeDefined()
546+
expect(manifest.artifacts['com.example:from-json']).toBeUndefined()
547+
})
548+
549+
it.each([
550+
['unset', undefined],
551+
['empty string', ''],
552+
['"0"', '0'],
553+
['"false"', 'false'],
554+
])(
555+
'treats %s as falsy and uses the fast path',
556+
async (_label, value) => {
557+
if (value === undefined) {
558+
delete process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK']
559+
} else {
560+
process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] = value
561+
}
562+
563+
const result = await extractBazelToMaven({
564+
bazelFlags: undefined,
565+
bazelOutputBase: tmp,
566+
bazelRc: undefined,
567+
bin: undefined,
568+
cwd: tmp,
569+
out: tmp,
570+
verbose: false,
571+
})
572+
573+
expect(result.ok).toBe(true)
574+
const manifest = JSON.parse(
575+
readFileSync(
576+
path.join(tmp, '_whole_repo', 'maven_install.json'),
577+
'utf8',
578+
),
579+
)
580+
expect(manifest.artifacts['com.example:from-json']).toBeDefined()
581+
expect(manifest.artifacts['com.example:from-regex']).toBeUndefined()
582+
},
583+
)
584+
585+
it.each([['"1"', '1'], ['"true"', 'true'], ['"YES"', 'YES']])(
586+
'treats %s as truthy and forces the fallback',
587+
async (_label, value) => {
588+
process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] = value
589+
590+
const result = await extractBazelToMaven({
591+
bazelFlags: undefined,
592+
bazelOutputBase: tmp,
593+
bazelRc: undefined,
594+
bin: undefined,
595+
cwd: tmp,
596+
out: tmp,
597+
verbose: false,
598+
})
599+
600+
expect(result.ok).toBe(true)
601+
const manifest = JSON.parse(
602+
readFileSync(
603+
path.join(tmp, '_whole_repo', 'maven_install.json'),
604+
'utf8',
605+
),
606+
)
607+
expect(manifest.artifacts['com.example:from-regex']).toBeDefined()
608+
expect(manifest.artifacts['com.example:from-json']).toBeUndefined()
609+
},
610+
)
611+
})

0 commit comments

Comments
 (0)