Skip to content

Commit 4e72843

Browse files
authored
Fix discovery of Go direct dependencies (#6)
* Fix discovery of direct dependencies - Our current approach relied on a main.go file residing at the root of the repository which may not always be the case. - Additionally calling go list {{.Deps}} module would not always give indented results. * Bump version
1 parent b383172 commit 4e72843

File tree

4 files changed

+122
-44
lines changed

4 files changed

+122
-44
lines changed

dist/index.js

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6161
return (mod && mod.__esModule) ? mod : { "default": mod };
6262
};
6363
Object.defineProperty(exports, "__esModule", ({ value: true }));
64-
exports.getDependencies = exports.listDeps = void 0;
64+
exports.getDependencies = exports.getModuleGraph = exports.listDirectDeps = void 0;
6565
const core = __importStar(__nccwpck_require__(2186));
6666
const path_1 = __importDefault(__nccwpck_require__(1017));
6767
const constants_1 = __nccwpck_require__(9677);
@@ -79,30 +79,64 @@ const parseDependency = (line) => {
7979
return;
8080
}
8181
};
82-
const listDeps = (dir, module = '') => {
83-
let output = (0, child_process_1.execSync)(`go list -f {{.Deps}} ${module}`, {
82+
const parseModuleUrl = (m) => {
83+
const [url, version = ''] = m.split('@');
84+
const [domain, owner, repo] = url.split('/');
85+
return { module: [domain, owner, repo].join('/'), version };
86+
};
87+
const listDirectDeps = (dir) => {
88+
let output = (0, child_process_1.execSync)(`go list -f '{{if not .Indirect}}{{.}}{{end}}' -m all`, { cwd: resolveDir(dir) }).toString();
89+
return output
90+
.split('\n')
91+
.map((d) => {
92+
const [module, version = ''] = d.split(' ');
93+
return { module, version };
94+
})
95+
.filter((entry) => filterDependency(entry.module));
96+
};
97+
exports.listDirectDeps = listDirectDeps;
98+
const getModuleGraph = (dir) => {
99+
const output = (0, child_process_1.execSync)(`go mod graph`, {
84100
cwd: resolveDir(dir),
85101
}).toString();
86-
// trim `[]` at start and end of string
87-
output = output.slice(1, -1);
88-
return output.split(/\s+/).filter(filterDependency);
102+
const graph = {};
103+
output.split('\n').forEach((line) => {
104+
if (!line) {
105+
return;
106+
}
107+
const [parent, child] = line.split(' ');
108+
const mod = parseModuleUrl(parent);
109+
const childMod = parseModuleUrl(child);
110+
const key = `${mod.module}@${mod.version}`;
111+
graph[key] = graph[key] || [];
112+
if (childMod.module !== key) {
113+
graph[key].push(childMod);
114+
}
115+
});
116+
Object.entries(graph).forEach(([key, deps]) => {
117+
graph[key] = (0, lodash_1.uniqBy)(deps, 'module');
118+
});
119+
return graph;
89120
};
90-
exports.listDeps = listDeps;
121+
exports.getModuleGraph = getModuleGraph;
91122
const getDependencies = (dir = '') => {
92-
const direct = (0, exports.listDeps)(dir);
93-
let dependencies = direct.map((d) => ({
94-
source: parseDependency(d),
95-
dependencies: (0, exports.listDeps)(dir, d).map((d) => ({ source: parseDependency(d) })),
96-
}));
97-
// Merge direct dependencies with the same source
98-
// and remove self dependencies
99-
const groups = (0, lodash_1.groupBy)(dependencies, 'source');
100-
return Object.entries(groups).map(([source, group]) => {
123+
const graph = (0, exports.getModuleGraph)(dir);
124+
const direct = (0, exports.listDirectDeps)(dir);
125+
let dependencies = direct
126+
.filter((d) => filterDependency(d.module))
127+
.map((d) => {
128+
const url = parseModuleUrl(d.module).module;
129+
const deps = graph[`${url}@${d.version}`] || [];
101130
return {
102-
source,
103-
dependencies: (0, lodash_1.uniqBy)(group.flatMap((g) => g.dependencies), 'source').filter((d) => d.source !== source),
131+
source: parseDependency(d.module),
132+
dependencies: deps
133+
.filter((d) => filterDependency(d.module))
134+
.map((d) => ({
135+
source: parseDependency(d.module),
136+
})),
104137
};
105138
});
139+
return dependencies;
106140
};
107141
exports.getDependencies = getDependencies;
108142

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "stackaid-json-generator",
3-
"version": "1.3.0",
3+
"version": "1.4.0",
44
"private": false,
55
"description": "A GitHub action to generate a stackaid.json file based on your repository's dependency graph",
66
"main": "lib/src/main.js",

src/go.ts

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as core from '@actions/core'
22
import path from 'path'
33
import { GITHUB_DOMAIN } from './constants'
44
import { execSync } from 'child_process'
5-
import { groupBy, uniqBy } from 'lodash'
5+
import { uniqBy } from 'lodash'
66

77
const sourceDir = core.getInput('src_dir') || process.cwd()
88

@@ -20,33 +20,77 @@ const parseDependency = (line: string) => {
2020
}
2121
}
2222

23-
export const listDeps = (dir: string, module: string = '') => {
24-
let output = execSync(`go list -f {{.Deps}} ${module}`, {
23+
const parseModuleUrl = (m: string) => {
24+
const [url, version = ''] = m.split('@')
25+
const [domain, owner, repo] = url.split('/')
26+
27+
return { module: [domain, owner, repo].join('/'), version }
28+
}
29+
30+
export const listDirectDeps = (dir: string) => {
31+
let output = execSync(
32+
`go list -f '{{if not .Indirect}}{{.}}{{end}}' -m all`,
33+
{ cwd: resolveDir(dir) }
34+
).toString()
35+
36+
return output
37+
.split('\n')
38+
.map((d) => {
39+
const [module, version = ''] = d.split(' ')
40+
return { module, version }
41+
})
42+
.filter((entry) => filterDependency(entry.module))
43+
}
44+
45+
export const getModuleGraph = (dir: string) => {
46+
const output = execSync(`go mod graph`, {
2547
cwd: resolveDir(dir),
2648
}).toString()
27-
// trim `[]` at start and end of string
28-
output = output.slice(1, -1)
2949

30-
return output.split(/\s+/).filter(filterDependency)
50+
const graph: Record<string, { module: string; version: string }[]> = {}
51+
52+
output.split('\n').forEach((line) => {
53+
if (!line) {
54+
return
55+
}
56+
57+
const [parent, child] = line.split(' ')
58+
const mod = parseModuleUrl(parent)
59+
const childMod = parseModuleUrl(child)
60+
61+
const key = `${mod.module}@${mod.version}`
62+
graph[key] = graph[key] || []
63+
64+
if (childMod.module !== key) {
65+
graph[key].push(childMod)
66+
}
67+
})
68+
69+
Object.entries(graph).forEach(([key, deps]) => {
70+
graph[key] = uniqBy(deps, 'module')
71+
})
72+
73+
return graph
3174
}
3275

3376
export const getDependencies = (dir: string = '') => {
34-
const direct = listDeps(dir)
35-
let dependencies = direct.map((d) => ({
36-
source: parseDependency(d),
37-
dependencies: listDeps(dir, d).map((d) => ({ source: parseDependency(d) })),
38-
}))
39-
40-
// Merge direct dependencies with the same source
41-
// and remove self dependencies
42-
const groups = groupBy(dependencies, 'source')
43-
return Object.entries(groups).map(([source, group]) => {
44-
return {
45-
source,
46-
dependencies: uniqBy(
47-
group.flatMap((g) => g.dependencies),
48-
'source'
49-
).filter((d) => d.source !== source),
50-
}
51-
}) as StackAidDependency[]
77+
const graph = getModuleGraph(dir)
78+
const direct = listDirectDeps(dir)
79+
80+
let dependencies = direct
81+
.filter((d) => filterDependency(d.module))
82+
.map((d) => {
83+
const url = parseModuleUrl(d.module).module
84+
const deps = graph[`${url}@${d.version}`] || []
85+
return {
86+
source: parseDependency(d.module) as string,
87+
dependencies: deps
88+
.filter((d) => filterDependency(d.module))
89+
.map((d) => ({
90+
source: parseDependency(d.module) as string,
91+
})),
92+
}
93+
})
94+
95+
return dependencies as StackAidDependency[]
5296
}

0 commit comments

Comments
 (0)