diff --git a/CHANGELOG.md b/CHANGELOG.md index b1e7273fc..b3b8ecef0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). -## [1.1.124](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.124) - 2026-06-19 +## [1.1.124](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.125) - 2026-06-19 ### Added +- New `socket manifest maven` command generates a Socket facts file (`.socket.facts.json`) directly from a Maven `pom.xml` project. Like the Gradle and sbt generators, it auto-detects your project, plugs into `socket manifest auto` and the `socket manifest setup` configurator, and accepts `--maven-opts` to pass options through to Maven (e.g. `--maven-opts="-P release -s settings.xml"`), plus `--bin` to point at a wrapper such as `./mvnw`. + +### Changed +- Updated the Coana CLI to v `15.5.5`. + +## [1.1.124](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.124) - 2026-06-19 + - `socket scan create --reach` accepts a new `--reach-retain-facts-file` flag. By default the CLI deletes the `.socket.facts.json` reachability report from the scan directory after a successful scan; pass this flag to keep it (e.g. for inspection or debugging). **Important:** you must delete the retained `.socket.facts.json` before running a fresh tier 1 reachability scan — a stale file left in place is picked up as a pre-generated input and silently overrides fresh analysis, so the new scan results will not be reliable. ### Changed diff --git a/package.json b/package.json index 5a45e7e85..650cc06b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "socket", - "version": "1.1.124", + "version": "1.1.125", "description": "CLI for Socket.dev", "homepage": "https://github.com/SocketDev/socket-cli", "license": "MIT", @@ -96,7 +96,7 @@ "@babel/preset-typescript": "7.27.1", "@babel/runtime": "7.28.4", "@biomejs/biome": "2.2.4", - "@coana-tech/cli": "15.5.4", + "@coana-tech/cli": "15.5.5", "@cyclonedx/cdxgen": "12.1.2", "@dotenvx/dotenvx": "1.49.0", "@eslint/compat": "1.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b49d340f8..d588dc211 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -128,8 +128,8 @@ importers: specifier: 2.2.4 version: 2.2.4 '@coana-tech/cli': - specifier: 15.5.4 - version: 15.5.4 + specifier: 15.5.5 + version: 15.5.5 '@cyclonedx/cdxgen': specifier: 12.1.2 version: 12.1.2 @@ -749,8 +749,8 @@ packages: resolution: {integrity: sha512-hAs5PPKPCQ3/Nha+1fo4A4/gL85fIfxZwHPehsjCJ+BhQH2/yw6/xReuaPA/RfNQr6iz1PcD7BZcE3ctyyl3EA==} cpu: [x64] - '@coana-tech/cli@15.5.4': - resolution: {integrity: sha512-jkfGcTQxlj7B0GfRdxZ4RvreXVAFY5lWBTBNJeAIEhD32zQugrZ0tHs+tG2o3AaPd6QFBipE8AYzY4AJmUUkeA==} + '@coana-tech/cli@15.5.5': + resolution: {integrity: sha512-eFZ1q1i7Xr8gEA80OfmzIoQXkDjrF3AUeRYaOvibVW6bJMJfDTBmxRkYtks6sloGgbNR9X/8Lwy4V+Z+D6llpA==} hasBin: true '@colors/colors@1.5.0': @@ -5385,7 +5385,7 @@ snapshots: '@cdxgen/cdxgen-plugins-bin@2.0.2': optional: true - '@coana-tech/cli@15.5.4': {} + '@coana-tech/cli@15.5.5': {} '@colors/colors@1.5.0': optional: true diff --git a/src/commands/manifest/README.md b/src/commands/manifest/README.md index 0798df74b..0f54b10a7 100644 --- a/src/commands/manifest/README.md +++ b/src/commands/manifest/README.md @@ -151,6 +151,13 @@ scala flows. Uses Gradle to generate a manifest file (`pom.xml`) for a Kotlin project; the underlying flow is identical to the gradle subcommand. +## socket manifest maven [beta] + +Generates a Socket facts file (`.socket.facts.json`) from a Maven `pom.xml` +project, using `mvn` (override with `--bin`, e.g. a project `./mvnw` wrapper). +Pass extra options through to maven with `--maven-opts` (e.g. +`--maven-opts="-P release -s settings.xml"`). + ## socket manifest scala [beta] Generates a manifest file (`pom.xml`) from Scala's `build.sbt` file. diff --git a/src/commands/manifest/cmd-manifest-maven.mts b/src/commands/manifest/cmd-manifest-maven.mts new file mode 100644 index 000000000..7db110028 --- /dev/null +++ b/src/commands/manifest/cmd-manifest-maven.mts @@ -0,0 +1,235 @@ +import path from 'node:path' + +import { debugFn } from '@socketsecurity/registry/lib/debug' +import { logger } from '@socketsecurity/registry/lib/logger' + +import { convertMavenToFacts } from './convert-maven-to-facts.mts' +import constants, { SOCKET_JSON } from '../../constants.mts' +import { commonFlags } from '../../flags.mts' +import { checkCommandInput } from '../../utils/check-input.mts' +import { getOutputKind } from '../../utils/get-output-kind.mts' +import { meowOrExit } from '../../utils/meow-with-subcommands.mts' +import { getFlagListOutput } from '../../utils/output-formatting.mts' +import { readOrDefaultSocketJson } from '../../utils/socket-json.mts' + +import type { + CliCommandConfig, + CliCommandContext, +} from '../../utils/meow-with-subcommands.mts' + +const config: CliCommandConfig = { + commandName: 'maven', + description: + '[beta] Generate a Socket facts file from a Maven `pom.xml` project', + hidden: false, + flags: { + ...commonFlags, + bin: { + type: 'string', + description: 'Location of the maven binary to use, default: mvn on PATH', + }, + includeConfigs: { + type: 'string', + description: + 'Comma-separated glob patterns matched against Maven dependency scopes (case-sensitive, `*` and `?` wildcards). Only scopes matching at least one pattern are resolved. e.g. `compile,runtime`. Default: every scope', + }, + excludeConfigs: { + type: 'string', + description: + 'Comma-separated glob patterns; Maven scopes matching any pattern are skipped (applied after --include-configs)', + }, + ignoreUnresolved: { + type: 'boolean', + description: + 'Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)', + }, + mavenOpts: { + type: 'string', + description: + 'Additional options to pass on to maven, e.g. `-P -s `', + }, + verbose: { + type: 'boolean', + description: 'Print debug messages', + }, + }, + help: (command, config) => ` + Usage + $ ${command} [options] [CWD=.] + + Options + ${getFlagListOutput(config.flags)} + + Emits a single \`.socket.facts.json\` describing the resolved dependency + graph of your Maven project, using maven (\`mvn\` on PATH by default). It + reads dependency metadata only and never downloads artifacts; an unresolved + dependency is a fatal error. You can pass --include-configs / + --exclude-configs (comma-separated glob patterns) to control which Maven + scopes are resolved (e.g. --include-configs=\`compile,runtime\`), and + --ignore-unresolved to warn on unresolved dependencies instead of failing. + + You can specify --bin to override the path to the \`mvn\` binary to invoke + (e.g. a project \`./mvnw\` wrapper), and --maven-opts to pass extra options + through to maven (e.g. \`-P -s \`). + + Support is beta. Please report issues or give us feedback on what's missing. + + Examples + + $ ${command} . + $ ${command} --bin=./mvnw . + $ ${command} --maven-opts="-P release" . + `, +} + +export const cmdManifestMaven = { + description: config.description, + hidden: config.hidden, + run, +} + +async function run( + argv: string[] | readonly string[], + importMeta: ImportMeta, + { parentName }: CliCommandContext, +): Promise { + const cli = meowOrExit({ + argv, + config, + importMeta, + parentName, + }) + + const { json = false, markdown = false } = cli.flags + + const dryRun = !!cli.flags['dryRun'] + + // TODO: Implement json/md further. + const outputKind = getOutputKind(json, markdown) + + let [cwd = '.'] = cli.input + // Note: path.resolve vs .join: + // If given path is absolute then cwd should not affect it. + cwd = path.resolve(process.cwd(), cwd) + + const sockJson = readOrDefaultSocketJson(cwd) + + debugFn( + 'inspect', + `override: ${SOCKET_JSON} maven`, + sockJson?.defaults?.manifest?.maven, + ) + + let { + bin, + excludeConfigs, + ignoreUnresolved, + includeConfigs, + mavenOpts, + verbose, + } = cli.flags + + // Set defaults for any flag/arg that is not given. Check socket.json first. + if (!bin) { + if (sockJson.defaults?.manifest?.maven?.bin) { + bin = sockJson.defaults?.manifest?.maven?.bin + logger.info(`Using default --bin from ${SOCKET_JSON}:`, bin) + } else { + bin = 'mvn' + } + } + if (!mavenOpts) { + if (sockJson.defaults?.manifest?.maven?.mavenOpts) { + mavenOpts = sockJson.defaults?.manifest?.maven?.mavenOpts + logger.info(`Using default --maven-opts from ${SOCKET_JSON}:`, mavenOpts) + } else { + mavenOpts = '' + } + } + if (includeConfigs === undefined) { + if (sockJson.defaults?.manifest?.maven?.includeConfigs !== undefined) { + includeConfigs = sockJson.defaults?.manifest?.maven?.includeConfigs + logger.info( + `Using default --include-configs from ${SOCKET_JSON}:`, + includeConfigs, + ) + } else { + includeConfigs = '' + } + } + if (excludeConfigs === undefined) { + if (sockJson.defaults?.manifest?.maven?.excludeConfigs !== undefined) { + excludeConfigs = sockJson.defaults?.manifest?.maven?.excludeConfigs + logger.info( + `Using default --exclude-configs from ${SOCKET_JSON}:`, + excludeConfigs, + ) + } else { + excludeConfigs = '' + } + } + if (ignoreUnresolved === undefined) { + if (sockJson.defaults?.manifest?.maven?.ignoreUnresolved !== undefined) { + ignoreUnresolved = sockJson.defaults?.manifest?.maven?.ignoreUnresolved + logger.info( + `Using default --ignore-unresolved from ${SOCKET_JSON}:`, + ignoreUnresolved, + ) + } else { + ignoreUnresolved = false + } + } + if (verbose === undefined) { + if (sockJson.defaults?.manifest?.maven?.verbose !== undefined) { + verbose = sockJson.defaults?.manifest?.maven?.verbose + logger.info(`Using default --verbose from ${SOCKET_JSON}:`, verbose) + } else { + verbose = false + } + } + + if (verbose) { + logger.group('- ', parentName, config.commandName, ':') + logger.group('- flags:', cli.flags) + logger.groupEnd() + logger.log('- input:', cli.input) + logger.groupEnd() + } + + const wasValidInput = checkCommandInput(outputKind, { + nook: true, + test: cli.input.length <= 1, + message: 'Can only accept one DIR (make sure to escape spaces!)', + fail: 'received ' + cli.input.length, + }) + if (!wasValidInput) { + return + } + + if (verbose) { + logger.group() + logger.info('- cwd:', cwd) + logger.info('- maven bin:', bin) + logger.groupEnd() + } + + if (dryRun) { + logger.log(constants.DRY_RUN_BAILING_NOW) + return + } + + const parsedMavenOpts = String(mavenOpts || '') + .split(' ') + .map(s => s.trim()) + .filter(Boolean) + + await convertMavenToFacts({ + bin: String(bin), + cwd, + excludeConfigs: String(excludeConfigs || ''), + ignoreUnresolved: Boolean(ignoreUnresolved), + includeConfigs: String(includeConfigs || ''), + mavenOpts: parsedMavenOpts, + verbose: Boolean(verbose), + }) +} diff --git a/src/commands/manifest/cmd-manifest-maven.test.mts b/src/commands/manifest/cmd-manifest-maven.test.mts new file mode 100644 index 000000000..c4dc818b3 --- /dev/null +++ b/src/commands/manifest/cmd-manifest-maven.test.mts @@ -0,0 +1,84 @@ +import { describe, expect } from 'vitest' + +import constants, { + FLAG_CONFIG, + FLAG_DRY_RUN, + FLAG_HELP, +} from '../../../src/constants.mts' +import { cmdit, spawnSocketCli } from '../../../test/utils.mts' + +describe('socket manifest maven', async () => { + const { binCliPath } = constants + + cmdit( + ['manifest', 'maven', FLAG_HELP, FLAG_CONFIG, '{}'], + `should support ${FLAG_HELP}`, + async cmd => { + const { code, stderr, stdout } = await spawnSocketCli(binCliPath, cmd) + expect(stdout).toMatchInlineSnapshot(` + "[beta] Generate a Socket facts file from a Maven \`pom.xml\` project + + Usage + $ socket manifest maven [options] [CWD=.] + + Options + --bin Location of the maven binary to use, default: mvn on PATH + --exclude-configs Comma-separated glob patterns; Maven scopes matching any pattern are skipped (applied after --include-configs) + --ignore-unresolved Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file) + --include-configs Comma-separated glob patterns matched against Maven dependency scopes (case-sensitive, \`*\` and \`?\` wildcards). Only scopes matching at least one pattern are resolved. e.g. \`compile,runtime\`. Default: every scope + --maven-opts Additional options to pass on to maven, e.g. \`-P -s \` + --verbose Print debug messages + + Emits a single \`.socket.facts.json\` describing the resolved dependency + graph of your Maven project, using maven (\`mvn\` on PATH by default). It + reads dependency metadata only and never downloads artifacts; an unresolved + dependency is a fatal error. You can pass --include-configs / + --exclude-configs (comma-separated glob patterns) to control which Maven + scopes are resolved (e.g. --include-configs=\`compile,runtime\`), and + --ignore-unresolved to warn on unresolved dependencies instead of failing. + + You can specify --bin to override the path to the \`mvn\` binary to invoke + (e.g. a project \`./mvnw\` wrapper), and --maven-opts to pass extra options + through to maven (e.g. \`-P -s \`). + + Support is beta. Please report issues or give us feedback on what's missing. + + Examples + + $ socket manifest maven . + $ socket manifest maven --bin=./mvnw . + $ socket manifest maven --maven-opts="-P release" ." + `) + expect(`\n ${stderr}`).toMatchInlineSnapshot(` + " + _____ _ _ /--------------- + | __|___ ___| |_ ___| |_ | CLI: + |__ | * | _| '_| -_| _| | token: , org: + |_____|___|___|_,_|___|_|.dev | Command: \`socket manifest maven\`, cwd: " + `) + + expect(code, 'explicit help should exit with code 0').toBe(0) + expect(stderr, 'banner includes base command').toContain( + '`socket manifest maven`', + ) + }, + ) + + cmdit( + ['manifest', 'maven', FLAG_DRY_RUN, FLAG_CONFIG, '{}'], + 'should require args with just dry-run', + async cmd => { + const { code, stderr, stdout } = await spawnSocketCli(binCliPath, cmd) + expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`) + expect(`\n ${stderr}`).toMatchInlineSnapshot(` + " + _____ _ _ /--------------- + | __|___ ___| |_ ___| |_ | CLI: + |__ | * | _| '_| -_| _| | token: , org: + |_____|___|___|_,_|___|_|.dev | Command: \`socket manifest maven\`, cwd: " + `) + + expect(code, 'dry-run should exit with code 0 if input ok').toBe(0) + }, + ) +}) diff --git a/src/commands/manifest/cmd-manifest.mts b/src/commands/manifest/cmd-manifest.mts index cd617dd79..518cdfddd 100644 --- a/src/commands/manifest/cmd-manifest.mts +++ b/src/commands/manifest/cmd-manifest.mts @@ -4,6 +4,7 @@ import { cmdManifestCdxgen } from './cmd-manifest-cdxgen.mts' import { cmdManifestConda } from './cmd-manifest-conda.mts' import { cmdManifestGradle } from './cmd-manifest-gradle.mts' import { cmdManifestKotlin } from './cmd-manifest-kotlin.mts' +import { cmdManifestMaven } from './cmd-manifest-maven.mts' import { cmdManifestScala } from './cmd-manifest-scala.mts' import { cmdManifestSetup } from './cmd-manifest-setup.mts' import { REQUIREMENTS_TXT } from '../../constants.mts' @@ -39,7 +40,7 @@ const config: CliCommandConfig = { per language. Currently supported language: bazel [beta], gradle [beta], kotlin (through - gradle) [beta], scala [beta]. + gradle) [beta], maven [beta], scala [beta]. Examples @@ -74,6 +75,7 @@ async function run( conda: cmdManifestConda, gradle: cmdManifestGradle, kotlin: cmdManifestKotlin, + maven: cmdManifestMaven, scala: cmdManifestScala, setup: cmdManifestSetup, }, diff --git a/src/commands/manifest/cmd-manifest.test.mts b/src/commands/manifest/cmd-manifest.test.mts index 604eb451c..93c264770 100644 --- a/src/commands/manifest/cmd-manifest.test.mts +++ b/src/commands/manifest/cmd-manifest.test.mts @@ -29,6 +29,7 @@ describe('socket manifest', async () => { conda [beta] Convert a Conda environment.yml file to a python requirements.txt gradle [beta] Generate a Socket facts file (or \`pom.xml\` with --pom) for a Gradle/Java/Kotlin/etc project kotlin [beta] Generate a Socket facts file (or \`pom.xml\` with --pom) for a Kotlin project + maven [beta] Generate a Socket facts file from a Maven \`pom.xml\` project scala [beta] Generate a Socket facts file (or \`pom.xml\` with --pom) from a Scala \`build.sbt\` project setup Start interactive configurator to customize default flag values for \`socket manifest\` in this dir diff --git a/src/commands/manifest/coana-manifest-facts.mts b/src/commands/manifest/coana-manifest-facts.mts index c77cde292..47ac6fb72 100644 --- a/src/commands/manifest/coana-manifest-facts.mts +++ b/src/commands/manifest/coana-manifest-facts.mts @@ -17,10 +17,10 @@ import { spawnCoanaDlx } from '../../utils/dlx.mts' // facts file. // // `spawnCoanaDlx` resolves the Coana CLI via dlx (or a local build when -// `SOCKET_CLI_COANA_LOCAL_PATH` is set). `bin` (the gradle/sbt executable) is -// always resolved by the caller to a concrete default (`/gradlew`, or -// `sbt` on PATH) before we get here, so it is forwarded verbatim; the empty -// guard below is just a cheap safeguard against passing `--bin ''`. +// `SOCKET_CLI_COANA_LOCAL_PATH` is set). `bin` (the gradle/maven/sbt executable) +// is always resolved by the caller to a concrete default (`/gradlew`, or +// `mvn`/`sbt` on PATH) before we get here, so it is forwarded verbatim; the +// empty guard below is just a cheap safeguard against passing `--bin ''`. export async function runCoanaManifestFacts({ bin, buildOpts, @@ -34,9 +34,9 @@ export async function runCoanaManifestFacts({ }: { bin: string buildOpts: string[] - buildOptsFlag: '--gradle-opts' | '--sbt-opts' + buildOptsFlag: '--gradle-opts' | '--maven-opts' | '--sbt-opts' cwd: string - ecosystem: 'gradle' | 'sbt' + ecosystem: 'gradle' | 'maven' | 'sbt' excludeConfigs: string ignoreUnresolved: boolean includeConfigs: string diff --git a/src/commands/manifest/convert-maven-to-facts.mts b/src/commands/manifest/convert-maven-to-facts.mts new file mode 100644 index 000000000..5255321ce --- /dev/null +++ b/src/commands/manifest/convert-maven-to-facts.mts @@ -0,0 +1,36 @@ +import { runCoanaManifestFacts } from './coana-manifest-facts.mts' + +// Generates a `.socket.facts.json` for a Maven project by delegating to the +// Coana CLI's `manifest maven` command (which owns the Maven plugin that +// resolves the dependency graph). socket-cli no longer runs maven itself; an +// explicit `bin` is forwarded as `--bin`, otherwise Coana defaults to `mvn` on +// PATH. +export async function convertMavenToFacts({ + bin, + cwd, + excludeConfigs, + ignoreUnresolved, + includeConfigs, + mavenOpts, + verbose, +}: { + bin: string + cwd: string + excludeConfigs: string + ignoreUnresolved: boolean + includeConfigs: string + mavenOpts: string[] + verbose: boolean +}): Promise { + await runCoanaManifestFacts({ + bin, + buildOpts: mavenOpts, + buildOptsFlag: '--maven-opts', + cwd, + ecosystem: 'maven', + excludeConfigs, + ignoreUnresolved, + includeConfigs, + verbose, + }) +} diff --git a/src/commands/manifest/detect-manifest-actions.mts b/src/commands/manifest/detect-manifest-actions.mts index b03309bd0..7be3f7330 100644 --- a/src/commands/manifest/detect-manifest-actions.mts +++ b/src/commands/manifest/detect-manifest-actions.mts @@ -20,6 +20,7 @@ export interface GeneratableManifests { count: number conda: boolean gradle: boolean + maven: boolean sbt: boolean } @@ -35,6 +36,7 @@ export async function detectManifestActions( count: 0, conda: false, gradle: false, + maven: false, sbt: false, } @@ -76,6 +78,17 @@ export async function detectManifestActions( output.count += 1 } + if (sockJson?.defaults?.manifest?.maven?.disabled) { + debugLog( + 'notice', + `[DEBUG] - maven auto-detection is disabled in ${SOCKET_JSON}`, + ) + } else if (existsSync(path.join(cwd, 'pom.xml'))) { + debugLog('notice', '[DEBUG] - Detected a Maven pom.xml build file') + output.maven = true + output.count += 1 + } + if (sockJson?.defaults?.manifest?.conda?.disabled) { debugLog( 'notice', diff --git a/src/commands/manifest/generate_auto_manifest.mts b/src/commands/manifest/generate_auto_manifest.mts index 0794be55b..7e34f78a4 100644 --- a/src/commands/manifest/generate_auto_manifest.mts +++ b/src/commands/manifest/generate_auto_manifest.mts @@ -4,6 +4,7 @@ import { logger } from '@socketsecurity/registry/lib/logger' import { extractBazelToMaven } from './bazel/extract_bazel_to_maven.mts' import { convertGradleToFacts } from './convert-gradle-to-facts.mts' +import { convertMavenToFacts } from './convert-maven-to-facts.mts' import { convertSbtToFacts } from './convert-sbt-to-facts.mts' import { convertGradleToMaven } from './convert_gradle_to_maven.mts' import { convertSbtToMaven } from './convert_sbt_to_maven.mts' @@ -112,6 +113,26 @@ export async function generateAutoManifest({ } } + if (!sockJson?.defaults?.manifest?.maven?.disabled && detected.maven) { + logger.log('Detected a Maven pom.xml build, generating Socket facts...') + await convertMavenToFacts({ + // Note: `mvn` is more likely to be resolved against PATH env. + bin: sockJson.defaults?.manifest?.maven?.bin ?? 'mvn', + cwd, + excludeConfigs: sockJson.defaults?.manifest?.maven?.excludeConfigs ?? '', + ignoreUnresolved: Boolean( + sockJson.defaults?.manifest?.maven?.ignoreUnresolved, + ), + includeConfigs: sockJson.defaults?.manifest?.maven?.includeConfigs ?? '', + mavenOpts: + sockJson.defaults?.manifest?.maven?.mavenOpts + ?.split(' ') + .map(s => s.trim()) + .filter(Boolean) ?? [], + verbose: Boolean(sockJson.defaults?.manifest?.maven?.verbose), + }) + } + if (!sockJson?.defaults?.manifest?.conda?.disabled && detected.conda) { logger.log( 'Detected an environment.yml file, running default Conda generator...', diff --git a/src/commands/manifest/setup-manifest-config.mts b/src/commands/manifest/setup-manifest-config.mts index c6cb97b0a..1e13a0f90 100644 --- a/src/commands/manifest/setup-manifest-config.mts +++ b/src/commands/manifest/setup-manifest-config.mts @@ -69,6 +69,11 @@ export async function setupManifestConfig( description: 'Generate a Socket facts file or pom.xml (for Kotlin) through gradle', }, + { + name: 'Maven'.padEnd(30, ' '), + value: 'maven', + description: 'Generate a Socket facts file through maven', + }, { name: 'Scala (gradle)'.padEnd(30, ' '), value: 'gradle', @@ -147,6 +152,13 @@ export async function setupManifestConfig( result = await setupGradle(sockJson.defaults.manifest.gradle) break } + case 'maven': { + if (!sockJson.defaults.manifest.maven) { + sockJson.defaults.manifest.maven = {} + } + result = await setupMaven(sockJson.defaults.manifest.maven) + break + } case 'sbt': { if (!sockJson.defaults.manifest.sbt) { sockJson.defaults.manifest.sbt = {} @@ -315,6 +327,52 @@ async function setupGradle( return notCanceled() } +async function setupMaven( + config: NonNullable< + NonNullable['manifest']>['maven'] + >, +): Promise> { + const bin = await askForBin(config.bin || 'mvn') + if (bin === undefined) { + return canceledByUser() + } else if (bin) { + config.bin = bin + } else { + delete config.bin + } + + const opts = await input({ + message: '(--maven-opts) Enter maven options to pass through', + default: config.mavenOpts || '', + required: false, + }) + if (opts === undefined) { + return canceledByUser() + } else if (opts) { + config.mavenOpts = opts + } else { + delete config.mavenOpts + } + + // Maven only generates Socket facts (no pom path), so always ask the + // facts-only options. + const factsOptions = await setupFactsOptions(config) + if (!factsOptions.ok || factsOptions.data.canceled) { + return factsOptions + } + + const verbose = await askForVerboseFlag(config.verbose) + if (verbose === undefined) { + return canceledByUser() + } else if (verbose === 'yes' || verbose === 'no') { + config.verbose = verbose === 'yes' + } else { + delete config.verbose + } + + return notCanceled() +} + async function setupSbt( config: NonNullable< NonNullable['manifest']>['sbt'] diff --git a/src/utils/socket-json.mts b/src/utils/socket-json.mts index e8c7ad257..3cbbfee23 100644 --- a/src/utils/socket-json.mts +++ b/src/utils/socket-json.mts @@ -69,6 +69,15 @@ export interface SocketJson { ignoreUnresolved?: boolean | undefined verbose?: boolean | undefined } + maven?: { + disabled?: boolean | undefined + bin?: string | undefined + excludeConfigs?: string | undefined + includeConfigs?: string | undefined + ignoreUnresolved?: boolean | undefined + mavenOpts?: string | undefined + verbose?: boolean | undefined + } sbt?: { disabled?: boolean | undefined infile?: string | undefined