@socketsecurity/cli-with-sentry 1.1.96 → 1.1.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/cli.js +1421 -63
- package/dist/cli.js.map +1 -1
- package/dist/constants.js +4 -4
- package/dist/constants.js.map +1 -1
- package/dist/tsconfig.dts.tsbuildinfo +1 -1
- package/dist/types/commands/manifest/bazel/bazel-bin-detect.d.mts +11 -0
- package/dist/types/commands/manifest/bazel/bazel-bin-detect.d.mts.map +1 -0
- package/dist/types/commands/manifest/bazel/bazel-build-parser.d.mts +34 -0
- package/dist/types/commands/manifest/bazel/bazel-build-parser.d.mts.map +1 -0
- package/dist/types/commands/manifest/bazel/bazel-java-shim.d.mts +10 -0
- package/dist/types/commands/manifest/bazel/bazel-java-shim.d.mts.map +1 -0
- package/dist/types/commands/manifest/bazel/bazel-output-base-check.d.mts +7 -0
- package/dist/types/commands/manifest/bazel/bazel-output-base-check.d.mts.map +1 -0
- package/dist/types/commands/manifest/bazel/bazel-python-shim.d.mts +9 -0
- package/dist/types/commands/manifest/bazel/bazel-python-shim.d.mts.map +1 -0
- package/dist/types/commands/manifest/bazel/bazel-query-runner.d.mts +41 -0
- package/dist/types/commands/manifest/bazel/bazel-query-runner.d.mts.map +1 -0
- package/dist/types/commands/manifest/bazel/bazel-repo-discovery.d.mts +34 -0
- package/dist/types/commands/manifest/bazel/bazel-repo-discovery.d.mts.map +1 -0
- package/dist/types/commands/manifest/bazel/bazel-workspace-detect.d.mts +13 -0
- package/dist/types/commands/manifest/bazel/bazel-workspace-detect.d.mts.map +1 -0
- package/dist/types/commands/manifest/bazel/cmd-manifest-bazel.d.mts +9 -0
- package/dist/types/commands/manifest/bazel/cmd-manifest-bazel.d.mts.map +1 -0
- package/dist/types/commands/manifest/bazel/extract_bazel_to_maven.d.mts +33 -0
- package/dist/types/commands/manifest/bazel/extract_bazel_to_maven.d.mts.map +1 -0
- package/dist/types/commands/manifest/cmd-manifest.d.mts.map +1 -1
- package/dist/types/commands/manifest/detect-manifest-actions.d.mts +1 -0
- package/dist/types/commands/manifest/detect-manifest-actions.d.mts.map +1 -1
- package/dist/types/commands/manifest/generate_auto_manifest.d.mts +4 -1
- package/dist/types/commands/manifest/generate_auto_manifest.d.mts.map +1 -1
- package/dist/types/commands/scan/handle-create-new-scan.d.mts.map +1 -1
- package/dist/types/utils/socket-json.d.mts +10 -0
- package/dist/types/utils/socket-json.d.mts.map +1 -1
- package/dist/utils.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -15,17 +15,18 @@ var words = require('../external/@socketsecurity/registry/lib/words');
|
|
|
15
15
|
var fs$1 = require('node:fs');
|
|
16
16
|
var arrays = require('../external/@socketsecurity/registry/lib/arrays');
|
|
17
17
|
var prompts = require('../external/@socketsecurity/registry/lib/prompts');
|
|
18
|
+
var bin = require('../external/@socketsecurity/registry/lib/bin');
|
|
19
|
+
var childProcess = require('node:child_process');
|
|
20
|
+
var os = require('node:os');
|
|
18
21
|
var spawn = require('../external/@socketsecurity/registry/lib/spawn');
|
|
19
22
|
var fs$2 = require('../external/@socketsecurity/registry/lib/fs');
|
|
20
23
|
var strings = require('../external/@socketsecurity/registry/lib/strings');
|
|
21
|
-
var os = require('node:os');
|
|
22
24
|
var path$1 = require('../external/@socketsecurity/registry/lib/path');
|
|
23
25
|
var require$$11 = require('../external/@socketsecurity/registry/lib/objects');
|
|
24
26
|
var registry = require('../external/@socketsecurity/registry');
|
|
25
27
|
var packages = require('../external/@socketsecurity/registry/lib/packages');
|
|
26
28
|
var require$$12 = require('../external/@socketsecurity/registry/lib/promises');
|
|
27
29
|
var regexps = require('../external/@socketsecurity/registry/lib/regexps');
|
|
28
|
-
var childProcess = require('node:child_process');
|
|
29
30
|
var require$$1 = require('node:util');
|
|
30
31
|
var promises = require('node:stream/promises');
|
|
31
32
|
|
|
@@ -331,9 +332,9 @@ const hidden$x = false;
|
|
|
331
332
|
const cmdAnalytics = {
|
|
332
333
|
description: description$F,
|
|
333
334
|
hidden: hidden$x,
|
|
334
|
-
run: run$
|
|
335
|
+
run: run$T
|
|
335
336
|
};
|
|
336
|
-
async function run$
|
|
337
|
+
async function run$T(argv, importMeta, {
|
|
337
338
|
parentName
|
|
338
339
|
}) {
|
|
339
340
|
const config = {
|
|
@@ -754,9 +755,9 @@ const hidden$w = false;
|
|
|
754
755
|
const cmdAuditLog = {
|
|
755
756
|
description: description$E,
|
|
756
757
|
hidden: hidden$w,
|
|
757
|
-
run: run$
|
|
758
|
+
run: run$S
|
|
758
759
|
};
|
|
759
|
-
async function run$
|
|
760
|
+
async function run$S(argv, importMeta, {
|
|
760
761
|
parentName
|
|
761
762
|
}) {
|
|
762
763
|
const config = {
|
|
@@ -1850,6 +1851,7 @@ async function detectManifestActions(
|
|
|
1850
1851
|
// regardless of local socket.json status. Sometimes we want that.
|
|
1851
1852
|
sockJson, cwd = process.cwd()) {
|
|
1852
1853
|
const output = {
|
|
1854
|
+
bazel: false,
|
|
1853
1855
|
cdxgen: false,
|
|
1854
1856
|
// TODO
|
|
1855
1857
|
count: 0,
|
|
@@ -1857,6 +1859,13 @@ sockJson, cwd = process.cwd()) {
|
|
|
1857
1859
|
gradle: false,
|
|
1858
1860
|
sbt: false
|
|
1859
1861
|
};
|
|
1862
|
+
if (sockJson?.defaults?.manifest?.bazel?.disabled) {
|
|
1863
|
+
require$$9.debugLog('notice', `[DEBUG] - bazel auto-detection is disabled in ${constants.SOCKET_JSON}`);
|
|
1864
|
+
} else if (fs$1.existsSync(path.join(cwd, 'MODULE.bazel')) || fs$1.existsSync(path.join(cwd, 'WORKSPACE')) || fs$1.existsSync(path.join(cwd, 'WORKSPACE.bazel'))) {
|
|
1865
|
+
require$$9.debugLog('notice', '[DEBUG] - Detected a Bazel workspace');
|
|
1866
|
+
output.bazel = true;
|
|
1867
|
+
output.count += 1;
|
|
1868
|
+
}
|
|
1860
1869
|
if (sockJson?.defaults?.manifest?.sbt?.disabled) {
|
|
1861
1870
|
require$$9.debugLog('notice', `[DEBUG] - sbt auto-detection is disabled in ${constants.SOCKET_JSON}`);
|
|
1862
1871
|
} else if (fs$1.existsSync(path.join(cwd, 'build.sbt'))) {
|
|
@@ -1884,7 +1893,1149 @@ sockJson, cwd = process.cwd()) {
|
|
|
1884
1893
|
output.count += 1;
|
|
1885
1894
|
}
|
|
1886
1895
|
}
|
|
1887
|
-
return output;
|
|
1896
|
+
return output;
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
/**
|
|
1900
|
+
* Resolve the bazel binary to invoke for `socket manifest bazel`.
|
|
1901
|
+
*
|
|
1902
|
+
* Resolution order:
|
|
1903
|
+
* 1. If `explicit` is provided, return it iff it exists on disk; else throw.
|
|
1904
|
+
* 2. Look up `bazelisk` on PATH (preferred — respects `.bazelversion`).
|
|
1905
|
+
* 3. Fall back to `bazel` on PATH.
|
|
1906
|
+
* 4. If neither is found, throw InputError with install instructions.
|
|
1907
|
+
*/
|
|
1908
|
+
async function resolveBazelBinary(explicit) {
|
|
1909
|
+
if (explicit) {
|
|
1910
|
+
if (!fs$1.existsSync(explicit)) {
|
|
1911
|
+
throw new utils.InputError(`--bazel path does not exist: ${explicit}. Install bazelisk or bazel, or pass an existing path via --bazel.`);
|
|
1912
|
+
}
|
|
1913
|
+
return explicit;
|
|
1914
|
+
}
|
|
1915
|
+
// Prefer bazelisk: respects .bazelversion in the workspace.
|
|
1916
|
+
const bazelisk = await bin.whichBin('bazelisk', {
|
|
1917
|
+
nothrow: true
|
|
1918
|
+
});
|
|
1919
|
+
if (bazelisk) {
|
|
1920
|
+
return bazelisk;
|
|
1921
|
+
}
|
|
1922
|
+
const bazel = await bin.whichBin('bazel', {
|
|
1923
|
+
nothrow: true
|
|
1924
|
+
});
|
|
1925
|
+
if (bazel) {
|
|
1926
|
+
return bazel;
|
|
1927
|
+
}
|
|
1928
|
+
throw new utils.InputError('Could not find bazelisk or bazel on PATH. ' + 'Install bazelisk (recommended; https://github.com/bazelbuild/bazelisk) ' + 'or bazel, or pass --bazel <path>.');
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
/**
|
|
1932
|
+
* Parse `bazel query --output=build` text and `unsorted_deps.json` files
|
|
1933
|
+
* (rules_jvm_external) into a uniform `ExtractedArtifact` shape consumed by
|
|
1934
|
+
* the converter.
|
|
1935
|
+
*
|
|
1936
|
+
* Security gate: every regex uses bounded character classes to prevent
|
|
1937
|
+
* catastrophic backtracking on hostile bazel-query output. Rules without
|
|
1938
|
+
* `maven_coordinates=` are skipped. Caller is responsible for size-capping
|
|
1939
|
+
* the input string.
|
|
1940
|
+
*/
|
|
1941
|
+
|
|
1942
|
+
// Per-rule block matcher: matches `<kind>(...)` where kind is jvm_import or
|
|
1943
|
+
// aar_import, bounded by `^)` (closing paren on its own line) — Bazel
|
|
1944
|
+
// `--output=build` output convention. Body length capped at 8 KiB; real
|
|
1945
|
+
// rules are ~500 bytes, so the cap is 16x normal. Prevents pathological
|
|
1946
|
+
// backtracking on hostile input.
|
|
1947
|
+
const RULE_RE = /^(jvm_import|aar_import)\(([\s\S]{0,8192}?)^\)/gm;
|
|
1948
|
+
|
|
1949
|
+
// Cache for per-attribute regexes — avoids recompiling the same pattern on
|
|
1950
|
+
// every rule block. Keyed by attr name; all attr names are safe alphanumeric
|
|
1951
|
+
// identifiers so no escaping is needed beyond the bounded character class.
|
|
1952
|
+
const ATTR_RE_CACHE = new Map();
|
|
1953
|
+
|
|
1954
|
+
// Cache for per-tag-key regexes used by extractTagValue.
|
|
1955
|
+
const TAG_RE_CACHE = new Map();
|
|
1956
|
+
function extractAttr(body, attr) {
|
|
1957
|
+
// Match `<attr> = "VALUE"` — quoted-string attrs only.
|
|
1958
|
+
// Quoted value capped at 4 KiB; canonical Maven URLs are ~150 bytes.
|
|
1959
|
+
let re = ATTR_RE_CACHE.get(attr);
|
|
1960
|
+
if (!re) {
|
|
1961
|
+
re = new RegExp(`\\b${attr}\\s*=\\s*"([^"\\n]{0,4096})"`);
|
|
1962
|
+
ATTR_RE_CACHE.set(attr, re);
|
|
1963
|
+
}
|
|
1964
|
+
const m = re.exec(body);
|
|
1965
|
+
return m?.[1];
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
// Extracts a `key=value` pair from inside a Bazel `tags = [...]` attribute
|
|
1969
|
+
// (rules_jvm_external encodes maven_sha256, maven_coordinates etc. this way).
|
|
1970
|
+
// Pattern: `"maven_sha256=<hex>"` inside the tags list.
|
|
1971
|
+
// Returns undefined when the tag is absent or malformed.
|
|
1972
|
+
function extractTagValue(body, tagKey) {
|
|
1973
|
+
// Match the full tags = [...] block (bounded at 8 KiB).
|
|
1974
|
+
const tagsM = /\btags\s*=\s*\[([\s\S]{0,8192}?)\]/m.exec(body);
|
|
1975
|
+
if (!tagsM) {
|
|
1976
|
+
return undefined;
|
|
1977
|
+
}
|
|
1978
|
+
const tagsBlob = tagsM[1];
|
|
1979
|
+
// Within the blob, look for "<tagKey>=<value>" inside a quoted string.
|
|
1980
|
+
// Bounded at 512 bytes per tag entry (sha256 hex is 64 chars; URLs ~150).
|
|
1981
|
+
let tagRe = TAG_RE_CACHE.get(tagKey);
|
|
1982
|
+
if (!tagRe) {
|
|
1983
|
+
tagRe = new RegExp(`"${tagKey}=([^"\\n]{0,512})"`);
|
|
1984
|
+
TAG_RE_CACHE.set(tagKey, tagRe);
|
|
1985
|
+
}
|
|
1986
|
+
const m = tagRe.exec(tagsBlob);
|
|
1987
|
+
return m?.[1];
|
|
1988
|
+
}
|
|
1989
|
+
function extractDeps(body) {
|
|
1990
|
+
// Match `deps = ["a", "b", ...]`. Body length capped at 16 KiB; real
|
|
1991
|
+
// dep lists are <2 KiB.
|
|
1992
|
+
const m = /\bdeps\s*=\s*\[([\s\S]{0,16384}?)\]/m.exec(body);
|
|
1993
|
+
if (!m) {
|
|
1994
|
+
return [];
|
|
1995
|
+
}
|
|
1996
|
+
const out = [];
|
|
1997
|
+
// Per-label cap at 512 bytes; real Bazel labels are <100 bytes.
|
|
1998
|
+
for (const q of m[1].matchAll(/"([^"\n]{0,512})"/g)) {
|
|
1999
|
+
out.push(q[1]);
|
|
2000
|
+
}
|
|
2001
|
+
return out;
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
/**
|
|
2005
|
+
* Parse `bazel query --output=build` stdout into `ExtractedArtifact[]`.
|
|
2006
|
+
* Skips rules without a `maven_coordinates` attribute (those aren't
|
|
2007
|
+
* rules_jvm_external lockfile rules).
|
|
2008
|
+
*/
|
|
2009
|
+
function parseBazelBuildOutput(text) {
|
|
2010
|
+
const results = [];
|
|
2011
|
+
for (const m of text.matchAll(RULE_RE)) {
|
|
2012
|
+
const ruleKind = m[1];
|
|
2013
|
+
const body = m[2];
|
|
2014
|
+
const ruleName = extractAttr(body, 'name');
|
|
2015
|
+
// maven_coordinates can be:
|
|
2016
|
+
// (a) a top-level rule attribute: `maven_coordinates = "g:a:v"` (newer rje)
|
|
2017
|
+
// (b) inside tags = [...]: `"maven_coordinates=g:a:v"` (older rje, e.g. ray)
|
|
2018
|
+
const coords = extractAttr(body, 'maven_coordinates') ?? extractTagValue(body, 'maven_coordinates');
|
|
2019
|
+
if (!ruleName || !coords) {
|
|
2020
|
+
continue;
|
|
2021
|
+
}
|
|
2022
|
+
// maven_sha256 is encoded inside tags = [...] as "maven_sha256=<hex>" by
|
|
2023
|
+
// rules_jvm_external; try tags first, fall back to standalone attr for
|
|
2024
|
+
// older rule shapes that may declare it as a top-level attribute.
|
|
2025
|
+
const mavenSha256 = extractTagValue(body, 'maven_sha256') ?? extractAttr(body, 'maven_sha256');
|
|
2026
|
+
results.push({
|
|
2027
|
+
ruleKind,
|
|
2028
|
+
ruleName,
|
|
2029
|
+
mavenCoordinates: coords,
|
|
2030
|
+
mavenUrl: extractAttr(body, 'maven_url'),
|
|
2031
|
+
mavenSha256,
|
|
2032
|
+
deps: extractDeps(body)
|
|
2033
|
+
});
|
|
2034
|
+
}
|
|
2035
|
+
return results;
|
|
2036
|
+
}
|
|
2037
|
+
function ruleNameFromCoordinate(c) {
|
|
2038
|
+
return c.replace(/[^A-Za-z0-9]/g, '_');
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
/**
|
|
2042
|
+
* Parse supported `external/<repo>/unsorted_deps.json` shapes emitted by
|
|
2043
|
+
* rules_jvm_external. Older files use an artifact array with full coordinates;
|
|
2044
|
+
* newer v2 lock-file-shaped files use artifact/dependency maps keyed by
|
|
2045
|
+
* `group:artifact`. Caller MUST size-cap the input because JSON.parse is
|
|
2046
|
+
* unbounded by default.
|
|
2047
|
+
*/
|
|
2048
|
+
function parseUnsortedDepsJson(json) {
|
|
2049
|
+
let parsed;
|
|
2050
|
+
try {
|
|
2051
|
+
parsed = JSON.parse(json);
|
|
2052
|
+
} catch {
|
|
2053
|
+
return [];
|
|
2054
|
+
}
|
|
2055
|
+
const maybe = parsed;
|
|
2056
|
+
if (Array.isArray(maybe.artifacts)) {
|
|
2057
|
+
const out = [];
|
|
2058
|
+
for (const a of maybe.artifacts) {
|
|
2059
|
+
if (typeof a?.coordinates !== 'string') {
|
|
2060
|
+
continue;
|
|
2061
|
+
}
|
|
2062
|
+
const deps = [];
|
|
2063
|
+
if (Array.isArray(a.deps)) {
|
|
2064
|
+
for (const d of a.deps) {
|
|
2065
|
+
if (typeof d === 'string') {
|
|
2066
|
+
deps.push(d);
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
out.push({
|
|
2071
|
+
ruleKind: 'jvm_import',
|
|
2072
|
+
ruleName: ruleNameFromCoordinate(a.coordinates),
|
|
2073
|
+
mavenCoordinates: a.coordinates,
|
|
2074
|
+
mavenUrl: typeof a.url === 'string' ? a.url : undefined,
|
|
2075
|
+
mavenSha256: typeof a.sha256 === 'string' ? a.sha256 : undefined,
|
|
2076
|
+
deps
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
return out;
|
|
2080
|
+
}
|
|
2081
|
+
if (!maybe.artifacts || typeof maybe.artifacts !== 'object') {
|
|
2082
|
+
return [];
|
|
2083
|
+
}
|
|
2084
|
+
const dependencies = maybe.dependencies ?? {};
|
|
2085
|
+
const out = [];
|
|
2086
|
+
for (const [groupArtifact, artifact] of Object.entries(maybe.artifacts)) {
|
|
2087
|
+
if (!artifact || typeof artifact.version !== 'string') {
|
|
2088
|
+
continue;
|
|
2089
|
+
}
|
|
2090
|
+
const shasums = artifact.shasums ?? {};
|
|
2091
|
+
const jarSha = shasums['jar'];
|
|
2092
|
+
if (typeof jarSha === 'string' || Object.keys(shasums).length === 0) {
|
|
2093
|
+
out.push(v2Artifact(groupArtifact, artifact.version, jarSha, dependencies));
|
|
2094
|
+
}
|
|
2095
|
+
for (const [classifier, sha256] of Object.entries(shasums)) {
|
|
2096
|
+
if (classifier === 'jar' || typeof sha256 !== 'string') {
|
|
2097
|
+
continue;
|
|
2098
|
+
}
|
|
2099
|
+
const classifierKey = `${groupArtifact}:jar:${classifier}`;
|
|
2100
|
+
out.push(v2Artifact(classifierKey, artifact.version, sha256, dependencies));
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
return out;
|
|
2104
|
+
}
|
|
2105
|
+
function v2Artifact(artifactKey, version, sha256, dependencies) {
|
|
2106
|
+
return {
|
|
2107
|
+
ruleKind: 'jvm_import',
|
|
2108
|
+
ruleName: ruleNameFromCoordinate(artifactKey),
|
|
2109
|
+
mavenCoordinates: `${artifactKey}:${version}`,
|
|
2110
|
+
mavenSha256: sha256,
|
|
2111
|
+
deps: Array.isArray(dependencies[artifactKey]) ? dependencies[artifactKey].filter(d => typeof d === 'string') : []
|
|
2112
|
+
};
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
let probed = false;
|
|
2116
|
+
|
|
2117
|
+
// Verifies `java` is functional in the current execution environment. Bazel
|
|
2118
|
+
// JVM manifest extraction (rules_jvm_external → Coursier) requires a real
|
|
2119
|
+
// JDK; the CLI does not attempt to discover Homebrew installs or mutate the
|
|
2120
|
+
// caller's PATH/JAVA_HOME. If `java -version` fails we throw with an
|
|
2121
|
+
// actionable message so the surfaced error names the prerequisite directly
|
|
2122
|
+
// instead of relying on Bazel's downstream diagnostic.
|
|
2123
|
+
function ensureJavaOnPath() {
|
|
2124
|
+
if (probed) {
|
|
2125
|
+
return;
|
|
2126
|
+
}
|
|
2127
|
+
try {
|
|
2128
|
+
childProcess.execSync('java -version', {
|
|
2129
|
+
stdio: 'ignore'
|
|
2130
|
+
});
|
|
2131
|
+
probed = true;
|
|
2132
|
+
} catch {
|
|
2133
|
+
throw new Error('Java is required for Bazel JVM manifest extraction ' + '(rules_jvm_external invokes Coursier, which needs a JDK). ' + 'Install a JDK (e.g. Temurin or OpenJDK) and ensure `java` is on PATH.');
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
// Validates that --bazel-output-base is a path we can use as Bazel's output_base.
|
|
2138
|
+
// Throws InputError if:
|
|
2139
|
+
// - the input contains `..` segments (path traversal guard)
|
|
2140
|
+
// - the existing path is not writable
|
|
2141
|
+
// - the path cannot be created (parent not writable)
|
|
2142
|
+
function validateOutputBase(outputBase, cwd) {
|
|
2143
|
+
// Path traversal guard: reject any literal `..` segment in user input.
|
|
2144
|
+
// After path.resolve these are normalised away, so we check the raw input.
|
|
2145
|
+
// Split on both separators. On Windows `path.sep === '\\'`, so
|
|
2146
|
+
// input like `foo/../etc` would not contain a `..` segment under the
|
|
2147
|
+
// platform-specific split, bypassing the guard — yet path.resolve below
|
|
2148
|
+
// would still normalise the `..` and a traversal target could materialise.
|
|
2149
|
+
const segments = outputBase.split(/[\\/]/);
|
|
2150
|
+
if (segments.includes('..')) {
|
|
2151
|
+
throw new utils.InputError(`--bazel-output-base must not contain '..' segments: ${outputBase}`);
|
|
2152
|
+
}
|
|
2153
|
+
const resolved = path.resolve(cwd, outputBase);
|
|
2154
|
+
if (fs$1.existsSync(resolved)) {
|
|
2155
|
+
try {
|
|
2156
|
+
fs$1.accessSync(resolved, fs$1.constants.W_OK);
|
|
2157
|
+
} catch {
|
|
2158
|
+
throw new utils.InputError(`--bazel-output-base is not writable: ${resolved}`);
|
|
2159
|
+
}
|
|
2160
|
+
return;
|
|
2161
|
+
}
|
|
2162
|
+
// Path does not exist yet — try to create it so bazel can populate it.
|
|
2163
|
+
try {
|
|
2164
|
+
fs$1.mkdirSync(resolved, {
|
|
2165
|
+
recursive: true
|
|
2166
|
+
});
|
|
2167
|
+
} catch (e) {
|
|
2168
|
+
throw new utils.InputError(`--bazel-output-base could not be created at ${resolved}: ${utils.getErrorCause(e)}`);
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
// Stable shim dir name — same process will get the same dir; concurrent
|
|
2173
|
+
// socket-cli invocations on the same machine share it. The symlink target
|
|
2174
|
+
// is whatever python3 resolves to NOW; if PATH changes between invocations
|
|
2175
|
+
// we replace the symlink.
|
|
2176
|
+
const SHIM_SUBDIR = 'socket-cli-bazel-python-shim';
|
|
2177
|
+
|
|
2178
|
+
// Cache the result for the lifetime of this process.
|
|
2179
|
+
let cached = null;
|
|
2180
|
+
|
|
2181
|
+
// Safe wrapper around whichBin that returns null instead of throwing when
|
|
2182
|
+
// nothrow semantics are broken in older registry versions (realpath 'null' bug).
|
|
2183
|
+
async function safeWhichBin(name) {
|
|
2184
|
+
try {
|
|
2185
|
+
return (await bin.whichBin(name, {
|
|
2186
|
+
nothrow: true
|
|
2187
|
+
})) ?? null;
|
|
2188
|
+
} catch {
|
|
2189
|
+
return null;
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
async function provisionPythonShim() {
|
|
2193
|
+
if (cached) {
|
|
2194
|
+
return cached;
|
|
2195
|
+
}
|
|
2196
|
+
const pythonOnPath = await safeWhichBin('python');
|
|
2197
|
+
if (pythonOnPath) {
|
|
2198
|
+
cached = {
|
|
2199
|
+
augmentedEnv: undefined,
|
|
2200
|
+
shimDir: undefined
|
|
2201
|
+
};
|
|
2202
|
+
return cached;
|
|
2203
|
+
}
|
|
2204
|
+
const python3OnPath = await safeWhichBin('python3');
|
|
2205
|
+
if (!python3OnPath) {
|
|
2206
|
+
throw new utils.InputError('Neither `python` nor `python3` found on PATH. Older versions of ' + 'rules_jvm_external require a `python` interpreter for repository ' + 'rules. Install Python 3 and ensure it is on PATH, then retry.');
|
|
2207
|
+
}
|
|
2208
|
+
const shimDir = path.join(os.tmpdir(), SHIM_SUBDIR);
|
|
2209
|
+
fs$1.mkdirSync(shimDir, {
|
|
2210
|
+
recursive: true
|
|
2211
|
+
});
|
|
2212
|
+
const linkPath = path.join(shimDir, 'python');
|
|
2213
|
+
// Replace the symlink defensively in case python3's resolved path moved.
|
|
2214
|
+
if (fs$1.existsSync(linkPath)) {
|
|
2215
|
+
try {
|
|
2216
|
+
fs$1.unlinkSync(linkPath);
|
|
2217
|
+
} catch {
|
|
2218
|
+
// Tolerate races; the next symlinkSync may still succeed.
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
// The shim dir is process-shared (os.tmpdir()/socket-cli-bazel-python-shim),
|
|
2222
|
+
// so a concurrent socket-cli invocation may re-create the link between our
|
|
2223
|
+
// unlinkSync and symlinkSync. Tolerate EEXIST when the link is back: the
|
|
2224
|
+
// other process won the race and left a usable shim in place.
|
|
2225
|
+
try {
|
|
2226
|
+
fs$1.symlinkSync(python3OnPath, linkPath);
|
|
2227
|
+
} catch (e) {
|
|
2228
|
+
if (e.code === 'EEXIST' && fs$1.existsSync(linkPath)) ; else {
|
|
2229
|
+
throw e;
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
const augmentedEnv = {
|
|
2233
|
+
...process.env,
|
|
2234
|
+
PATH: `${shimDir}${path.delimiter}${process.env['PATH'] ?? ''}`
|
|
2235
|
+
};
|
|
2236
|
+
cached = {
|
|
2237
|
+
augmentedEnv,
|
|
2238
|
+
shimDir
|
|
2239
|
+
};
|
|
2240
|
+
return cached;
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
// Default per-invocation timeout for bazel queries. Bazel cold-cache starts
|
|
2244
|
+
// can take several minutes; 10 minutes is generous while still bounding CI hangs.
|
|
2245
|
+
const BAZEL_QUERY_TIMEOUT_MS = 600_000;
|
|
2246
|
+
|
|
2247
|
+
// Splits the user-supplied --bazel-flags string on whitespace.
|
|
2248
|
+
// Empty / undefined returns []. No shell parsing — quoted args with embedded
|
|
2249
|
+
// whitespace are not supported (documented limitation; same trust model as
|
|
2250
|
+
// gradleOpts).
|
|
2251
|
+
function splitBazelFlags(flags) {
|
|
2252
|
+
if (!flags) {
|
|
2253
|
+
return [];
|
|
2254
|
+
}
|
|
2255
|
+
return flags.split(/\s+/).filter(Boolean);
|
|
2256
|
+
}
|
|
2257
|
+
function buildBazelModShowVisibleReposArgv(opts) {
|
|
2258
|
+
const startup = [];
|
|
2259
|
+
if (opts.bazelRc) {
|
|
2260
|
+
startup.push(`--bazelrc=${opts.bazelRc}`);
|
|
2261
|
+
}
|
|
2262
|
+
if (opts.bazelOutputBase) {
|
|
2263
|
+
startup.push(`--output_base=${opts.bazelOutputBase}`);
|
|
2264
|
+
}
|
|
2265
|
+
const userFlags = splitBazelFlags(opts.bazelFlags);
|
|
2266
|
+
return [...startup, 'mod', 'show_repo', '--all_visible_repos', '--output=streamed_jsonproto', ...userFlags];
|
|
2267
|
+
}
|
|
2268
|
+
function buildBazelArgv(queryStr, opts) {
|
|
2269
|
+
// Startup flags MUST precede the `query` subcommand.
|
|
2270
|
+
// Bazel argv shape: <startup> query <queryFlags> <invocationFlags> <queryStr> --output=build <userFlags>
|
|
2271
|
+
const startup = [];
|
|
2272
|
+
if (opts.bazelRc) {
|
|
2273
|
+
startup.push(`--bazelrc=${opts.bazelRc}`);
|
|
2274
|
+
}
|
|
2275
|
+
if (opts.bazelOutputBase) {
|
|
2276
|
+
startup.push(`--output_base=${opts.bazelOutputBase}`);
|
|
2277
|
+
}
|
|
2278
|
+
// Keep query output stable and avoid updating Bazel lockfiles while extracting.
|
|
2279
|
+
const queryFlags = ['--lockfile_mode=off', '--noshow_progress'];
|
|
2280
|
+
const userFlags = splitBazelFlags(opts.bazelFlags);
|
|
2281
|
+
return [...startup, 'query', ...queryFlags, ...opts.invocationFlags, queryStr, '--output=build', ...userFlags];
|
|
2282
|
+
}
|
|
2283
|
+
function stringField(value) {
|
|
2284
|
+
return typeof value === 'string' ? value : '';
|
|
2285
|
+
}
|
|
2286
|
+
function numericExitCode(value) {
|
|
2287
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
2288
|
+
}
|
|
2289
|
+
function normalizeSpawnError(error) {
|
|
2290
|
+
const e = error;
|
|
2291
|
+
return {
|
|
2292
|
+
code: numericExitCode(e?.code) ?? numericExitCode(e?.status) ?? -1,
|
|
2293
|
+
stderr: stringField(e?.stderr),
|
|
2294
|
+
stdout: stringField(e?.stdout)
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
/**
|
|
2299
|
+
* Run `bazel query` with the standardized argv shape and capture
|
|
2300
|
+
* stdout/stderr/code. Wraps the call in a spinner that resolves on success
|
|
2301
|
+
* and fails on non-zero exit. Rejected spawn calls are normalized into a
|
|
2302
|
+
* BazelQueryResult so retry/skip handling can inspect stderr.
|
|
2303
|
+
*/
|
|
2304
|
+
async function runBazelQuery(queryStr, opts) {
|
|
2305
|
+
const argv = buildBazelArgv(queryStr, opts);
|
|
2306
|
+
if (opts.verbose) {
|
|
2307
|
+
logger.logger.log('[VERBOSE] Executing:', opts.bin, ', args:', argv);
|
|
2308
|
+
}
|
|
2309
|
+
const {
|
|
2310
|
+
spinner
|
|
2311
|
+
} = constants.default;
|
|
2312
|
+
let result;
|
|
2313
|
+
try {
|
|
2314
|
+
spinner.start(`Running bazel query (${queryStr.slice(0, 80)})...`);
|
|
2315
|
+
const output = await spawn.spawn(opts.bin, argv, {
|
|
2316
|
+
cwd: opts.cwd,
|
|
2317
|
+
timeout: BAZEL_QUERY_TIMEOUT_MS,
|
|
2318
|
+
...(opts.env ? {
|
|
2319
|
+
env: opts.env
|
|
2320
|
+
} : {})
|
|
2321
|
+
});
|
|
2322
|
+
const {
|
|
2323
|
+
code,
|
|
2324
|
+
stderr,
|
|
2325
|
+
stdout
|
|
2326
|
+
} = output;
|
|
2327
|
+
result = {
|
|
2328
|
+
code,
|
|
2329
|
+
stdout,
|
|
2330
|
+
stderr
|
|
2331
|
+
};
|
|
2332
|
+
return result;
|
|
2333
|
+
} catch (e) {
|
|
2334
|
+
result = normalizeSpawnError(e);
|
|
2335
|
+
return result;
|
|
2336
|
+
} finally {
|
|
2337
|
+
const truncated = queryStr.slice(0, 80);
|
|
2338
|
+
if (result?.code === 0) {
|
|
2339
|
+
spinner.successAndStop(`bazel query completed (${truncated}).`);
|
|
2340
|
+
} else {
|
|
2341
|
+
spinner.failAndStop(`bazel query failed (${truncated}).`);
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
/**
|
|
2347
|
+
* Bzlmod-native visible repository enumeration. This is only a candidate
|
|
2348
|
+
* source; callers must still validate each returned apparent repo name with a
|
|
2349
|
+
* semantic query for generated JVM Maven rules.
|
|
2350
|
+
*/
|
|
2351
|
+
async function runBazelModShowVisibleRepos(opts) {
|
|
2352
|
+
const argv = buildBazelModShowVisibleReposArgv(opts);
|
|
2353
|
+
if (opts.verbose) {
|
|
2354
|
+
logger.logger.log('[VERBOSE] Executing:', opts.bin, ', args:', argv);
|
|
2355
|
+
}
|
|
2356
|
+
try {
|
|
2357
|
+
const output = await spawn.spawn(opts.bin, argv, {
|
|
2358
|
+
cwd: opts.cwd,
|
|
2359
|
+
timeout: BAZEL_QUERY_TIMEOUT_MS,
|
|
2360
|
+
...(opts.env ? {
|
|
2361
|
+
env: opts.env
|
|
2362
|
+
} : {})
|
|
2363
|
+
});
|
|
2364
|
+
const {
|
|
2365
|
+
code,
|
|
2366
|
+
stderr,
|
|
2367
|
+
stdout
|
|
2368
|
+
} = output;
|
|
2369
|
+
return {
|
|
2370
|
+
code,
|
|
2371
|
+
stdout,
|
|
2372
|
+
stderr
|
|
2373
|
+
};
|
|
2374
|
+
} catch (e) {
|
|
2375
|
+
return normalizeSpawnError(e);
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
/**
|
|
2380
|
+
* Build a `RepoProbe` (compatible with bazel-repo-discovery) bound to opts.
|
|
2381
|
+
* Used by `discoverMavenRepos` to validate candidate Maven repo
|
|
2382
|
+
* names against the running workspace.
|
|
2383
|
+
*/
|
|
2384
|
+
function buildProbeFor(opts) {
|
|
2385
|
+
return async repoName => {
|
|
2386
|
+
const queryStr = `kind("jvm_import rule|aar_import rule", @${repoName}//:*)`;
|
|
2387
|
+
const result = await runBazelQuery(queryStr, opts);
|
|
2388
|
+
return {
|
|
2389
|
+
stdout: result.stdout,
|
|
2390
|
+
code: result.code
|
|
2391
|
+
};
|
|
2392
|
+
};
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
// Maximum size (bytes) we will read for any single Bazel workspace file.
|
|
2396
|
+
// Prevents DoS via maliciously large MODULE.bazel / WORKSPACE / .bzl files.
|
|
2397
|
+
const MAX_WORKSPACE_FILE_BYTES = 5 * 1024 * 1024;
|
|
2398
|
+
|
|
2399
|
+
// Maximum candidate count we will return (deduped) before truncating.
|
|
2400
|
+
// Real repos have <20; this is a hard ceiling against pathological inputs.
|
|
2401
|
+
const MAX_CANDIDATES = 256;
|
|
2402
|
+
|
|
2403
|
+
// Regex strategy: anchored, bounded character classes, no nested quantifiers.
|
|
2404
|
+
// Match `use_repo(maven, "X", "Y", ...)` with a bounded arg-list window to
|
|
2405
|
+
// avoid catastrophic backtracking on hostile input.
|
|
2406
|
+
|
|
2407
|
+
// Bzlmod use_repo(maven, "name1", "name2"...).
|
|
2408
|
+
// Bounded: matches up to ~4KB of arg list to avoid catastrophic backtracking.
|
|
2409
|
+
const USE_REPO_RE = /use_repo\s*\(\s*maven\s*,([^)]{0,4096})\)/g;
|
|
2410
|
+
const BAZEL_REPO_NAME_PATTERN = '[A-Za-z0-9._+-]{1,129}';
|
|
2411
|
+
const BAZEL_REPO_NAME_RE = new RegExp(`^${BAZEL_REPO_NAME_PATTERN}$`);
|
|
2412
|
+
// Quoted-name extractor inside the captured argument blob.
|
|
2413
|
+
const QUOTED_NAME_RE = new RegExp(`"(${BAZEL_REPO_NAME_PATTERN})"`, 'g');
|
|
2414
|
+
|
|
2415
|
+
// Legacy maven_install(name = "X", ...) on a single statement.
|
|
2416
|
+
// Match the name= keyword arg specifically; bounded.
|
|
2417
|
+
const MAVEN_INSTALL_NAME_RE = new RegExp(`maven_install\\s*\\([^)]{0,8192}?\\bname\\s*=\\s*"(${BAZEL_REPO_NAME_PATTERN})"`, 'g');
|
|
2418
|
+
const MAVEN_COORDINATES_MARKER_RE = /\bmaven_coordinates\s*=/;
|
|
2419
|
+
|
|
2420
|
+
// Reads file contents, refusing files that exceed MAX_WORKSPACE_FILE_BYTES.
|
|
2421
|
+
// Returns null when the file is missing, oversized, or unreadable.
|
|
2422
|
+
function safeReadFile(file) {
|
|
2423
|
+
if (!fs$1.existsSync(file)) {
|
|
2424
|
+
return null;
|
|
2425
|
+
}
|
|
2426
|
+
try {
|
|
2427
|
+
const stat = fs$1.statSync(file);
|
|
2428
|
+
if (stat.size > MAX_WORKSPACE_FILE_BYTES) {
|
|
2429
|
+
return null;
|
|
2430
|
+
}
|
|
2431
|
+
return fs$1.readFileSync(file, 'utf8');
|
|
2432
|
+
} catch {
|
|
2433
|
+
return null;
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
// Walks workspace root for legacy Starlark sources we can scan: WORKSPACE
|
|
2438
|
+
// (and WORKSPACE.bazel) plus top-level .bzl files. Non-recursive by design;
|
|
2439
|
+
// Phase 1 explicitly avoids static Starlark parsing at depth.
|
|
2440
|
+
function listLegacyStarlarkFiles(cwd) {
|
|
2441
|
+
const files = [];
|
|
2442
|
+
const candidates = ['WORKSPACE', 'WORKSPACE.bazel'];
|
|
2443
|
+
for (const c of candidates) {
|
|
2444
|
+
const p = path.join(cwd, c);
|
|
2445
|
+
if (fs$1.existsSync(p)) {
|
|
2446
|
+
files.push(p);
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
// Top-level .bzl files only.
|
|
2450
|
+
try {
|
|
2451
|
+
for (const entry of fs$1.readdirSync(cwd)) {
|
|
2452
|
+
if (entry.endsWith('.bzl')) {
|
|
2453
|
+
files.push(path.join(cwd, entry));
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
} catch {
|
|
2457
|
+
// Ignore unreadable cwd.
|
|
2458
|
+
}
|
|
2459
|
+
return files;
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
// Returns deduplicated, sorted list of items, capped at MAX_CANDIDATES.
|
|
2463
|
+
function uniqueSorted(items) {
|
|
2464
|
+
const seen = new Set();
|
|
2465
|
+
const out = [];
|
|
2466
|
+
for (const item of items) {
|
|
2467
|
+
if (!seen.has(item)) {
|
|
2468
|
+
seen.add(item);
|
|
2469
|
+
out.push(item);
|
|
2470
|
+
if (out.length >= MAX_CANDIDATES) {
|
|
2471
|
+
break;
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
return out.sort();
|
|
2476
|
+
}
|
|
2477
|
+
function apparentNameFromJsonValue(value) {
|
|
2478
|
+
if (!value || typeof value !== 'object') {
|
|
2479
|
+
return undefined;
|
|
2480
|
+
}
|
|
2481
|
+
const obj = value;
|
|
2482
|
+
const direct = obj['apparentName'] ?? obj['apparent_name'];
|
|
2483
|
+
if (typeof direct === 'string') {
|
|
2484
|
+
return direct;
|
|
2485
|
+
}
|
|
2486
|
+
for (const nested of Object.values(obj)) {
|
|
2487
|
+
const found = apparentNameFromJsonValue(nested);
|
|
2488
|
+
if (found) {
|
|
2489
|
+
return found;
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
return undefined;
|
|
2493
|
+
}
|
|
2494
|
+
function normalizeRepoName(name) {
|
|
2495
|
+
const repo = name.startsWith('@') ? name.slice(1) : name;
|
|
2496
|
+
return BAZEL_REPO_NAME_RE.test(repo) ? repo : undefined;
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
// Parse `bazel mod show_repo --all_visible_repos --output=streamed_jsonproto`
|
|
2500
|
+
// output. Bazel's JSON proto field casing may vary by formatter; accept both
|
|
2501
|
+
// lowerCamel and snake_case, and tolerate wrapper objects around Repository.
|
|
2502
|
+
function parseVisibleRepoCandidates(output) {
|
|
2503
|
+
const candidates = [];
|
|
2504
|
+
for (const line of output.split(/\r?\n/)) {
|
|
2505
|
+
const trimmed = line.trim();
|
|
2506
|
+
if (!trimmed) {
|
|
2507
|
+
continue;
|
|
2508
|
+
}
|
|
2509
|
+
try {
|
|
2510
|
+
const parsed = JSON.parse(trimmed);
|
|
2511
|
+
const apparentName = apparentNameFromJsonValue(parsed);
|
|
2512
|
+
if (apparentName) {
|
|
2513
|
+
const repo = normalizeRepoName(apparentName);
|
|
2514
|
+
if (repo) {
|
|
2515
|
+
candidates.push(repo);
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
} catch {
|
|
2519
|
+
// Ignore malformed lines; caller will fall back to static discovery when
|
|
2520
|
+
// no usable visible repo names are found.
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
return uniqueSorted(candidates);
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
// Step 1: parse candidate Maven repo names from Bzlmod and legacy entry points.
|
|
2527
|
+
function parseMavenRepoCandidates(cwd, verbose) {
|
|
2528
|
+
const candidates = [];
|
|
2529
|
+
|
|
2530
|
+
// Bzlmod path: parse MODULE.bazel for use_repo(maven, ...).
|
|
2531
|
+
const moduleBazel = path.join(cwd, 'MODULE.bazel');
|
|
2532
|
+
const moduleContent = safeReadFile(moduleBazel);
|
|
2533
|
+
if (moduleContent) {
|
|
2534
|
+
const bzlmodHits = [];
|
|
2535
|
+
for (const m of moduleContent.matchAll(USE_REPO_RE)) {
|
|
2536
|
+
const argBlob = m[1] ?? '';
|
|
2537
|
+
for (const n of argBlob.matchAll(QUOTED_NAME_RE)) {
|
|
2538
|
+
bzlmodHits.push(n[1]);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
candidates.push(...bzlmodHits);
|
|
2542
|
+
if (verbose) {
|
|
2543
|
+
logger.logger.log('[VERBOSE] discovery: scanned', moduleBazel, `(${bzlmodHits.length} use_repo match(es))`);
|
|
2544
|
+
}
|
|
2545
|
+
} else if (verbose) {
|
|
2546
|
+
logger.logger.log('[VERBOSE] discovery:', moduleBazel, 'not present (skipping bzlmod scan)');
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
// Legacy path: scan WORKSPACE + top-level .bzl files for maven_install(name=...).
|
|
2550
|
+
const legacyFiles = listLegacyStarlarkFiles(cwd);
|
|
2551
|
+
if (verbose) {
|
|
2552
|
+
logger.logger.log('[VERBOSE] discovery: legacy files considered:', legacyFiles.length ? legacyFiles : '(none)');
|
|
2553
|
+
}
|
|
2554
|
+
for (const file of legacyFiles) {
|
|
2555
|
+
const content = safeReadFile(file);
|
|
2556
|
+
if (!content) {
|
|
2557
|
+
continue;
|
|
2558
|
+
}
|
|
2559
|
+
const fileHits = [];
|
|
2560
|
+
for (const m of content.matchAll(MAVEN_INSTALL_NAME_RE)) {
|
|
2561
|
+
fileHits.push(m[1]);
|
|
2562
|
+
}
|
|
2563
|
+
candidates.push(...fileHits);
|
|
2564
|
+
if (verbose) {
|
|
2565
|
+
logger.logger.log('[VERBOSE] discovery: scanned', file, `(${fileHits.length} maven_install name match(es))`);
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
const deduped = uniqueSorted(candidates);
|
|
2569
|
+
if (verbose) {
|
|
2570
|
+
logger.logger.log('[VERBOSE] discovery: candidate set (pre-seed):', deduped);
|
|
2571
|
+
}
|
|
2572
|
+
return deduped;
|
|
2573
|
+
}
|
|
2574
|
+
// Step 2: validate a candidate by running the probe and confirming
|
|
2575
|
+
// `maven_coordinates=` appears in stdout (the marker emitted by jvm_import /
|
|
2576
|
+
// aar_import rules generated by rules_jvm_external). Returns the probe
|
|
2577
|
+
// stdout alongside the verdict so the caller can cache it and reuse it
|
|
2578
|
+
// instead of running an identical extraction query.
|
|
2579
|
+
async function validateMavenRepo(repoName, probe, verbose) {
|
|
2580
|
+
try {
|
|
2581
|
+
const result = await probe(repoName);
|
|
2582
|
+
if (result.code !== 0) {
|
|
2583
|
+
if (verbose) {
|
|
2584
|
+
logger.logger.log(`[VERBOSE] discovery: probe @${repoName}: REJECT (code=${result.code})`);
|
|
2585
|
+
}
|
|
2586
|
+
return {
|
|
2587
|
+
valid: false,
|
|
2588
|
+
stdout: result.stdout
|
|
2589
|
+
};
|
|
2590
|
+
}
|
|
2591
|
+
const valid = MAVEN_COORDINATES_MARKER_RE.test(result.stdout);
|
|
2592
|
+
if (verbose) {
|
|
2593
|
+
logger.logger.log(`[VERBOSE] discovery: probe @${repoName}:`, valid ? 'ACCEPT (maven_coordinates marker found)' : 'REJECT (no maven_coordinates marker in probe stdout)');
|
|
2594
|
+
}
|
|
2595
|
+
return {
|
|
2596
|
+
valid,
|
|
2597
|
+
stdout: result.stdout
|
|
2598
|
+
};
|
|
2599
|
+
} catch (e) {
|
|
2600
|
+
if (verbose) {
|
|
2601
|
+
logger.logger.log(`[VERBOSE] discovery: probe @${repoName}: REJECT (probe threw):`, utils.getErrorCause(e));
|
|
2602
|
+
}
|
|
2603
|
+
return {
|
|
2604
|
+
valid: false,
|
|
2605
|
+
stdout: ''
|
|
2606
|
+
};
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
// The default maven_install repo name when no explicit `name=` is given.
|
|
2611
|
+
// Included as a seed so repos that define maven_install in a subdirectory
|
|
2612
|
+
// .bzl file (not scanned by parseMavenRepoCandidates) are still discovered.
|
|
2613
|
+
const DEFAULT_MAVEN_REPO_SEED = 'maven';
|
|
2614
|
+
|
|
2615
|
+
// Composition: parse, then validate each candidate; return validated subset
|
|
2616
|
+
// as a Map keyed by repo name with the validated probe stdout as value.
|
|
2617
|
+
// Map iteration order matches insertion order, so callers that just want
|
|
2618
|
+
// the list of repo names can call `Array.from(repos.keys())`. Callers that
|
|
2619
|
+
// want to skip re-running the same `bazel query` during extraction can read
|
|
2620
|
+
// the cached stdout off the Map and parse it directly.
|
|
2621
|
+
//
|
|
2622
|
+
// Always seeds with the default `@maven` repo name so repos whose
|
|
2623
|
+
// maven_install is defined in a sub-directory .bzl file (not reachable by
|
|
2624
|
+
// the top-level static scan) can still be discovered via probe validation.
|
|
2625
|
+
async function discoverMavenRepos(cwd, probe, nativeCandidates, verbose) {
|
|
2626
|
+
const parsed = nativeCandidates && nativeCandidates.length ? nativeCandidates : parseMavenRepoCandidates(cwd, verbose);
|
|
2627
|
+
if (verbose) {
|
|
2628
|
+
logger.logger.log('[VERBOSE] discovery: candidate source:', nativeCandidates && nativeCandidates.length ? `bzlmod visible-repos (${nativeCandidates.length})` : `static parse (${parsed.length})`);
|
|
2629
|
+
}
|
|
2630
|
+
// Seed with the default repo name first (so it appears first in output if
|
|
2631
|
+
// validated). Dedup via Set before validation.
|
|
2632
|
+
const seen = new Set([DEFAULT_MAVEN_REPO_SEED]);
|
|
2633
|
+
const candidates = [DEFAULT_MAVEN_REPO_SEED];
|
|
2634
|
+
for (const c of parsed) {
|
|
2635
|
+
if (!seen.has(c)) {
|
|
2636
|
+
seen.add(c);
|
|
2637
|
+
candidates.push(c);
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
if (verbose) {
|
|
2641
|
+
logger.logger.log('[VERBOSE] discovery: candidate set to probe (seed-first, deduped):', candidates);
|
|
2642
|
+
}
|
|
2643
|
+
const validated = new Map();
|
|
2644
|
+
for (const c of candidates) {
|
|
2645
|
+
// eslint-disable-next-line no-await-in-loop
|
|
2646
|
+
const result = await validateMavenRepo(c, probe, verbose);
|
|
2647
|
+
if (result.valid) {
|
|
2648
|
+
validated.set(c, result.stdout);
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
if (verbose) {
|
|
2652
|
+
logger.logger.log('[VERBOSE] discovery: validated repos:', Array.from(validated.keys()));
|
|
2653
|
+
}
|
|
2654
|
+
return validated;
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
// Detects whether the given Bazel workspace uses Bzlmod (MODULE.bazel),
|
|
2658
|
+
// legacy WORKSPACE (WORKSPACE or WORKSPACE.bazel), or both (migration).
|
|
2659
|
+
// Throws InputError when neither marker file is present.
|
|
2660
|
+
function detectWorkspaceMode(cwd) {
|
|
2661
|
+
const moduleBazel = fs$1.existsSync(path.join(cwd, 'MODULE.bazel'));
|
|
2662
|
+
const workspaceFile = fs$1.existsSync(path.join(cwd, 'WORKSPACE')) || fs$1.existsSync(path.join(cwd, 'WORKSPACE.bazel'));
|
|
2663
|
+
if (!moduleBazel && !workspaceFile) {
|
|
2664
|
+
throw new utils.InputError(`No Bazel workspace found at ${cwd} (looked for MODULE.bazel, WORKSPACE, WORKSPACE.bazel).`);
|
|
2665
|
+
}
|
|
2666
|
+
return {
|
|
2667
|
+
bzlmod: moduleBazel,
|
|
2668
|
+
workspace: workspaceFile
|
|
2669
|
+
};
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
// Returns the bazel CLI flags needed to invoke the correct workspace mode.
|
|
2673
|
+
// Bzlmod-only or migration-window: rely on Bazel 7+ default (Bzlmod on).
|
|
2674
|
+
// Legacy-only: explicitly disable Bzlmod and enable WORKSPACE.
|
|
2675
|
+
function getBazelInvocationFlags(mode) {
|
|
2676
|
+
if (mode.bzlmod) {
|
|
2677
|
+
// Bzlmod-only or migration: Bzlmod wins; no flags needed (Bazel 7+ default).
|
|
2678
|
+
return [];
|
|
2679
|
+
}
|
|
2680
|
+
// Legacy-only: explicitly switch to WORKSPACE mode.
|
|
2681
|
+
return ['--noenable_bzlmod', '--enable_workspace'];
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
// Splits "g:a:v" -> { groupArtifact: "g:a", version: "v" }.
|
|
2685
|
+
// Returns null on malformed input.
|
|
2686
|
+
function splitCoord(c) {
|
|
2687
|
+
const lastColon = c.lastIndexOf(':');
|
|
2688
|
+
if (lastColon < 1) {
|
|
2689
|
+
return null;
|
|
2690
|
+
}
|
|
2691
|
+
return {
|
|
2692
|
+
groupArtifact: c.slice(0, lastColon),
|
|
2693
|
+
version: c.slice(lastColon + 1)
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
// Builds a lookup from rule label suffix (e.g. ":com_google_guava_guava") to canonical coord.
|
|
2697
|
+
function buildLabelToCoordMap(artifacts) {
|
|
2698
|
+
const fullLabels = new Map();
|
|
2699
|
+
const suffixToCoords = new Map();
|
|
2700
|
+
for (const a of artifacts) {
|
|
2701
|
+
// The rule name (e.g. "com_google_guava_guava") becomes the path under @<repo>//:<name>.
|
|
2702
|
+
// We record by ":<name>" suffix so we can look up regardless of repo name.
|
|
2703
|
+
const suffix = `:${a.ruleName}`;
|
|
2704
|
+
const coords = suffixToCoords.get(suffix) ?? new Set();
|
|
2705
|
+
coords.add(a.mavenCoordinates);
|
|
2706
|
+
suffixToCoords.set(suffix, coords);
|
|
2707
|
+
if (a.sourceRepo) {
|
|
2708
|
+
fullLabels.set(`@${a.sourceRepo}//${suffix}`, a.mavenCoordinates);
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
return {
|
|
2712
|
+
fullLabels,
|
|
2713
|
+
suffixToCoords
|
|
2714
|
+
};
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
// Converts a Bazel dep label to a Maven coordinate, using the label-to-coord map.
|
|
2718
|
+
// Returns null when the label is not recognised.
|
|
2719
|
+
function depLabelToCoord(label, labelToCoord) {
|
|
2720
|
+
// label may be "@maven//:com_google_guava_failureaccess".
|
|
2721
|
+
const colon = label.lastIndexOf(':');
|
|
2722
|
+
if (colon < 0) {
|
|
2723
|
+
return null;
|
|
2724
|
+
}
|
|
2725
|
+
const fullMatch = labelToCoord.fullLabels.get(label);
|
|
2726
|
+
if (fullMatch) {
|
|
2727
|
+
return fullMatch;
|
|
2728
|
+
}
|
|
2729
|
+
const key = label.slice(colon);
|
|
2730
|
+
const suffixMatches = labelToCoord.suffixToCoords.get(key);
|
|
2731
|
+
if (!suffixMatches) {
|
|
2732
|
+
return null;
|
|
2733
|
+
}
|
|
2734
|
+
if (suffixMatches.size > 1) {
|
|
2735
|
+
throw new Error(`Ambiguous Bazel dependency label ${label} maps rule suffix ${key} to multiple Maven coordinates: ${Array.from(suffixMatches).sort().join(', ')}. The generated maven_install.json cannot resolve this dependency label losslessly.`);
|
|
2736
|
+
}
|
|
2737
|
+
return Array.from(suffixMatches)[0] ?? null;
|
|
2738
|
+
}
|
|
2739
|
+
function normalizeToMavenInstallJson(artifacts) {
|
|
2740
|
+
const labelToCoord = buildLabelToCoordMap(artifacts);
|
|
2741
|
+
const out = {
|
|
2742
|
+
artifacts: {},
|
|
2743
|
+
dependencies: {}
|
|
2744
|
+
};
|
|
2745
|
+
const versionsByGroupArtifact = new Map();
|
|
2746
|
+
const dependencySets = new Map();
|
|
2747
|
+
for (const a of artifacts) {
|
|
2748
|
+
const split = splitCoord(a.mavenCoordinates);
|
|
2749
|
+
if (!split) {
|
|
2750
|
+
continue;
|
|
2751
|
+
}
|
|
2752
|
+
const existingVersion = versionsByGroupArtifact.get(split.groupArtifact);
|
|
2753
|
+
if (existingVersion && existingVersion !== split.version) {
|
|
2754
|
+
throw new Error(`Conflicting versions for ${split.groupArtifact}: ${existingVersion}, ${split.version}. The generated maven_install.json cannot represent multiple versions for the same group:artifact losslessly.`);
|
|
2755
|
+
}
|
|
2756
|
+
if (!existingVersion) {
|
|
2757
|
+
versionsByGroupArtifact.set(split.groupArtifact, split.version);
|
|
2758
|
+
out.artifacts[split.groupArtifact] = {
|
|
2759
|
+
shasums: a.mavenSha256 ? {
|
|
2760
|
+
jar: a.mavenSha256
|
|
2761
|
+
} : {},
|
|
2762
|
+
version: split.version
|
|
2763
|
+
};
|
|
2764
|
+
} else if (a.mavenSha256 && !out.artifacts[split.groupArtifact]?.shasums.jar) {
|
|
2765
|
+
out.artifacts[split.groupArtifact] = {
|
|
2766
|
+
shasums: {
|
|
2767
|
+
jar: a.mavenSha256
|
|
2768
|
+
},
|
|
2769
|
+
version: split.version
|
|
2770
|
+
};
|
|
2771
|
+
}
|
|
2772
|
+
// Dependency keys in maven_install.json use "g:a" (no version),
|
|
2773
|
+
// matching the canonical rules_jvm_external lockfile shape.
|
|
2774
|
+
// Only emit an entry when there are actual dependencies (lockfile omits
|
|
2775
|
+
// artifacts with an empty dep list).
|
|
2776
|
+
const depKey = split.groupArtifact;
|
|
2777
|
+
const depCoords = dependencySets.get(depKey) ?? new Set();
|
|
2778
|
+
for (const depLabel of a.deps) {
|
|
2779
|
+
// First try our rule-label lookup (the common case for --output=build text).
|
|
2780
|
+
const c = depLabelToCoord(depLabel, labelToCoord);
|
|
2781
|
+
if (c) {
|
|
2782
|
+
// c is "g:a:v"; strip the version to produce "g:a" per lockfile shape.
|
|
2783
|
+
const cs = splitCoord(c);
|
|
2784
|
+
depCoords.add(cs ? cs.groupArtifact : c);
|
|
2785
|
+
} else if (depLabel.includes(':') && !depLabel.startsWith('@') && !depLabel.startsWith(':')) {
|
|
2786
|
+
// unsorted_deps.json deps may be "g:a:v" in older files or
|
|
2787
|
+
// "g:a" in v2 lock-file-shaped maps. Strip only when a version is
|
|
2788
|
+
// present.
|
|
2789
|
+
const parts = depLabel.split(':');
|
|
2790
|
+
depCoords.add(parts.length >= 3 ? parts.slice(0, -1).join(':') : depLabel);
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
if (depCoords.size) {
|
|
2794
|
+
dependencySets.set(depKey, depCoords);
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
for (const [depKey, depCoords] of dependencySets) {
|
|
2798
|
+
out.dependencies[depKey] = Array.from(depCoords);
|
|
2799
|
+
}
|
|
2800
|
+
return out;
|
|
2801
|
+
}
|
|
2802
|
+
|
|
2803
|
+
// Resolves the bazel `external/` dir for the given workspace.
|
|
2804
|
+
//
|
|
2805
|
+
// Bazel's `bazel-out/` convenience symlink points at
|
|
2806
|
+
// `<output_base>/execroot/<workspace>/bazel-out/`; the `external/` dir we
|
|
2807
|
+
// want is at `<output_base>/external/`. `path.join` is purely lexical and
|
|
2808
|
+
// would collapse `bazel-out/..` to the cwd itself, which is the wrong place
|
|
2809
|
+
// Resolve the symlink at the filesystem level and walk up to
|
|
2810
|
+
// `<output_base>` instead.
|
|
2811
|
+
function bazelExternalDir(cwd, outputBase) {
|
|
2812
|
+
if (outputBase) {
|
|
2813
|
+
return path.join(outputBase, 'external');
|
|
2814
|
+
}
|
|
2815
|
+
const bazelOutLink = path.join(cwd, 'bazel-out');
|
|
2816
|
+
if (!fs$1.existsSync(bazelOutLink)) {
|
|
2817
|
+
return null;
|
|
2818
|
+
}
|
|
2819
|
+
try {
|
|
2820
|
+
// realpath follows symlinks: .../<output_base>/execroot/<workspace>/bazel-out
|
|
2821
|
+
const real = fs$1.realpathSync(bazelOutLink);
|
|
2822
|
+
// Walk up bazel-out -> <workspace> -> execroot -> <output_base>, then into external/.
|
|
2823
|
+
return path.join(real, '..', '..', '..', 'external');
|
|
2824
|
+
} catch {
|
|
2825
|
+
return null;
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
|
|
2829
|
+
// Tries `external/<repo>/unsorted_deps.json` first; falls back to parsing the
|
|
2830
|
+
// probe stdout the caller already captured during discovery. Discovery runs
|
|
2831
|
+
// the same `kind("jvm_import rule|aar_import rule", @<repo>//:*)` query that
|
|
2832
|
+
// extraction needs, so reusing its stdout skips one bazel-query invocation
|
|
2833
|
+
// per repo on the unpinned path (where unsorted_deps.json isn't on disk).
|
|
2834
|
+
async function extractFromOneRepo(repoName, queryOpts, cachedProbeStdout) {
|
|
2835
|
+
const verbose = queryOpts.verbose;
|
|
2836
|
+
// unsorted_deps.json lives under the bazel external dir.
|
|
2837
|
+
// When --output_base is set, it's under that; otherwise under the workspace's
|
|
2838
|
+
// bazel-out symlink (resolved via realpath, NOT lexical path.join — the
|
|
2839
|
+
// lexical form would collapse `bazel-out/..` to cwd and miss the file).
|
|
2840
|
+
const externalDir = bazelExternalDir(queryOpts.cwd, queryOpts.bazelOutputBase);
|
|
2841
|
+
if (verbose) {
|
|
2842
|
+
logger.logger.log(`[VERBOSE] @${repoName}: external dir:`, externalDir ?? '(unresolved — bazel-out symlink absent)');
|
|
2843
|
+
}
|
|
2844
|
+
const candidates = externalDir ? [path.join(externalDir, repoName, 'unsorted_deps.json')] : [];
|
|
2845
|
+
for (const c of candidates) {
|
|
2846
|
+
if (fs$1.existsSync(c)) {
|
|
2847
|
+
// Bound the read to 1GB to prevent OOM on hostile content while allowing large real-world lockfiles.
|
|
2848
|
+
// eslint-disable-next-line no-await-in-loop
|
|
2849
|
+
const stat = await fs$1.promises.stat(c);
|
|
2850
|
+
if (stat.size > 1024 * 1024 * 1024) {
|
|
2851
|
+
logger.logger.warn(`Skipping oversized ${c} (${stat.size} bytes); falling back to cached probe stdout.`);
|
|
2852
|
+
break;
|
|
2853
|
+
}
|
|
2854
|
+
const json = fs$1.readFileSync(c, 'utf8');
|
|
2855
|
+
const parsed = parseUnsortedDepsJson(json);
|
|
2856
|
+
if (parsed.length) {
|
|
2857
|
+
if (verbose) {
|
|
2858
|
+
logger.logger.log(`[VERBOSE] @${repoName}: source=unsorted_deps.json (${c}, ${parsed.length} artifact(s))`);
|
|
2859
|
+
}
|
|
2860
|
+
return parsed.map(a => ({
|
|
2861
|
+
...a,
|
|
2862
|
+
sourceRepo: repoName
|
|
2863
|
+
}));
|
|
2864
|
+
}
|
|
2865
|
+
} else if (verbose) {
|
|
2866
|
+
logger.logger.log(`[VERBOSE] @${repoName}: unsorted_deps.json miss at`, c);
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
// Reuse the probe stdout that discovery already captured for this repo.
|
|
2870
|
+
// The probe ran exactly this query during validation and only validated
|
|
2871
|
+
// repos with code === 0 make it into the cache, so retry is unnecessary
|
|
2872
|
+
// — if the probe was flaky, the repo wouldn't be in the map.
|
|
2873
|
+
if (!cachedProbeStdout) {
|
|
2874
|
+
logger.logger.warn(`No cached probe stdout for @${repoName}; skipping. (This shouldn't happen — discovery should have populated it.)`);
|
|
2875
|
+
return [];
|
|
2876
|
+
}
|
|
2877
|
+
if (verbose) {
|
|
2878
|
+
logger.logger.log(`[VERBOSE] @${repoName}: source=cached probe stdout (${cachedProbeStdout.length} bytes)`);
|
|
2879
|
+
}
|
|
2880
|
+
return parseBazelBuildOutput(cachedProbeStdout).map(a => ({
|
|
2881
|
+
...a,
|
|
2882
|
+
sourceRepo: repoName
|
|
2883
|
+
}));
|
|
2884
|
+
}
|
|
2885
|
+
async function extractBazelToMaven(opts) {
|
|
2886
|
+
const {
|
|
2887
|
+
cwd,
|
|
2888
|
+
out,
|
|
2889
|
+
verbose
|
|
2890
|
+
} = opts;
|
|
2891
|
+
logger.logger.group('bazel2maven:');
|
|
2892
|
+
logger.logger.info(`- src dir: \`${cwd}\``);
|
|
2893
|
+
logger.logger.info(`- out dir: \`${out}\``);
|
|
2894
|
+
if (!fs$1.existsSync(cwd)) {
|
|
2895
|
+
logger.logger.warn(`Warning: cwd does not exist: ${cwd}`);
|
|
2896
|
+
}
|
|
2897
|
+
logger.logger.groupEnd();
|
|
2898
|
+
try {
|
|
2899
|
+
// Validate caller-provided Bazel filesystem settings before invoking Bazel.
|
|
2900
|
+
if (opts.bazelOutputBase) {
|
|
2901
|
+
validateOutputBase(opts.bazelOutputBase, opts.cwd);
|
|
2902
|
+
}
|
|
2903
|
+
// Java must be available before rules_jvm_external/Coursier runs;
|
|
2904
|
+
// python shim follows so its augmented PATH inherits the JDK prefix.
|
|
2905
|
+
ensureJavaOnPath();
|
|
2906
|
+
const shim = await provisionPythonShim();
|
|
2907
|
+
const baseEnv = shim.augmentedEnv ?? opts.env;
|
|
2908
|
+
|
|
2909
|
+
// Step 1: workspace detection.
|
|
2910
|
+
const mode = detectWorkspaceMode(cwd);
|
|
2911
|
+
logger.logger.info(`Workspace mode: bzlmod=${mode.bzlmod} workspace=${mode.workspace}`);
|
|
2912
|
+
const invocationFlags = getBazelInvocationFlags(mode);
|
|
2913
|
+
|
|
2914
|
+
// Step 2: bazel binary resolution.
|
|
2915
|
+
const bin = await resolveBazelBinary(opts.bin);
|
|
2916
|
+
logger.logger.info(`Using bazel: ${bin}`);
|
|
2917
|
+
if (verbose) {
|
|
2918
|
+
logger.logger.log('[VERBOSE] resolved options:', {
|
|
2919
|
+
bin,
|
|
2920
|
+
bazelRc: opts.bazelRc ?? '(unset)',
|
|
2921
|
+
bazelOutputBase: opts.bazelOutputBase ?? '(unset)',
|
|
2922
|
+
bazelFlags: opts.bazelFlags ?? '(unset)',
|
|
2923
|
+
invocationFlags
|
|
2924
|
+
});
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
// Step 3: build the shared query options object.
|
|
2928
|
+
const queryOpts = {
|
|
2929
|
+
bin,
|
|
2930
|
+
cwd,
|
|
2931
|
+
invocationFlags,
|
|
2932
|
+
...(opts.bazelRc ? {
|
|
2933
|
+
bazelRc: opts.bazelRc
|
|
2934
|
+
} : {}),
|
|
2935
|
+
...(opts.bazelFlags ? {
|
|
2936
|
+
bazelFlags: opts.bazelFlags
|
|
2937
|
+
} : {}),
|
|
2938
|
+
...(opts.bazelOutputBase ? {
|
|
2939
|
+
bazelOutputBase: opts.bazelOutputBase
|
|
2940
|
+
} : {}),
|
|
2941
|
+
...(baseEnv ? {
|
|
2942
|
+
env: baseEnv
|
|
2943
|
+
} : {}),
|
|
2944
|
+
verbose
|
|
2945
|
+
};
|
|
2946
|
+
|
|
2947
|
+
// Step 4: discover validated Maven repos via the two-step recipe.
|
|
2948
|
+
// Bzlmod has a native visible-repository surface; prefer that over static
|
|
2949
|
+
// MODULE.bazel parsing and keep bounded parsing as the legacy/fallback path.
|
|
2950
|
+
let nativeCandidates;
|
|
2951
|
+
if (mode.bzlmod) {
|
|
2952
|
+
const visibleRepos = await runBazelModShowVisibleRepos(queryOpts);
|
|
2953
|
+
if (visibleRepos.code === 0) {
|
|
2954
|
+
nativeCandidates = parseVisibleRepoCandidates(visibleRepos.stdout);
|
|
2955
|
+
if (verbose) {
|
|
2956
|
+
logger.logger.log('[VERBOSE] Bzlmod visible repo candidates:', nativeCandidates);
|
|
2957
|
+
}
|
|
2958
|
+
} else if (verbose) {
|
|
2959
|
+
logger.logger.log('[VERBOSE] bazel mod show_repo failed; falling back to static candidate parsing:', visibleRepos.stderr);
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
// Returns Map<repoName, probeStdout> so extraction can reuse the probe
|
|
2963
|
+
// output and skip running an identical bazel-query a second time.
|
|
2964
|
+
const probe = buildProbeFor(queryOpts);
|
|
2965
|
+
const repos = await discoverMavenRepos(cwd, probe, nativeCandidates, verbose);
|
|
2966
|
+
const repoNames = Array.from(repos.keys());
|
|
2967
|
+
logger.logger.info(`Discovered ${repos.size} Maven repo(s): ${repoNames.join(', ') || '(none)'}`);
|
|
2968
|
+
|
|
2969
|
+
// Step 5: extract artifacts from each repo (preferring unsorted_deps.json).
|
|
2970
|
+
const allArtifacts = [];
|
|
2971
|
+
for (const [repo, probeStdout] of repos) {
|
|
2972
|
+
// eslint-disable-next-line no-await-in-loop
|
|
2973
|
+
const artifacts = await extractFromOneRepo(repo, queryOpts, probeStdout);
|
|
2974
|
+
allArtifacts.push(...artifacts);
|
|
2975
|
+
logger.logger.info(`@${repo}: ${artifacts.length} artifact(s)`);
|
|
2976
|
+
}
|
|
2977
|
+
|
|
2978
|
+
// Step 6: normalize to maven_install.json shape.
|
|
2979
|
+
const normalized = normalizeToMavenInstallJson(allArtifacts);
|
|
2980
|
+
|
|
2981
|
+
// Step 7: write outputs.
|
|
2982
|
+
// Standalone output writes directly to `out`; auto-manifest uses a sibling directory
|
|
2983
|
+
// to avoid colliding with a repo's checked-in rules_jvm_external lockfile and
|
|
2984
|
+
// to avoid repo-root gitignore patterns such as `/maven_install.json`.
|
|
2985
|
+
const layout = opts.outLayout ?? 'standalone';
|
|
2986
|
+
const manifestDir = layout === 'flat' ? path.join(out, '.socket-auto-manifest') : out;
|
|
2987
|
+
fs$1.mkdirSync(manifestDir, {
|
|
2988
|
+
recursive: true
|
|
2989
|
+
});
|
|
2990
|
+
const manifestPath = path.join(manifestDir, 'maven_install.json');
|
|
2991
|
+
await fs$1.promises.writeFile(manifestPath, JSON.stringify(normalized, null, 2), 'utf8');
|
|
2992
|
+
if (verbose) {
|
|
2993
|
+
logger.logger.log('[VERBOSE] outputs:', {
|
|
2994
|
+
artifactCount: allArtifacts.length,
|
|
2995
|
+
generatedManifest: path.relative(out, manifestPath),
|
|
2996
|
+
layout,
|
|
2997
|
+
manifest: manifestPath,
|
|
2998
|
+
mavenRepos: repoNames,
|
|
2999
|
+
tool: 'socket manifest bazel',
|
|
3000
|
+
workspace: {
|
|
3001
|
+
bzlmod: mode.bzlmod,
|
|
3002
|
+
legacyWorkspace: mode.workspace
|
|
3003
|
+
}
|
|
3004
|
+
});
|
|
3005
|
+
}
|
|
3006
|
+
if (!allArtifacts.length) {
|
|
3007
|
+
process.exitCode = 1;
|
|
3008
|
+
logger.logger.fail('No Maven artifacts extracted. See warnings above.');
|
|
3009
|
+
return {
|
|
3010
|
+
artifactCount: 0,
|
|
3011
|
+
manifestPath,
|
|
3012
|
+
ok: false
|
|
3013
|
+
};
|
|
3014
|
+
}
|
|
3015
|
+
logger.logger.success(`Wrote ${allArtifacts.length} artifact(s) to ${path.relative(cwd, manifestPath)}.`);
|
|
3016
|
+
return {
|
|
3017
|
+
artifactCount: allArtifacts.length,
|
|
3018
|
+
manifestPath,
|
|
3019
|
+
ok: true
|
|
3020
|
+
};
|
|
3021
|
+
} catch (e) {
|
|
3022
|
+
process.exitCode = 1;
|
|
3023
|
+
// Always surface the error message; users should not have to
|
|
3024
|
+
// re-run a multi-minute bazel build with --verbose just to see whether
|
|
3025
|
+
// the failure was a missing dependency, permission error, or network blip.
|
|
3026
|
+
logger.logger.fail(`Unexpected error in bazel2maven: ${utils.getErrorCause(e)}`);
|
|
3027
|
+
if (verbose) {
|
|
3028
|
+
logger.logger.group('[VERBOSE] error:');
|
|
3029
|
+
logger.logger.log(e);
|
|
3030
|
+
logger.logger.groupEnd();
|
|
3031
|
+
} else {
|
|
3032
|
+
logger.logger.info('Re-run with --verbose for the full stack.');
|
|
3033
|
+
}
|
|
3034
|
+
return {
|
|
3035
|
+
artifactCount: 0,
|
|
3036
|
+
ok: false
|
|
3037
|
+
};
|
|
3038
|
+
}
|
|
1888
3039
|
}
|
|
1889
3040
|
|
|
1890
3041
|
async function convertGradleToMaven({
|
|
@@ -2326,6 +3477,7 @@ async function generateAutoManifest({
|
|
|
2326
3477
|
verbose
|
|
2327
3478
|
}) {
|
|
2328
3479
|
const sockJson = utils.readOrDefaultSocketJson(cwd);
|
|
3480
|
+
const generatedFiles = [];
|
|
2329
3481
|
if (verbose) {
|
|
2330
3482
|
logger.logger.info(`Using this ${constants.SOCKET_JSON} for defaults:`, sockJson);
|
|
2331
3483
|
}
|
|
@@ -2362,6 +3514,32 @@ async function generateAutoManifest({
|
|
|
2362
3514
|
verbose: Boolean(sockJson.defaults?.manifest?.conda?.verbose)
|
|
2363
3515
|
});
|
|
2364
3516
|
}
|
|
3517
|
+
if (!sockJson?.defaults?.manifest?.bazel?.disabled && detected.bazel) {
|
|
3518
|
+
const bazelConfig = sockJson?.defaults?.manifest?.bazel;
|
|
3519
|
+
logger.logger.log('Detected a Bazel workspace, extracting Maven dependencies via bazel query...');
|
|
3520
|
+
const bazelResult = await extractBazelToMaven({
|
|
3521
|
+
bazelFlags: bazelConfig?.bazelFlags,
|
|
3522
|
+
bazelOutputBase: bazelConfig?.bazelOutputBase,
|
|
3523
|
+
bazelRc: bazelConfig?.bazelRc,
|
|
3524
|
+
bin: bazelConfig?.bazel ?? bazelConfig?.bin,
|
|
3525
|
+
cwd,
|
|
3526
|
+
// Auto-manifest writes into a sibling directory instead of the repo root
|
|
3527
|
+
// so scan discovery can pick it up without colliding with a checked-in
|
|
3528
|
+
// rules_jvm_external lockfile or repo-root gitignore patterns.
|
|
3529
|
+
out: bazelConfig?.out ?? cwd,
|
|
3530
|
+
outLayout: 'flat',
|
|
3531
|
+
verbose: Boolean(bazelConfig?.verbose) || verbose
|
|
3532
|
+
});
|
|
3533
|
+
if (!bazelResult.ok) {
|
|
3534
|
+
throw new Error('Bazel auto-manifest generation failed');
|
|
3535
|
+
}
|
|
3536
|
+
if (bazelResult.manifestPath) {
|
|
3537
|
+
generatedFiles.push(bazelResult.manifestPath);
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
return {
|
|
3541
|
+
generatedFiles
|
|
3542
|
+
};
|
|
2365
3543
|
}
|
|
2366
3544
|
|
|
2367
3545
|
// Keys for CDX and SPDX in the supported files response.
|
|
@@ -2414,6 +3592,7 @@ async function handleCreateNewScan({
|
|
|
2414
3592
|
tmp,
|
|
2415
3593
|
workspace
|
|
2416
3594
|
}) {
|
|
3595
|
+
let scanTargets = targets;
|
|
2417
3596
|
require$$9.debugFn('notice', `Creating new scan for ${orgSlug}/${workspace ? `${workspace}/` : ''}${repoName}`);
|
|
2418
3597
|
require$$9.debugDir('inspect', {
|
|
2419
3598
|
autoManifest,
|
|
@@ -2438,12 +3617,15 @@ async function handleCreateNewScan({
|
|
|
2438
3617
|
require$$9.debugDir('inspect', {
|
|
2439
3618
|
detected
|
|
2440
3619
|
});
|
|
2441
|
-
await generateAutoManifest({
|
|
3620
|
+
const autoManifestResult = await generateAutoManifest({
|
|
2442
3621
|
detected,
|
|
2443
3622
|
cwd,
|
|
2444
3623
|
outputKind,
|
|
2445
3624
|
verbose: false
|
|
2446
3625
|
});
|
|
3626
|
+
if (autoManifestResult.generatedFiles.length) {
|
|
3627
|
+
scanTargets = Array.from(new Set([...targets, ...autoManifestResult.generatedFiles]));
|
|
3628
|
+
}
|
|
2447
3629
|
logger.logger.info('Auto-generation finished. Proceeding with Scan creation.');
|
|
2448
3630
|
}
|
|
2449
3631
|
const {
|
|
@@ -2478,7 +3660,7 @@ async function handleCreateNewScan({
|
|
|
2478
3660
|
reachabilityOptions: reach,
|
|
2479
3661
|
target: targets[0]
|
|
2480
3662
|
});
|
|
2481
|
-
const packagePaths = await utils.getPackageFilesForScan(
|
|
3663
|
+
const packagePaths = await utils.getPackageFilesForScan(scanTargets, supportedFiles, {
|
|
2482
3664
|
additionalIgnores: additionalScaIgnores,
|
|
2483
3665
|
config: socketConfig,
|
|
2484
3666
|
cwd
|
|
@@ -2669,7 +3851,7 @@ async function handleCi(autoManifest) {
|
|
|
2669
3851
|
});
|
|
2670
3852
|
}
|
|
2671
3853
|
|
|
2672
|
-
const config$
|
|
3854
|
+
const config$l = {
|
|
2673
3855
|
commandName: 'ci',
|
|
2674
3856
|
description: 'Alias for `socket scan create --report` (creates report and exits with error if unhealthy)',
|
|
2675
3857
|
hidden: false,
|
|
@@ -2687,7 +3869,7 @@ const config$k = {
|
|
|
2687
3869
|
$ ${command} [options]
|
|
2688
3870
|
|
|
2689
3871
|
Options
|
|
2690
|
-
${utils.getFlagListOutput(config$
|
|
3872
|
+
${utils.getFlagListOutput(config$l.flags)}
|
|
2691
3873
|
|
|
2692
3874
|
This command is intended to use in CI runs to allow automated systems to
|
|
2693
3875
|
accept or reject a current build. It will use the default org of the
|
|
@@ -2705,16 +3887,16 @@ const config$k = {
|
|
|
2705
3887
|
`
|
|
2706
3888
|
};
|
|
2707
3889
|
const cmdCI = {
|
|
2708
|
-
description: config$
|
|
2709
|
-
hidden: config$
|
|
2710
|
-
run: run$
|
|
3890
|
+
description: config$l.description,
|
|
3891
|
+
hidden: config$l.hidden,
|
|
3892
|
+
run: run$R
|
|
2711
3893
|
};
|
|
2712
|
-
async function run$
|
|
3894
|
+
async function run$R(argv, importMeta, {
|
|
2713
3895
|
parentName
|
|
2714
3896
|
}) {
|
|
2715
3897
|
const cli = utils.meowOrExit({
|
|
2716
3898
|
argv,
|
|
2717
|
-
config: config$
|
|
3899
|
+
config: config$l,
|
|
2718
3900
|
parentName,
|
|
2719
3901
|
importMeta
|
|
2720
3902
|
});
|
|
@@ -2957,9 +4139,9 @@ const hidden$v = false;
|
|
|
2957
4139
|
const cmdConfigAuto = {
|
|
2958
4140
|
description: description$D,
|
|
2959
4141
|
hidden: hidden$v,
|
|
2960
|
-
run: run$
|
|
4142
|
+
run: run$Q
|
|
2961
4143
|
};
|
|
2962
|
-
async function run$
|
|
4144
|
+
async function run$Q(argv, importMeta, {
|
|
2963
4145
|
parentName
|
|
2964
4146
|
}) {
|
|
2965
4147
|
const config = {
|
|
@@ -3063,7 +4245,7 @@ async function handleConfigGet({
|
|
|
3063
4245
|
await outputConfigGet(key, result, outputKind);
|
|
3064
4246
|
}
|
|
3065
4247
|
|
|
3066
|
-
const config$
|
|
4248
|
+
const config$k = {
|
|
3067
4249
|
commandName: 'get',
|
|
3068
4250
|
description: 'Get the value of a local CLI config item',
|
|
3069
4251
|
hidden: false,
|
|
@@ -3093,16 +4275,16 @@ ${utils.getSupportedConfigEntries().map(({
|
|
|
3093
4275
|
`
|
|
3094
4276
|
};
|
|
3095
4277
|
const cmdConfigGet = {
|
|
3096
|
-
description: config$
|
|
3097
|
-
hidden: config$
|
|
3098
|
-
run: run$
|
|
4278
|
+
description: config$k.description,
|
|
4279
|
+
hidden: config$k.hidden,
|
|
4280
|
+
run: run$P
|
|
3099
4281
|
};
|
|
3100
|
-
async function run$
|
|
4282
|
+
async function run$P(argv, importMeta, {
|
|
3101
4283
|
parentName
|
|
3102
4284
|
}) {
|
|
3103
4285
|
const cli = utils.meowOrExit({
|
|
3104
4286
|
argv,
|
|
3105
|
-
config: config$
|
|
4287
|
+
config: config$k,
|
|
3106
4288
|
importMeta,
|
|
3107
4289
|
parentName
|
|
3108
4290
|
});
|
|
@@ -3204,7 +4386,7 @@ async function outputConfigList({
|
|
|
3204
4386
|
}
|
|
3205
4387
|
}
|
|
3206
4388
|
|
|
3207
|
-
const config$
|
|
4389
|
+
const config$j = {
|
|
3208
4390
|
commandName: 'list',
|
|
3209
4391
|
description: 'Show all local CLI config items and their values',
|
|
3210
4392
|
hidden: false,
|
|
@@ -3229,16 +4411,16 @@ const config$i = {
|
|
|
3229
4411
|
`
|
|
3230
4412
|
};
|
|
3231
4413
|
const cmdConfigList = {
|
|
3232
|
-
description: config$
|
|
3233
|
-
hidden: config$
|
|
3234
|
-
run: run$
|
|
4414
|
+
description: config$j.description,
|
|
4415
|
+
hidden: config$j.hidden,
|
|
4416
|
+
run: run$O
|
|
3235
4417
|
};
|
|
3236
|
-
async function run$
|
|
4418
|
+
async function run$O(argv, importMeta, {
|
|
3237
4419
|
parentName
|
|
3238
4420
|
}) {
|
|
3239
4421
|
const cli = utils.meowOrExit({
|
|
3240
4422
|
argv,
|
|
3241
|
-
config: config$
|
|
4423
|
+
config: config$j,
|
|
3242
4424
|
importMeta,
|
|
3243
4425
|
parentName
|
|
3244
4426
|
});
|
|
@@ -3323,9 +4505,9 @@ const hidden$u = false;
|
|
|
3323
4505
|
const cmdConfigSet = {
|
|
3324
4506
|
description: description$C,
|
|
3325
4507
|
hidden: hidden$u,
|
|
3326
|
-
run: run$
|
|
4508
|
+
run: run$N
|
|
3327
4509
|
};
|
|
3328
|
-
async function run$
|
|
4510
|
+
async function run$N(argv, importMeta, {
|
|
3329
4511
|
parentName
|
|
3330
4512
|
}) {
|
|
3331
4513
|
const config = {
|
|
@@ -3450,9 +4632,9 @@ const hidden$t = false;
|
|
|
3450
4632
|
const cmdConfigUnset = {
|
|
3451
4633
|
description: description$B,
|
|
3452
4634
|
hidden: hidden$t,
|
|
3453
|
-
run: run$
|
|
4635
|
+
run: run$M
|
|
3454
4636
|
};
|
|
3455
|
-
async function run$
|
|
4637
|
+
async function run$M(argv, importMeta, {
|
|
3456
4638
|
parentName
|
|
3457
4639
|
}) {
|
|
3458
4640
|
const config = {
|
|
@@ -4650,7 +5832,7 @@ const hidden$s = false;
|
|
|
4650
5832
|
const cmdFix = {
|
|
4651
5833
|
description: description$z,
|
|
4652
5834
|
hidden: hidden$s,
|
|
4653
|
-
run: run$
|
|
5835
|
+
run: run$L
|
|
4654
5836
|
};
|
|
4655
5837
|
const generalFlags$2 = {
|
|
4656
5838
|
autopilot: {
|
|
@@ -4814,7 +5996,7 @@ const hiddenFlags = {
|
|
|
4814
5996
|
hidden: true
|
|
4815
5997
|
}
|
|
4816
5998
|
};
|
|
4817
|
-
async function run$
|
|
5999
|
+
async function run$L(argv, importMeta, {
|
|
4818
6000
|
parentName
|
|
4819
6001
|
}) {
|
|
4820
6002
|
const config = {
|
|
@@ -5131,7 +6313,7 @@ async function handleInstallCompletion(targetName) {
|
|
|
5131
6313
|
await outputInstallCompletion(result);
|
|
5132
6314
|
}
|
|
5133
6315
|
|
|
5134
|
-
const config$
|
|
6316
|
+
const config$i = {
|
|
5135
6317
|
commandName: 'completion',
|
|
5136
6318
|
description: 'Install bash completion for Socket CLI',
|
|
5137
6319
|
hidden: false,
|
|
@@ -5168,16 +6350,16 @@ const config$h = {
|
|
|
5168
6350
|
`
|
|
5169
6351
|
};
|
|
5170
6352
|
const cmdInstallCompletion = {
|
|
5171
|
-
description: config$
|
|
5172
|
-
hidden: config$
|
|
5173
|
-
run: run$
|
|
6353
|
+
description: config$i.description,
|
|
6354
|
+
hidden: config$i.hidden,
|
|
6355
|
+
run: run$K
|
|
5174
6356
|
};
|
|
5175
|
-
async function run$
|
|
6357
|
+
async function run$K(argv, importMeta, {
|
|
5176
6358
|
parentName
|
|
5177
6359
|
}) {
|
|
5178
6360
|
const cli = utils.meowOrExit({
|
|
5179
6361
|
argv,
|
|
5180
|
-
config: config$
|
|
6362
|
+
config: config$i,
|
|
5181
6363
|
parentName,
|
|
5182
6364
|
importMeta
|
|
5183
6365
|
});
|
|
@@ -5234,7 +6416,7 @@ async function handleCmdJson(cwd) {
|
|
|
5234
6416
|
await outputCmdJson(cwd);
|
|
5235
6417
|
}
|
|
5236
6418
|
|
|
5237
|
-
const config$
|
|
6419
|
+
const config$h = {
|
|
5238
6420
|
commandName: 'json',
|
|
5239
6421
|
description: `Display the \`${constants.SOCKET_JSON}\` that would be applied for target folder`,
|
|
5240
6422
|
hidden: true,
|
|
@@ -5253,16 +6435,16 @@ const config$g = {
|
|
|
5253
6435
|
`
|
|
5254
6436
|
};
|
|
5255
6437
|
const cmdJson = {
|
|
5256
|
-
description: config$
|
|
5257
|
-
hidden: config$
|
|
5258
|
-
run: run$
|
|
6438
|
+
description: config$h.description,
|
|
6439
|
+
hidden: config$h.hidden,
|
|
6440
|
+
run: run$J
|
|
5259
6441
|
};
|
|
5260
|
-
async function run$
|
|
6442
|
+
async function run$J(argv, importMeta, {
|
|
5261
6443
|
parentName
|
|
5262
6444
|
}) {
|
|
5263
6445
|
const cli = utils.meowOrExit({
|
|
5264
6446
|
argv,
|
|
5265
|
-
config: config$
|
|
6447
|
+
config: config$h,
|
|
5266
6448
|
parentName,
|
|
5267
6449
|
importMeta
|
|
5268
6450
|
});
|
|
@@ -5417,9 +6599,9 @@ const hidden$r = false;
|
|
|
5417
6599
|
const cmdLogin = {
|
|
5418
6600
|
description: description$x,
|
|
5419
6601
|
hidden: hidden$r,
|
|
5420
|
-
run: run$
|
|
6602
|
+
run: run$I
|
|
5421
6603
|
};
|
|
5422
|
-
async function run$
|
|
6604
|
+
async function run$I(argv, importMeta, {
|
|
5423
6605
|
parentName
|
|
5424
6606
|
}) {
|
|
5425
6607
|
const config = {
|
|
@@ -5497,7 +6679,7 @@ function attemptLogout() {
|
|
|
5497
6679
|
}
|
|
5498
6680
|
}
|
|
5499
6681
|
|
|
5500
|
-
const config$
|
|
6682
|
+
const config$g = {
|
|
5501
6683
|
commandName: 'logout',
|
|
5502
6684
|
description: 'Socket API logout',
|
|
5503
6685
|
hidden: false,
|
|
@@ -5515,16 +6697,16 @@ const config$f = {
|
|
|
5515
6697
|
`
|
|
5516
6698
|
};
|
|
5517
6699
|
const cmdLogout = {
|
|
5518
|
-
description: config$
|
|
5519
|
-
hidden: config$
|
|
5520
|
-
run: run$
|
|
6700
|
+
description: config$g.description,
|
|
6701
|
+
hidden: config$g.hidden,
|
|
6702
|
+
run: run$H
|
|
5521
6703
|
};
|
|
5522
|
-
async function run$
|
|
6704
|
+
async function run$H(argv, importMeta, {
|
|
5523
6705
|
parentName
|
|
5524
6706
|
}) {
|
|
5525
6707
|
const cli = utils.meowOrExit({
|
|
5526
6708
|
argv,
|
|
5527
|
-
config: config$
|
|
6709
|
+
config: config$g,
|
|
5528
6710
|
importMeta,
|
|
5529
6711
|
parentName
|
|
5530
6712
|
});
|
|
@@ -5837,7 +7019,7 @@ const yargsConfig = {
|
|
|
5837
7019
|
'usages-slices-file' // hidden
|
|
5838
7020
|
]
|
|
5839
7021
|
};
|
|
5840
|
-
const config$
|
|
7022
|
+
const config$f = {
|
|
5841
7023
|
commandName: 'cdxgen',
|
|
5842
7024
|
description: 'Run cdxgen for SBOM generation',
|
|
5843
7025
|
hidden: false,
|
|
@@ -5847,11 +7029,11 @@ const config$e = {
|
|
|
5847
7029
|
help: () => ''
|
|
5848
7030
|
};
|
|
5849
7031
|
const cmdManifestCdxgen = {
|
|
5850
|
-
description: config$
|
|
5851
|
-
hidden: config$
|
|
5852
|
-
run: run$
|
|
7032
|
+
description: config$f.description,
|
|
7033
|
+
hidden: config$f.hidden,
|
|
7034
|
+
run: run$G
|
|
5853
7035
|
};
|
|
5854
|
-
async function run$
|
|
7036
|
+
async function run$G(argv, importMeta, context) {
|
|
5855
7037
|
const {
|
|
5856
7038
|
parentName
|
|
5857
7039
|
} = {
|
|
@@ -5861,7 +7043,7 @@ async function run$F(argv, importMeta, context) {
|
|
|
5861
7043
|
const cli = utils.meowOrExit({
|
|
5862
7044
|
// Don't let meow take over --help.
|
|
5863
7045
|
argv: argv.filter(a => !utils.isHelpFlag(a)),
|
|
5864
|
-
config: config$
|
|
7046
|
+
config: config$f,
|
|
5865
7047
|
importMeta,
|
|
5866
7048
|
parentName
|
|
5867
7049
|
});
|
|
@@ -5934,6 +7116,181 @@ async function run$F(argv, importMeta, context) {
|
|
|
5934
7116
|
await spawnPromise;
|
|
5935
7117
|
}
|
|
5936
7118
|
|
|
7119
|
+
const config$e = {
|
|
7120
|
+
commandName: 'bazel',
|
|
7121
|
+
description: '[beta] Bazel JVM SBOM support — generate manifest files (`maven_install.json`) for a Bazel/Maven project',
|
|
7122
|
+
hidden: false,
|
|
7123
|
+
flags: {
|
|
7124
|
+
...flags.commonFlags,
|
|
7125
|
+
bazel: {
|
|
7126
|
+
type: 'string',
|
|
7127
|
+
description: 'Path to bazel/bazelisk binary; default: $(which bazelisk) || $(which bazel)'
|
|
7128
|
+
},
|
|
7129
|
+
bazelFlags: {
|
|
7130
|
+
type: 'string',
|
|
7131
|
+
description: 'Flags forwarded to every bazel invocation (single quoted string)'
|
|
7132
|
+
},
|
|
7133
|
+
bazelOutputBase: {
|
|
7134
|
+
type: 'string',
|
|
7135
|
+
description: 'Bazel --output_base for read-only-cache CI environments'
|
|
7136
|
+
},
|
|
7137
|
+
bazelRc: {
|
|
7138
|
+
type: 'string',
|
|
7139
|
+
description: 'Path to additional .bazelrc fragments forwarded to bazel'
|
|
7140
|
+
},
|
|
7141
|
+
out: {
|
|
7142
|
+
type: 'string',
|
|
7143
|
+
description: 'Output directory for generated manifests; default: ./.socket/bazel-manifests/'
|
|
7144
|
+
},
|
|
7145
|
+
verbose: {
|
|
7146
|
+
type: 'boolean',
|
|
7147
|
+
description: 'Stream bazel stdout/stderr'
|
|
7148
|
+
}
|
|
7149
|
+
},
|
|
7150
|
+
help: (command, config) => `
|
|
7151
|
+
Usage
|
|
7152
|
+
$ ${command} [options] [CWD=.]
|
|
7153
|
+
|
|
7154
|
+
Options
|
|
7155
|
+
${utils.getFlagListOutput(config.flags)}
|
|
7156
|
+
|
|
7157
|
+
[beta] Generates Bazel JVM SBOM manifests (\`maven_install.json\`-shaped)
|
|
7158
|
+
by running \`bazel query\` against discovered Maven repos. Output is
|
|
7159
|
+
consumed by \`socket scan create\`'s server-side parser.
|
|
7160
|
+
|
|
7161
|
+
Note: this command generates Maven dependency manifests for Bazel JVM
|
|
7162
|
+
workspaces. It does not run reachability analysis.
|
|
7163
|
+
|
|
7164
|
+
To generate AND upload in one step, use \`socket scan create --auto-manifest\`
|
|
7165
|
+
instead — it detects Bazel workspaces, runs the same extraction, and uploads
|
|
7166
|
+
the result. This subcommand is for generation only.
|
|
7167
|
+
|
|
7168
|
+
Examples
|
|
7169
|
+
$ ${command} .
|
|
7170
|
+
$ ${command} --bazel=/usr/local/bin/bazelisk .
|
|
7171
|
+
`
|
|
7172
|
+
};
|
|
7173
|
+
const cmdManifestBazel = {
|
|
7174
|
+
description: config$e.description,
|
|
7175
|
+
hidden: config$e.hidden,
|
|
7176
|
+
run: run$F
|
|
7177
|
+
};
|
|
7178
|
+
async function run$F(argv, importMeta, {
|
|
7179
|
+
parentName
|
|
7180
|
+
}) {
|
|
7181
|
+
const cli = utils.meowOrExit({
|
|
7182
|
+
argv,
|
|
7183
|
+
config: config$e,
|
|
7184
|
+
importMeta,
|
|
7185
|
+
parentName
|
|
7186
|
+
});
|
|
7187
|
+
const {
|
|
7188
|
+
json = false,
|
|
7189
|
+
markdown = false
|
|
7190
|
+
} = cli.flags;
|
|
7191
|
+
const dryRun = !!cli.flags['dryRun'];
|
|
7192
|
+
|
|
7193
|
+
// TODO: Implement json/md further.
|
|
7194
|
+
const outputKind = utils.getOutputKind(json, markdown);
|
|
7195
|
+
let [cwd = '.'] = cli.input;
|
|
7196
|
+
// Note: path.resolve vs .join:
|
|
7197
|
+
// If given path is absolute then cwd should not affect it.
|
|
7198
|
+
cwd = path.resolve(process.cwd(), cwd);
|
|
7199
|
+
const sockJson = utils.readOrDefaultSocketJson(cwd);
|
|
7200
|
+
require$$9.debugFn('inspect', `override: ${constants.SOCKET_JSON} bazel`, sockJson?.defaults?.manifest?.bazel);
|
|
7201
|
+
let {
|
|
7202
|
+
bazel,
|
|
7203
|
+
bazelFlags,
|
|
7204
|
+
bazelOutputBase,
|
|
7205
|
+
bazelRc,
|
|
7206
|
+
out,
|
|
7207
|
+
verbose
|
|
7208
|
+
} = cli.flags;
|
|
7209
|
+
|
|
7210
|
+
// Set defaults for any flag/arg that is not given. Check socket.json first.
|
|
7211
|
+
if (!bazel) {
|
|
7212
|
+
const defaultBazel = sockJson.defaults?.manifest?.bazel?.bazel ?? sockJson.defaults?.manifest?.bazel?.bin;
|
|
7213
|
+
if (defaultBazel) {
|
|
7214
|
+
bazel = defaultBazel;
|
|
7215
|
+
logger.logger.info(`Using default --bazel from ${constants.SOCKET_JSON}:`, bazel);
|
|
7216
|
+
}
|
|
7217
|
+
// Otherwise leave undefined; resolveBazelBinary performs the PATH
|
|
7218
|
+
// lookup for bazelisk/bazel.
|
|
7219
|
+
}
|
|
7220
|
+
if (!bazelFlags) {
|
|
7221
|
+
if (sockJson.defaults?.manifest?.bazel?.bazelFlags) {
|
|
7222
|
+
bazelFlags = sockJson.defaults?.manifest?.bazel?.bazelFlags;
|
|
7223
|
+
logger.logger.info(`Using default --bazel-flags from ${constants.SOCKET_JSON}:`, bazelFlags);
|
|
7224
|
+
} else {
|
|
7225
|
+
bazelFlags = '';
|
|
7226
|
+
}
|
|
7227
|
+
}
|
|
7228
|
+
if (!bazelOutputBase) {
|
|
7229
|
+
if (sockJson.defaults?.manifest?.bazel?.bazelOutputBase) {
|
|
7230
|
+
bazelOutputBase = sockJson.defaults?.manifest?.bazel?.bazelOutputBase;
|
|
7231
|
+
logger.logger.info(`Using default --bazel-output-base from ${constants.SOCKET_JSON}:`, bazelOutputBase);
|
|
7232
|
+
}
|
|
7233
|
+
}
|
|
7234
|
+
if (!bazelRc) {
|
|
7235
|
+
if (sockJson.defaults?.manifest?.bazel?.bazelRc) {
|
|
7236
|
+
bazelRc = sockJson.defaults?.manifest?.bazel?.bazelRc;
|
|
7237
|
+
logger.logger.info(`Using default --bazel-rc from ${constants.SOCKET_JSON}:`, bazelRc);
|
|
7238
|
+
}
|
|
7239
|
+
}
|
|
7240
|
+
if (!out) {
|
|
7241
|
+
if (sockJson.defaults?.manifest?.bazel?.out) {
|
|
7242
|
+
out = sockJson.defaults?.manifest?.bazel?.out;
|
|
7243
|
+
logger.logger.info(`Using default --out from ${constants.SOCKET_JSON}:`, out);
|
|
7244
|
+
} else {
|
|
7245
|
+
out = path.join(cwd, '.socket', 'bazel-manifests');
|
|
7246
|
+
}
|
|
7247
|
+
}
|
|
7248
|
+
if (verbose === undefined) {
|
|
7249
|
+
if (sockJson.defaults?.manifest?.bazel?.verbose !== undefined) {
|
|
7250
|
+
verbose = sockJson.defaults?.manifest?.bazel?.verbose;
|
|
7251
|
+
logger.logger.info(`Using default --verbose from ${constants.SOCKET_JSON}:`, verbose);
|
|
7252
|
+
} else {
|
|
7253
|
+
verbose = false;
|
|
7254
|
+
}
|
|
7255
|
+
}
|
|
7256
|
+
if (verbose) {
|
|
7257
|
+
logger.logger.group('- ', parentName, config$e.commandName, ':');
|
|
7258
|
+
logger.logger.group('- flags:', cli.flags);
|
|
7259
|
+
logger.logger.groupEnd();
|
|
7260
|
+
logger.logger.log('- input:', cli.input);
|
|
7261
|
+
logger.logger.groupEnd();
|
|
7262
|
+
}
|
|
7263
|
+
const wasValidInput = utils.checkCommandInput(outputKind, {
|
|
7264
|
+
nook: true,
|
|
7265
|
+
test: cli.input.length <= 1,
|
|
7266
|
+
message: 'Can only accept one DIR (make sure to escape spaces!)',
|
|
7267
|
+
fail: 'received ' + cli.input.length
|
|
7268
|
+
});
|
|
7269
|
+
if (!wasValidInput) {
|
|
7270
|
+
return;
|
|
7271
|
+
}
|
|
7272
|
+
if (verbose) {
|
|
7273
|
+
logger.logger.group();
|
|
7274
|
+
logger.logger.info('- cwd:', cwd);
|
|
7275
|
+
logger.logger.info('- bazel bin:', bazel);
|
|
7276
|
+
logger.logger.info('- out:', out);
|
|
7277
|
+
logger.logger.groupEnd();
|
|
7278
|
+
}
|
|
7279
|
+
if (dryRun) {
|
|
7280
|
+
logger.logger.log(constants.default.DRY_RUN_BAILING_NOW);
|
|
7281
|
+
return;
|
|
7282
|
+
}
|
|
7283
|
+
await extractBazelToMaven({
|
|
7284
|
+
bazelFlags: bazelFlags,
|
|
7285
|
+
bazelOutputBase: bazelOutputBase,
|
|
7286
|
+
bazelRc: bazelRc,
|
|
7287
|
+
bin: bazel,
|
|
7288
|
+
cwd,
|
|
7289
|
+
out: out,
|
|
7290
|
+
verbose: Boolean(verbose)
|
|
7291
|
+
});
|
|
7292
|
+
}
|
|
7293
|
+
|
|
5937
7294
|
const config$d = {
|
|
5938
7295
|
commandName: 'auto',
|
|
5939
7296
|
description: 'Auto-detect build and attempt to generate manifest file',
|
|
@@ -7167,6 +8524,7 @@ async function run$y(argv, importMeta, {
|
|
|
7167
8524
|
importMeta,
|
|
7168
8525
|
subcommands: {
|
|
7169
8526
|
auto: cmdManifestAuto,
|
|
8527
|
+
bazel: cmdManifestBazel,
|
|
7170
8528
|
cdxgen: cmdManifestCdxgen,
|
|
7171
8529
|
conda: cmdManifestConda,
|
|
7172
8530
|
gradle: cmdManifestGradle,
|
|
@@ -15865,5 +17223,5 @@ process.on('unhandledRejection', async (reason, promise) => {
|
|
|
15865
17223
|
// eslint-disable-next-line n/no-process-exit
|
|
15866
17224
|
process.exit(1);
|
|
15867
17225
|
});
|
|
15868
|
-
//# debugId=
|
|
17226
|
+
//# debugId=18cb97b2-20ce-409e-aec7-5485ca050fb0
|
|
15869
17227
|
//# sourceMappingURL=cli.js.map
|