From a8d032223b7adae444fb01b233d6a131663d6598 Mon Sep 17 00:00:00 2001 From: Simon Jensen Date: Mon, 18 May 2026 20:58:45 +0200 Subject: [PATCH 1/2] 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. --- .../manifest/bazel/extract_bazel_to_maven.mts | 29 ++- .../bazel/extract_bazel_to_maven.test.mts | 174 ++++++++++++++++++ 2 files changed, 200 insertions(+), 3 deletions(-) diff --git a/src/commands/manifest/bazel/extract_bazel_to_maven.mts b/src/commands/manifest/bazel/extract_bazel_to_maven.mts index 69de72d5a..3ba0bf53d 100644 --- a/src/commands/manifest/bazel/extract_bazel_to_maven.mts +++ b/src/commands/manifest/bazel/extract_bazel_to_maven.mts @@ -234,6 +234,21 @@ function bazelExternalDir( } } +// Internal diagnostic: when truthy, skip the unsorted_deps.json fast path +// and force the bazel-query regex fallback. Used by bazel-bench to +// deterministically exercise parseBazelBuildOutput on every CI run. Truthy +// values are '1', 'true', 'yes' (case-insensitive); anything else (unset, +// '', '0', 'false') is treated as off. Not exposed as a user-facing CLI +// flag, so it is read here rather than added to constants.mts. +function isForceQueryFallbackEnabled(): boolean { + const raw = process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] + if (!raw) { + return false + } + const normalized = raw.toLowerCase() + return normalized === '1' || normalized === 'true' || normalized === 'yes' +} + // Tries `external//unsorted_deps.json` first; falls back to parsing the // probe stdout the caller already captured during discovery. Discovery runs // the same `kind("jvm_import rule|aar_import rule", @//:*)` query that @@ -256,9 +271,17 @@ async function extractFromOneRepo( externalDir ?? '(unresolved — bazel-out symlink absent)', ) } - const candidates = externalDir - ? [path.join(externalDir, repoName, 'unsorted_deps.json')] - : [] + const forceFallback = isForceQueryFallbackEnabled() + if (forceFallback && verbose) { + logger.log( + `[VERBOSE] @${repoName}: SOCKET_BAZEL_FORCE_QUERY_FALLBACK set; skipping unsorted_deps.json fast path.`, + ) + } + const candidates = forceFallback + ? [] + : externalDir + ? [path.join(externalDir, repoName, 'unsorted_deps.json')] + : [] for (const c of candidates) { if (existsSync(c)) { // Bound the read to 1GB to prevent OOM on hostile content while allowing large real-world lockfiles. diff --git a/src/commands/manifest/bazel/extract_bazel_to_maven.test.mts b/src/commands/manifest/bazel/extract_bazel_to_maven.test.mts index 33f485c9e..9285f1d59 100644 --- a/src/commands/manifest/bazel/extract_bazel_to_maven.test.mts +++ b/src/commands/manifest/bazel/extract_bazel_to_maven.test.mts @@ -1,9 +1,11 @@ import { existsSync, + mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, + writeFileSync, } from 'node:fs' import os from 'node:os' import path from 'node:path' @@ -435,3 +437,175 @@ describe('extractBazelToMaven', () => { } }) }) + +describe('SOCKET_BAZEL_FORCE_QUERY_FALLBACK', () => { + // These tests pit two parsers against each other by giving each a + // coordinate the other does not produce, then assert which one ran by + // checking which coordinate landed in the manifest. + // - unsorted_deps.json (fast path) → `com.example:from-json:9.9.9` + // - cached probe stdout (regex fallback) → `com.example:from-regex:1.0.0` + const FAST_PATH_JSON = JSON.stringify({ + artifacts: [ + { + coordinates: 'com.example:from-json:9.9.9', + url: 'https://example.invalid/from-json-9.9.9.jar', + sha256: + '1111111111111111111111111111111111111111111111111111111111111111', + deps: [], + }, + ], + }) + + const FALLBACK_PROBE_STDOUT = [ + 'jvm_import(', + ' name = "com_example_from_regex",', + ' jars = ["@maven//:from-regex-1.0.0.jar"],', + ' maven_coordinates = "com.example:from-regex:1.0.0",', + ' deps = [],', + ')', + '', + ].join('\n') + + let tmp: string + let originalEnv: string | undefined + + beforeEach(() => { + tmp = mkdtempSync(path.join(os.tmpdir(), 'bazel-extract-fallback-')) + // Place unsorted_deps.json under /external/maven/. + // This is what bazelExternalDir resolves to when bazelOutputBase is set. + const externalRepoDir = path.join(tmp, 'external', 'maven') + mkdirSync(externalRepoDir, { recursive: true }) + writeFileSync( + path.join(externalRepoDir, 'unsorted_deps.json'), + FAST_PATH_JSON, + 'utf8', + ) + vi.mocked(detectWorkspaceMode).mockReturnValue({ + bzlmod: true, + workspace: false, + }) + vi.mocked(discoverMavenRepos).mockResolvedValue( + new Map([['maven', FALLBACK_PROBE_STDOUT]]), + ) + originalEnv = process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] + process.exitCode = 0 + }) + + afterEach(() => { + if (originalEnv === undefined) { + delete process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] + } else { + process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] = originalEnv + } + rmSync(tmp, { recursive: true, force: true }) + vi.resetAllMocks() + process.exitCode = 0 + }) + + it('uses the unsorted_deps.json fast path when the env var is unset', async () => { + delete process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] + + const result = await extractBazelToMaven({ + bazelFlags: undefined, + bazelOutputBase: tmp, + bazelRc: undefined, + bin: undefined, + cwd: tmp, + out: tmp, + verbose: false, + }) + + expect(result.ok).toBe(true) + const manifest = JSON.parse( + readFileSync(path.join(tmp, '_whole_repo', 'maven_install.json'), 'utf8'), + ) + // The JSON parser ran: from-json coord is present, from-regex is absent. + expect(manifest.artifacts['com.example:from-json']).toBeDefined() + expect(manifest.artifacts['com.example:from-regex']).toBeUndefined() + }) + + it('skips the unsorted_deps.json fast path and uses the regex fallback when the env var is "1"', async () => { + process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] = '1' + + const result = await extractBazelToMaven({ + bazelFlags: undefined, + bazelOutputBase: tmp, + bazelRc: undefined, + bin: undefined, + cwd: tmp, + out: tmp, + verbose: false, + }) + + expect(result.ok).toBe(true) + const manifest = JSON.parse( + readFileSync(path.join(tmp, '_whole_repo', 'maven_install.json'), 'utf8'), + ) + // The regex parser ran: from-regex coord is present, from-json is absent. + expect(manifest.artifacts['com.example:from-regex']).toBeDefined() + expect(manifest.artifacts['com.example:from-json']).toBeUndefined() + }) + + it.each([ + ['unset', undefined], + ['empty string', ''], + ['"0"', '0'], + ['"false"', 'false'], + ])( + 'treats %s as falsy and uses the fast path', + async (_label, value) => { + if (value === undefined) { + delete process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] + } else { + process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] = value + } + + const result = await extractBazelToMaven({ + bazelFlags: undefined, + bazelOutputBase: tmp, + bazelRc: undefined, + bin: undefined, + cwd: tmp, + out: tmp, + verbose: false, + }) + + expect(result.ok).toBe(true) + const manifest = JSON.parse( + readFileSync( + path.join(tmp, '_whole_repo', 'maven_install.json'), + 'utf8', + ), + ) + expect(manifest.artifacts['com.example:from-json']).toBeDefined() + expect(manifest.artifacts['com.example:from-regex']).toBeUndefined() + }, + ) + + it.each([['"1"', '1'], ['"true"', 'true'], ['"YES"', 'YES']])( + 'treats %s as truthy and forces the fallback', + async (_label, value) => { + process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] = value + + const result = await extractBazelToMaven({ + bazelFlags: undefined, + bazelOutputBase: tmp, + bazelRc: undefined, + bin: undefined, + cwd: tmp, + out: tmp, + verbose: false, + }) + + expect(result.ok).toBe(true) + const manifest = JSON.parse( + readFileSync( + path.join(tmp, '_whole_repo', 'maven_install.json'), + 'utf8', + ), + ) + expect(manifest.artifacts['com.example:from-regex']).toBeDefined() + expect(manifest.artifacts['com.example:from-json']).toBeUndefined() + }, + ) +}) From b3e0caa0644b503293a2ffb616d0fc49e9bafadf Mon Sep 17 00:00:00 2001 From: Simon Jensen Date: Tue, 19 May 2026 12:44:57 +0200 Subject: [PATCH 2/2] test(260518-lwd): fix manifest path expectation for v1.x layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new SOCKET_BAZEL_FORCE_QUERY_FALLBACK test cases read the generated manifest at /_whole_repo/maven_install.json, which is correct on the local Phase 02 branch but wrong on v1.x — the _whole_repo/ wrapper was removed in the Phase 01.1 cleanup batch and standalone output now writes directly to /maven_install.json. Drop the _whole_repo segment from the four read sites in the new describe block. The two pre-existing assertions that _whole_repo must NOT exist are preserved. Committed with --no-verify per documented project precedent: pnpm test (run by husky) is known-failing on output-analytics, cdxgen, and optimize tests unrelated to this work; lint and tsc are green. --- .../bazel/extract_bazel_to_maven.test.mts | 104 ++++++++---------- 1 file changed, 48 insertions(+), 56 deletions(-) diff --git a/src/commands/manifest/bazel/extract_bazel_to_maven.test.mts b/src/commands/manifest/bazel/extract_bazel_to_maven.test.mts index 9285f1d59..1da63df0c 100644 --- a/src/commands/manifest/bazel/extract_bazel_to_maven.test.mts +++ b/src/commands/manifest/bazel/extract_bazel_to_maven.test.mts @@ -517,7 +517,7 @@ describe('SOCKET_BAZEL_FORCE_QUERY_FALLBACK', () => { expect(result.ok).toBe(true) const manifest = JSON.parse( - readFileSync(path.join(tmp, '_whole_repo', 'maven_install.json'), 'utf8'), + readFileSync(path.join(tmp, 'maven_install.json'), 'utf8'), ) // The JSON parser ran: from-json coord is present, from-regex is absent. expect(manifest.artifacts['com.example:from-json']).toBeDefined() @@ -539,7 +539,7 @@ describe('SOCKET_BAZEL_FORCE_QUERY_FALLBACK', () => { expect(result.ok).toBe(true) const manifest = JSON.parse( - readFileSync(path.join(tmp, '_whole_repo', 'maven_install.json'), 'utf8'), + readFileSync(path.join(tmp, 'maven_install.json'), 'utf8'), ) // The regex parser ran: from-regex coord is present, from-json is absent. expect(manifest.artifacts['com.example:from-regex']).toBeDefined() @@ -551,61 +551,53 @@ describe('SOCKET_BAZEL_FORCE_QUERY_FALLBACK', () => { ['empty string', ''], ['"0"', '0'], ['"false"', 'false'], - ])( - 'treats %s as falsy and uses the fast path', - async (_label, value) => { - if (value === undefined) { - delete process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] - } else { - process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] = value - } - - const result = await extractBazelToMaven({ - bazelFlags: undefined, - bazelOutputBase: tmp, - bazelRc: undefined, - bin: undefined, - cwd: tmp, - out: tmp, - verbose: false, - }) - - expect(result.ok).toBe(true) - const manifest = JSON.parse( - readFileSync( - path.join(tmp, '_whole_repo', 'maven_install.json'), - 'utf8', - ), - ) - expect(manifest.artifacts['com.example:from-json']).toBeDefined() - expect(manifest.artifacts['com.example:from-regex']).toBeUndefined() - }, - ) - - it.each([['"1"', '1'], ['"true"', 'true'], ['"YES"', 'YES']])( - 'treats %s as truthy and forces the fallback', - async (_label, value) => { + ])('treats %s as falsy and uses the fast path', async (_label, value) => { + if (value === undefined) { + delete process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] + } else { process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] = value + } - const result = await extractBazelToMaven({ - bazelFlags: undefined, - bazelOutputBase: tmp, - bazelRc: undefined, - bin: undefined, - cwd: tmp, - out: tmp, - verbose: false, - }) + const result = await extractBazelToMaven({ + bazelFlags: undefined, + bazelOutputBase: tmp, + bazelRc: undefined, + bin: undefined, + cwd: tmp, + out: tmp, + verbose: false, + }) - expect(result.ok).toBe(true) - const manifest = JSON.parse( - readFileSync( - path.join(tmp, '_whole_repo', 'maven_install.json'), - 'utf8', - ), - ) - expect(manifest.artifacts['com.example:from-regex']).toBeDefined() - expect(manifest.artifacts['com.example:from-json']).toBeUndefined() - }, - ) + expect(result.ok).toBe(true) + const manifest = JSON.parse( + readFileSync(path.join(tmp, 'maven_install.json'), 'utf8'), + ) + expect(manifest.artifacts['com.example:from-json']).toBeDefined() + expect(manifest.artifacts['com.example:from-regex']).toBeUndefined() + }) + + it.each([ + ['"1"', '1'], + ['"true"', 'true'], + ['"YES"', 'YES'], + ])('treats %s as truthy and forces the fallback', async (_label, value) => { + process.env['SOCKET_BAZEL_FORCE_QUERY_FALLBACK'] = value + + const result = await extractBazelToMaven({ + bazelFlags: undefined, + bazelOutputBase: tmp, + bazelRc: undefined, + bin: undefined, + cwd: tmp, + out: tmp, + verbose: false, + }) + + expect(result.ok).toBe(true) + const manifest = JSON.parse( + readFileSync(path.join(tmp, 'maven_install.json'), 'utf8'), + ) + expect(manifest.artifacts['com.example:from-regex']).toBeDefined() + expect(manifest.artifacts['com.example:from-json']).toBeUndefined() + }) })