Skip to content

Commit 848421d

Browse files
authored
feat(workspace): add Go workspace (go.work) discovery (#495)
1 parent 1d590b2 commit 848421d

14 files changed

Lines changed: 188 additions & 2 deletions

File tree

src/index.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { getCustom } from "./tools.js";
99
import { resolveBatchMetadata, resolveContinueOnError } from './batch_opts.js'
1010
import { discoverMavenModules } from './providers/java_maven.js'
1111
import { discoverGradleSubprojects } from './providers/java_gradle.js'
12+
import { discoverGoWorkspaceModules } from './providers/golang_gomodules.js'
1213
import {
1314
discoverWorkspaceCrates,
1415
discoverWorkspacePackages,
@@ -27,6 +28,7 @@ export default { componentAnalysis, stackAnalysis, stackAnalysisBatch, imageAnal
2728
export {
2829
discoverMavenModules,
2930
discoverGradleSubprojects,
31+
discoverGoWorkspaceModules,
3032
discoverWorkspacePackages,
3133
discoverWorkspaceCrates,
3234
validatePackageJson,
@@ -323,7 +325,7 @@ async function generateOneSbom(manifestPath, workspaceOpts) {
323325
*
324326
* @param {string} root - Resolved workspace root
325327
* @param {Options} opts
326-
* @returns {Promise<{ ecosystem: 'javascript' | 'cargo' | 'maven' | 'gradle' | 'unknown', manifestPaths: string[] }>}
328+
* @returns {Promise<{ ecosystem: 'javascript' | 'cargo' | 'maven' | 'gradle' | 'gomodules' | 'unknown', manifestPaths: string[] }>}
327329
* @private
328330
*/
329331
async function detectWorkspaceManifests(root, opts) {
@@ -352,6 +354,13 @@ async function detectWorkspaceManifests(root, opts) {
352354
}
353355
}
354356

357+
if (fs.existsSync(path.join(root, 'go.work'))) {
358+
const manifestPaths = await discoverGoWorkspaceModules(root, opts)
359+
if (manifestPaths.length > 0) {
360+
return { ecosystem: 'gomodules', manifestPaths }
361+
}
362+
}
363+
355364
const hasJsLock = fs.existsSync(path.join(root, 'pnpm-lock.yaml'))
356365
|| fs.existsSync(path.join(root, 'yarn.lock'))
357366
|| fs.existsSync(path.join(root, 'package-lock.json'))
@@ -547,7 +556,7 @@ async function stackAnalysisBatch(workspaceRoot, html = false, opts = {}) {
547556
}
548557

549558
if (manifestPaths.length === 0) {
550-
throw new Error(`No workspace manifests found at ${root}. Ensure Cargo.toml+Cargo.lock or package.json+lock file exist.`)
559+
throw new Error(`No workspace manifests found at ${root}. Ensure a supported workspace root exists (Cargo.toml+Cargo.lock, go.work, or package.json+lock file).`)
551560
}
552561

553562
const workspaceOpts = { ...opts, TRUSTIFY_DA_WORKSPACE_DIR: root }

src/providers/golang_gomodules.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { PackageURL } from 'packageurl-js'
66
import { readLicenseFile } from '../license/license_utils.js'
77
import Sbom from '../sbom.js'
88
import { getCustom, getCustomPath, invokeCommand } from "../tools.js";
9+
import { filterManifestPathsByDiscoveryIgnore, resolveWorkspaceDiscoveryIgnore } from '../workspace.js'
910

1011
import { getParser, getRequireQuery } from './gomod_parser.js'
1112

@@ -427,3 +428,56 @@ function getLineSeparatorGolang() {
427428
let reg = /\n|\r\n/
428429
return reg
429430
}
431+
432+
/**
433+
* Discover all go.mod manifest paths in a Go workspace.
434+
* Uses `go work edit -json` to get workspace members.
435+
*
436+
* @param {string} workspaceRoot - Absolute or relative path to workspace root (must contain go.work)
437+
* @param {import('../index.js').Options} [opts={}]
438+
* @returns {Promise<string[]>} Paths to go.mod files (absolute)
439+
*/
440+
export async function discoverGoWorkspaceModules(workspaceRoot, opts = {}) {
441+
const root = path.resolve(workspaceRoot)
442+
const goWork = path.join(root, 'go.work')
443+
444+
if (!fs.existsSync(goWork)) {
445+
return []
446+
}
447+
448+
const goBin = getCustomPath('go', opts)
449+
let output
450+
try {
451+
output = invokeCommand(goBin, ['work', 'edit', '-json', goWork], { cwd: root })
452+
} catch {
453+
return []
454+
}
455+
456+
let workspace
457+
try {
458+
workspace = JSON.parse(output.toString().trim())
459+
} catch {
460+
return []
461+
}
462+
463+
const useEntries = workspace.Use || []
464+
if (useEntries.length === 0) {
465+
return []
466+
}
467+
468+
const manifestPaths = []
469+
for (const entry of useEntries) {
470+
const diskPath = entry.DiskPath
471+
if (!diskPath) {
472+
continue
473+
}
474+
const moduleDir = path.resolve(root, diskPath)
475+
const goMod = path.join(moduleDir, 'go.mod')
476+
if (fs.existsSync(goMod)) {
477+
manifestPaths.push(goMod)
478+
}
479+
}
480+
481+
const ignorePatterns = resolveWorkspaceDiscoveryIgnore(opts)
482+
return filterManifestPathsByDiscoveryIgnore(manifestPaths, root, ignorePatterns)
483+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
go 1.22
2+
3+
use (
4+
./module-a
5+
./module-b
6+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module example.com/module-a
2+
3+
go 1.22
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module example.com/module-b
2+
3+
go 1.22
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
go 1.22
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module example.com/existing
2+
3+
go 1.22
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
go 1.22
2+
3+
use (
4+
./existing
5+
./nonexistent
6+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
go 1.22
2+
3+
use (
4+
./libs/core
5+
./libs/util
6+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module example.com/libs/core
2+
3+
go 1.22

0 commit comments

Comments
 (0)