Skip to content

Commit 4009c0c

Browse files
committed
fix(bazel): keep pypi generation explicit
- remove Bazel PyPI auto-manifest config and dispatch - drop live no-ecosystem constructed test and clean PyPI type imports
1 parent aa31655 commit 4009c0c

8 files changed

Lines changed: 32 additions & 191 deletions

src/commands/manifest/README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ socket manifest bazel [options] [DIR=.]
4343

4444
> **Upload**: This subcommand only generates manifests. To generate and
4545
> upload in one step, use `socket scan create --auto-manifest .` — it
46-
> detects the workspace, generates Bazel Maven manifests by default, and
47-
> uploads the result. Bazel PyPI auto-manifest generation requires an explicit
48-
> `defaults.manifest.bazel.ecosystem` config value that includes `pypi`.
46+
> detects the workspace, generates Bazel Maven manifests, and uploads the
47+
> result. Generate Bazel PyPI manifests explicitly with `socket manifest bazel
48+
> --ecosystem pypi`, then scan the generated output with `socket scan create`.
4949
5050
### Examples
5151

@@ -89,7 +89,7 @@ When `--ecosystem pypi` is selected, the command:
8989
`requirements.txt` cannot represent both versions, and silently
9090
picking one would produce a misleading SBOM.
9191

92-
### Unsupported PyPI Forms (Phase 02.1)
92+
### Unsupported PyPI Forms
9393

9494
The PyPI extractor is intentionally narrow in this phase:
9595

@@ -101,8 +101,9 @@ The PyPI extractor is intentionally narrow in this phase:
101101
- **Private corpus validation** requires authenticated GitHub access.
102102
When credentials are unavailable, the bazel-bench harness's private
103103
PyPI case skips cleanly with a distinct reason rather than failing.
104-
- **Whole-repo extraction.** Phase 02.1 is Tier 2 whole-repo scope.
105-
Per-target PyPI slicing is deferred to Phase 4.
104+
- **Whole-repo extraction.** The initial PyPI implementation emits one
105+
whole-workspace manifest. Per-target PyPI slicing is not currently
106+
supported.
106107

107108
### Cross-Language Edges
108109

src/commands/manifest/bazel/cmd-manifest-bazel.mts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -187,23 +187,10 @@ async function run(
187187
sockJson?.defaults?.manifest?.bazel,
188188
)
189189

190-
let { bazel, bazelFlags, bazelOutputBase, bazelRc, ecosystem, out, verbose } =
191-
cli.flags
190+
const { ecosystem } = cli.flags
191+
let { bazel, bazelFlags, bazelOutputBase, bazelRc, out, verbose } = cli.flags
192192

193193
// Set defaults for any flag/arg that is not given. Check socket.json first.
194-
// The meow flag is isMultiple: true, so cli.flags.ecosystem is
195-
// string[] | undefined. The SocketJson schema allows either a single
196-
// string or an array, so normalize a string default to a one-element
197-
// array before assigning.
198-
if (!ecosystem) {
199-
const rawEcosystem = sockJson.defaults?.manifest?.bazel?.ecosystem
200-
if (rawEcosystem) {
201-
ecosystem = Array.isArray(rawEcosystem)
202-
? [...rawEcosystem]
203-
: [rawEcosystem as string]
204-
logger.info(`Using default --ecosystem from ${SOCKET_JSON}:`, ecosystem)
205-
}
206-
}
207194
if (!bazel) {
208195
const defaultBazel =
209196
sockJson.defaults?.manifest?.bazel?.bazel ??

src/commands/manifest/bazel/extract_bazel_to_pypi.constructed.test.mts

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -125,32 +125,3 @@ describe.skipIf(isSandboxed())(
125125
}, 60000)
126126
},
127127
)
128-
129-
describe('extract_bazel_to_pypi — sandbox fallback', () => {
130-
it('returns noEcosystemFound when explicit mode has no Python rules', async () => {
131-
const { writeFileSync } = await import('node:fs')
132-
const noRulesDir = mkdtempSync(path.join(os.tmpdir(), 'no-python-rules-'))
133-
try {
134-
// Write a minimal MODULE.bazel so workspace detection passes.
135-
writeFileSync(
136-
path.join(noRulesDir, 'MODULE.bazel'),
137-
'module(name="test")\n',
138-
'utf8',
139-
)
140-
const result = await extractBazelToPypi({
141-
bazelFlags: undefined,
142-
bazelOutputBase: undefined,
143-
bazelRc: undefined,
144-
bin: undefined,
145-
cwd: noRulesDir,
146-
out: noRulesDir,
147-
verbose: false,
148-
explicitEcosystem: true,
149-
})
150-
expect(result.noEcosystemFound).toBe(true)
151-
expect(result.ok).toBe(false)
152-
} finally {
153-
rmSync(noRulesDir, { recursive: true, force: true })
154-
}
155-
}, 60000)
156-
})

src/commands/manifest/bazel/extract_bazel_to_pypi.mts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ import {
3333
import { getErrorCause } from '../../../utils/errors.mts'
3434

3535
import type { PypiHubCandidate } from './bazel-pypi-discovery.mts'
36+
import type {
37+
ExtractedPypiPackage,
38+
ReachedPypiLabel,
39+
} from './bazel-pypi-parser.mts'
3640
import type { BazelQueryOptions } from './bazel-query-runner.mts'
3741

3842
export type ExtractBazelToPypiOptions = {
@@ -306,10 +310,7 @@ async function resolveHubLockfile(
306310
},
307311
cwd: string,
308312
verbose: boolean,
309-
): Promise<
310-
| Map<string, import('./bazel-pypi-parser.mts').ExtractedPypiPackage>
311-
| undefined
312-
> {
313+
): Promise<Map<string, ExtractedPypiPackage> | undefined> {
313314
const resolved =
314315
hubInfo.requirementsLockPath ??
315316
resolveRequirementsLockPath(hubInfo.requirementsLockLabel, cwd)
@@ -331,7 +332,7 @@ async function queryReachedPypiLabels(
331332
hubName: string,
332333
queryOpts: BazelQueryOptions,
333334
verbose: boolean,
334-
): Promise<Array<import('./bazel-pypi-parser.mts').ReachedPypiLabel>> {
335+
): Promise<ReachedPypiLabel[]> {
335336
const queryStr = 'deps(kind("py_library|py_binary|py_test", //...))'
336337
const result = await runBazelQuery(queryStr, queryOpts, 'label')
337338
if (result.code !== 0) {
@@ -350,16 +351,11 @@ async function queryReachedPypiLabels(
350351
// entries. For each reached label, if the lockfile missed it, resolve the
351352
// actual target via `--output=build` and extract pypi_name/pypi_version.
352353
async function buildSpokeTagLookup(
353-
reached: Array<import('./bazel-pypi-parser.mts').ReachedPypiLabel>,
354+
reached: ReachedPypiLabel[],
354355
queryOpts: BazelQueryOptions,
355356
verbose: boolean,
356-
): Promise<
357-
Map<string, import('./bazel-pypi-parser.mts').ExtractedPypiPackage>
358-
> {
359-
const lookup = new Map<
360-
string,
361-
import('./bazel-pypi-parser.mts').ExtractedPypiPackage
362-
>()
357+
): Promise<Map<string, ExtractedPypiPackage>> {
358+
const lookup = new Map<string, ExtractedPypiPackage>()
363359
for (const label of reached) {
364360
// Only query the spoke if we haven't already resolved it.
365361
if (lookup.has(label.normalizedName)) {

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs'
1+
import {
2+
existsSync,
3+
mkdtempSync,
4+
readFileSync,
5+
rmSync,
6+
writeFileSync,
7+
} from 'node:fs'
28
import os from 'node:os'
39
import path from 'node:path'
410

@@ -128,7 +134,6 @@ describe('extractBazelToPypi', () => {
128134

129135
// Create a requirements_lock.txt in the temp dir.
130136
const lockPath = path.join(tmp, 'requirements_lock.txt')
131-
const { writeFileSync } = await import('node:fs')
132137
writeFileSync(lockPath, 'requests==2.33.1\n', 'utf8')
133138

134139
const result = await extractBazelToPypi({
@@ -178,7 +183,6 @@ describe('extractBazelToPypi', () => {
178183
stderr: '',
179184
})
180185

181-
const { writeFileSync } = await import('node:fs')
182186
writeFileSync(
183187
path.join(tmp, 'requirements_lock.txt'),
184188
'requests==2.33.1\n',
@@ -273,7 +277,6 @@ describe('extractBazelToPypi', () => {
273277
stderr: '',
274278
})
275279

276-
const { writeFileSync } = await import('node:fs')
277280
writeFileSync(
278281
path.join(tmp, 'requirements_lock.txt'),
279282
'requests==2.33.1\n',
@@ -324,7 +327,6 @@ describe('extractBazelToPypi', () => {
324327
stderr: '',
325328
})
326329

327-
const { writeFileSync } = await import('node:fs')
328330
writeFileSync(
329331
path.join(tmp, 'requirements_lock.txt'),
330332
'charset-normalizer==3.4.7\n',
@@ -389,7 +391,6 @@ describe('extractBazelToPypi', () => {
389391
stderr: '',
390392
})
391393

392-
const { writeFileSync } = await import('node:fs')
393394
writeFileSync(
394395
path.join(tmp, 'requirements_lock.txt'),
395396
'requests==2.33.1\n',
@@ -426,7 +427,6 @@ describe('extractBazelToPypi', () => {
426427
]),
427428
)
428429

429-
const { writeFileSync } = await import('node:fs')
430430
writeFileSync(
431431
path.join(tmp, 'requirements_lock.txt'),
432432
'foo-bar==1.0.0\nFoo_Bar==2.0.0\n',
@@ -469,7 +469,6 @@ describe('extractBazelToPypi', () => {
469469
stderr: '',
470470
})
471471

472-
const { writeFileSync } = await import('node:fs')
473472
writeFileSync(
474473
path.join(tmp, 'requirements_lock.txt'),
475474
'requests==2.33.1\n',

src/commands/manifest/generate_auto_manifest.mts

Lines changed: 5 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import path from 'node:path'
33
import { logger } from '@socketsecurity/registry/lib/logger'
44

55
import { extractBazelToMaven } from './bazel/extract_bazel_to_maven.mts'
6-
import { extractBazelToPypi } from './bazel/extract_bazel_to_pypi.mts'
76
import { convertGradleToMaven } from './convert_gradle_to_maven.mts'
87
import { convertSbtToMaven } from './convert_sbt_to_maven.mts'
98
import { handleManifestConda } from './handle-manifest-conda.mts'
@@ -87,14 +86,6 @@ export async function generateAutoManifest({
8786

8887
if (!sockJson?.defaults?.manifest?.bazel?.disabled && detected.bazel) {
8988
const bazelConfig = sockJson?.defaults?.manifest?.bazel
90-
type EcosystemOutcome = {
91-
ecosystem: 'maven' | 'pypi'
92-
ok: boolean
93-
noEcosystemFound?: boolean
94-
hardFailure?: boolean
95-
manifestPath?: string | undefined
96-
}
97-
const outcomes: EcosystemOutcome[] = []
9889

9990
logger.log(
10091
'Detected a Bazel workspace, extracting Maven dependencies via bazel query...',
@@ -109,63 +100,15 @@ export async function generateAutoManifest({
109100
outLayout: 'flat',
110101
verbose: Boolean(bazelConfig?.verbose) || verbose,
111102
})
112-
outcomes.push({
113-
ecosystem: 'maven',
114-
noEcosystemFound: Boolean(mavenResult.noEcosystemFound),
115-
ok: mavenResult.ok,
116-
manifestPath: mavenResult.manifestPath,
117-
})
118-
119-
const configuredEcosystems = bazelConfig?.ecosystem
120-
const requestedEcosystems = Array.isArray(configuredEcosystems)
121-
? configuredEcosystems
122-
: configuredEcosystems
123-
? [configuredEcosystems]
124-
: []
125-
const shouldRunPypi = requestedEcosystems.includes('pypi')
126-
127-
if (shouldRunPypi) {
128-
logger.log('Extracting PyPI dependencies via bazel query...')
129-
const pypiResult = await extractBazelToPypi({
130-
bazelFlags: bazelConfig?.bazelFlags,
131-
bazelOutputBase: bazelConfig?.bazelOutputBase,
132-
bazelRc: bazelConfig?.bazelRc,
133-
bin: bazelConfig?.bazel ?? bazelConfig?.bin,
134-
cwd,
135-
explicitEcosystem: true,
136-
out: bazelConfig?.out ?? cwd,
137-
outLayout: 'flat',
138-
verbose: Boolean(bazelConfig?.verbose) || verbose,
139-
})
140-
outcomes.push({
141-
ecosystem: 'pypi',
142-
ok: pypiResult.ok,
143-
noEcosystemFound: Boolean(pypiResult.noEcosystemFound),
144-
manifestPath: pypiResult.manifestPath,
145-
})
146-
} else if (verbose) {
147-
logger.info(
148-
'Skipping Bazel PyPI auto-manifest extraction; set defaults.manifest.bazel.ecosystem to include "pypi" to opt in.',
149-
)
150-
}
151-
152-
// Auto-manifest outcome matrix: hard failures are fatal, no-discovery is
153-
// informational, and successes are returned only when nothing hard-failed.
154-
const successes = outcomes.filter(o => o.ok && o.manifestPath)
155-
const hardFailures = outcomes.filter(o => !o.ok && !o.noEcosystemFound)
156-
const noDiscoveries = outcomes.filter(o => o.noEcosystemFound)
157103

158-
if (hardFailures.length) {
159-
const ecosystems = hardFailures.map(f => f.ecosystem).join(', ')
104+
if (!mavenResult.ok && !mavenResult.noEcosystemFound) {
160105
throw new Error(
161-
`Bazel auto-manifest generation failed for ecosystem(s): ${ecosystems}`,
106+
'Bazel auto-manifest generation failed for ecosystem(s): maven',
162107
)
163108
}
164-
if (successes.length) {
165-
for (const s of successes) {
166-
generatedFiles.push(s.manifestPath!)
167-
}
168-
} else if (noDiscoveries.length === outcomes.length) {
109+
if (mavenResult.ok && mavenResult.manifestPath) {
110+
generatedFiles.push(mavenResult.manifestPath)
111+
} else if (mavenResult.noEcosystemFound) {
169112
logger.info('No supported Bazel Maven ecosystem detected.')
170113
}
171114
}

0 commit comments

Comments
 (0)