@trustify-da/trustify-da-javascript-client 0.3.0-ea.f501753 → 0.3.0-ea.fa16f06
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/README.md +2 -2
- package/dist/package.json +25 -22
- package/dist/src/analysis.d.ts +14 -7
- package/dist/src/analysis.js +29 -13
- package/dist/src/cli.js +106 -2
- package/dist/src/cyclone_dx_sbom.js +11 -3
- package/dist/src/index.d.ts +17 -12
- package/dist/src/index.js +21 -7
- package/dist/src/license/index.d.ts +3 -3
- package/dist/src/license/index.js +4 -1
- package/dist/src/license/license_utils.js +4 -1
- package/dist/src/license/licenses_api.d.ts +2 -2
- package/dist/src/oci_image/images.d.ts +1 -1
- package/dist/src/oci_image/images.js +15 -1
- package/dist/src/oci_image/utils.d.ts +5 -5
- package/dist/src/provider.d.ts +4 -3
- package/dist/src/provider.js +6 -2
- package/dist/src/providers/base_java.d.ts +14 -3
- package/dist/src/providers/base_java.js +16 -6
- package/dist/src/providers/base_javascript.d.ts +20 -9
- package/dist/src/providers/base_javascript.js +10 -3
- package/dist/src/providers/base_pyproject.d.ts +10 -5
- package/dist/src/providers/base_pyproject.js +7 -0
- package/dist/src/providers/containerfile_parser.d.ts +5 -0
- package/dist/src/providers/containerfile_parser.js +20 -0
- package/dist/src/providers/golang_gomodules.d.ts +2 -1
- package/dist/src/providers/golang_gomodules.js +1 -1
- package/dist/src/providers/java_gradle.js +2 -2
- package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
- package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
- package/dist/src/providers/java_maven.d.ts +2 -2
- package/dist/src/providers/java_maven.js +72 -0
- package/dist/src/providers/javascript_bun.d.ts +10 -0
- package/dist/src/providers/javascript_bun.js +100 -0
- package/dist/src/providers/javascript_npm.d.ts +6 -1
- package/dist/src/providers/oci_dockerfile.d.ts +51 -0
- package/dist/src/providers/oci_dockerfile.js +177 -0
- package/dist/src/providers/processors/yarn_berry_processor.d.ts +2 -2
- package/dist/src/providers/processors/yarn_classic_processor.d.ts +2 -2
- package/dist/src/providers/python_controller.js +61 -4
- package/dist/src/providers/python_pip.d.ts +1 -0
- package/dist/src/providers/python_pip.js +1 -1
- package/dist/src/providers/python_pip_pyproject.js +3 -1
- package/dist/src/providers/python_poetry.d.ts +31 -8
- package/dist/src/providers/python_poetry.js +62 -14
- package/dist/src/providers/python_uv.d.ts +18 -14
- package/dist/src/providers/python_uv.js +111 -2
- package/dist/src/providers/rust_cargo.d.ts +6 -2
- package/dist/src/providers/rust_cargo.js +105 -19
- package/dist/src/providers/tree-sitter-containerfile.wasm +0 -0
- package/dist/src/remediate.d.ts +21 -0
- package/dist/src/remediate.js +149 -0
- package/dist/src/remediation.d.ts +57 -0
- package/dist/src/remediation.js +436 -0
- package/dist/src/remediation_report.d.ts +40 -0
- package/dist/src/remediation_report.js +159 -0
- package/dist/src/tools.d.ts +2 -2
- package/dist/src/updaters/maven_updater.d.ts +26 -0
- package/dist/src/updaters/maven_updater.js +322 -0
- package/dist/src/updaters/toml_updater.d.ts +33 -0
- package/dist/src/updaters/toml_updater.js +244 -0
- package/dist/src/workspace.d.ts +9 -0
- package/dist/src/workspace.js +1 -1
- package/package.json +26 -23
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export default class Python_poetry extends Base_pyproject {
|
|
2
|
+
_verifyPoetryAccessible(poetryBin: any): void;
|
|
2
3
|
/**
|
|
3
4
|
* @param {string} manifestDir
|
|
4
5
|
* @param {string} _workspaceDir - unused (poetry has no workspace support)
|
|
@@ -6,7 +7,7 @@ export default class Python_poetry extends Base_pyproject {
|
|
|
6
7
|
* @param {Object} opts
|
|
7
8
|
* @returns {Promise<{directDeps: string[], graph: Map<string, {name: string, version: string, children: string[]}>}>}
|
|
8
9
|
*/
|
|
9
|
-
_getDependencyData(manifestDir: string, _workspaceDir: string, parsed: object, opts:
|
|
10
|
+
_getDependencyData(manifestDir: string, _workspaceDir: string, parsed: object, opts: Object): Promise<{
|
|
10
11
|
directDeps: string[];
|
|
11
12
|
graph: Map<string, {
|
|
12
13
|
name: string;
|
|
@@ -18,17 +19,17 @@ export default class Python_poetry extends Base_pyproject {
|
|
|
18
19
|
* Get poetry show --tree output.
|
|
19
20
|
* @param {string} manifestDir
|
|
20
21
|
* @param {boolean} hasDevGroup
|
|
21
|
-
* @param {
|
|
22
|
+
* @param {string} poetryBin
|
|
22
23
|
* @returns {string}
|
|
23
24
|
*/
|
|
24
|
-
_getPoetryShowTreeOutput(manifestDir: string, hasDevGroup: boolean,
|
|
25
|
+
_getPoetryShowTreeOutput(manifestDir: string, hasDevGroup: boolean, poetryBin: string): string;
|
|
25
26
|
/**
|
|
26
27
|
* Get poetry show --all output (flat list with resolved versions).
|
|
27
28
|
* @param {string} manifestDir
|
|
28
|
-
* @param {
|
|
29
|
+
* @param {string} poetryBin
|
|
29
30
|
* @returns {string}
|
|
30
31
|
*/
|
|
31
|
-
_getPoetryShowAllOutput(manifestDir: string,
|
|
32
|
+
_getPoetryShowAllOutput(manifestDir: string, poetryBin: string): string;
|
|
32
33
|
/**
|
|
33
34
|
* Parse poetry show --all output into a version map.
|
|
34
35
|
* Lines look like: "name (!) 1.2.3 Description text..."
|
|
@@ -46,29 +47,51 @@ export default class Python_poetry extends Base_pyproject {
|
|
|
46
47
|
* → transitiveMarkers['click']['colorama'] = "sys_platform == 'win32'"
|
|
47
48
|
* @param {string|null} lockDir
|
|
48
49
|
* @param {object} parsed - parsed pyproject.toml
|
|
49
|
-
* @returns {{directMarkers: Map<string, string>, transitiveMarkers: Map<string, Map<string, string>>}}
|
|
50
|
+
* @returns {{directMarkers: Map<string, string>, transitiveMarkers: Map<string, Map<string, string>>, hashMap: Map<string, Array<{alg: string, content: string}>>}}
|
|
50
51
|
*/
|
|
51
52
|
_extractMarkerData(lockDir: string | null, parsed: object): {
|
|
52
53
|
directMarkers: Map<string, string>;
|
|
53
54
|
transitiveMarkers: Map<string, Map<string, string>>;
|
|
55
|
+
hashMap: Map<string, Array<{
|
|
56
|
+
alg: string;
|
|
57
|
+
content: string;
|
|
58
|
+
}>>;
|
|
54
59
|
};
|
|
60
|
+
/**
|
|
61
|
+
* Extract a SHA-256 hex digest from poetry.lock files array.
|
|
62
|
+
* Prefers the sdist (.tar.gz) hash; falls back to the first wheel hash.
|
|
63
|
+
* @param {Array<{file: string, hash: string}>} [files]
|
|
64
|
+
* @returns {string|null}
|
|
65
|
+
*/
|
|
66
|
+
_extractSha256FromFiles(files?: Array<{
|
|
67
|
+
file: string;
|
|
68
|
+
hash: string;
|
|
69
|
+
}>): string | null;
|
|
55
70
|
/**
|
|
56
71
|
* Parse poetry show --tree output into a dependency graph structure.
|
|
57
72
|
*
|
|
58
73
|
* @param {string} treeOutput
|
|
59
74
|
* @param {Map<string, string>} versionMap - canonical name -> resolved version
|
|
60
|
-
* @param {{directMarkers: Map<string, string>, transitiveMarkers: Map<string, Map<string, string>>}} markerData
|
|
61
|
-
* @returns {{directDeps: string[], graph: Map<string, {name: string, version: string, children: string[]}>}}
|
|
75
|
+
* @param {{directMarkers: Map<string, string>, transitiveMarkers: Map<string, Map<string, string>>, hashMap: Map<string, Array<{alg: string, content: string}>>}} markerData
|
|
76
|
+
* @returns {{directDeps: string[], graph: Map<string, {name: string, version: string, children: string[], hashes?: Array<{alg: string, content: string}>}>}}
|
|
62
77
|
*/
|
|
63
78
|
_parsePoetryTree(treeOutput: string, versionMap: Map<string, string>, markerData: {
|
|
64
79
|
directMarkers: Map<string, string>;
|
|
65
80
|
transitiveMarkers: Map<string, Map<string, string>>;
|
|
81
|
+
hashMap: Map<string, Array<{
|
|
82
|
+
alg: string;
|
|
83
|
+
content: string;
|
|
84
|
+
}>>;
|
|
66
85
|
}): {
|
|
67
86
|
directDeps: string[];
|
|
68
87
|
graph: Map<string, {
|
|
69
88
|
name: string;
|
|
70
89
|
version: string;
|
|
71
90
|
children: string[];
|
|
91
|
+
hashes?: Array<{
|
|
92
|
+
alg: string;
|
|
93
|
+
content: string;
|
|
94
|
+
}>;
|
|
72
95
|
}>;
|
|
73
96
|
};
|
|
74
97
|
}
|
|
@@ -32,6 +32,17 @@ export default class Python_poetry extends Base_pyproject {
|
|
|
32
32
|
_cmdName() {
|
|
33
33
|
return 'poetry';
|
|
34
34
|
}
|
|
35
|
+
_verifyPoetryAccessible(poetryBin) {
|
|
36
|
+
try {
|
|
37
|
+
invokeCommand(poetryBin, ['--version']);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (error.code === 'ENOENT') {
|
|
41
|
+
throw new Error(`poetry is not accessible at "${poetryBin}"`, { cause: error });
|
|
42
|
+
}
|
|
43
|
+
throw new Error('failed to check for poetry binary', { cause: error });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
35
46
|
/**
|
|
36
47
|
* @param {string} manifestDir
|
|
37
48
|
* @param {string} _workspaceDir - unused (poetry has no workspace support)
|
|
@@ -41,9 +52,15 @@ export default class Python_poetry extends Base_pyproject {
|
|
|
41
52
|
*/
|
|
42
53
|
// eslint-disable-next-line no-unused-vars
|
|
43
54
|
async _getDependencyData(manifestDir, _workspaceDir, parsed, opts) {
|
|
55
|
+
let poetryBin = getCustomPath('poetry', opts);
|
|
56
|
+
let envBypass = environmentVariableIsPopulated('TRUSTIFY_DA_POETRY_SHOW_TREE')
|
|
57
|
+
&& environmentVariableIsPopulated('TRUSTIFY_DA_POETRY_SHOW_ALL');
|
|
58
|
+
if (!envBypass) {
|
|
59
|
+
this._verifyPoetryAccessible(poetryBin);
|
|
60
|
+
}
|
|
44
61
|
let hasDevGroup = !!(parsed.tool?.poetry?.group?.dev || parsed.tool?.poetry?.['dev-dependencies']);
|
|
45
|
-
let treeOutput = this._getPoetryShowTreeOutput(manifestDir, hasDevGroup,
|
|
46
|
-
let showAllOutput = this._getPoetryShowAllOutput(manifestDir,
|
|
62
|
+
let treeOutput = this._getPoetryShowTreeOutput(manifestDir, hasDevGroup, poetryBin);
|
|
63
|
+
let showAllOutput = this._getPoetryShowAllOutput(manifestDir, poetryBin);
|
|
47
64
|
let versionMap = this._parsePoetryShowAll(showAllOutput);
|
|
48
65
|
let lockDir = this._findLockFileDir(manifestDir, opts);
|
|
49
66
|
let markerData = this._extractMarkerData(lockDir, parsed);
|
|
@@ -53,14 +70,13 @@ export default class Python_poetry extends Base_pyproject {
|
|
|
53
70
|
* Get poetry show --tree output.
|
|
54
71
|
* @param {string} manifestDir
|
|
55
72
|
* @param {boolean} hasDevGroup
|
|
56
|
-
* @param {
|
|
73
|
+
* @param {string} poetryBin
|
|
57
74
|
* @returns {string}
|
|
58
75
|
*/
|
|
59
|
-
_getPoetryShowTreeOutput(manifestDir, hasDevGroup,
|
|
76
|
+
_getPoetryShowTreeOutput(manifestDir, hasDevGroup, poetryBin) {
|
|
60
77
|
if (environmentVariableIsPopulated('TRUSTIFY_DA_POETRY_SHOW_TREE')) {
|
|
61
78
|
return Buffer.from(process.env['TRUSTIFY_DA_POETRY_SHOW_TREE'], 'base64').toString('utf-8');
|
|
62
79
|
}
|
|
63
|
-
let poetryBin = getCustomPath('poetry', opts);
|
|
64
80
|
let args = ['show', '--tree', '--no-ansi'];
|
|
65
81
|
if (hasDevGroup) {
|
|
66
82
|
args.push('--without', 'dev');
|
|
@@ -70,14 +86,13 @@ export default class Python_poetry extends Base_pyproject {
|
|
|
70
86
|
/**
|
|
71
87
|
* Get poetry show --all output (flat list with resolved versions).
|
|
72
88
|
* @param {string} manifestDir
|
|
73
|
-
* @param {
|
|
89
|
+
* @param {string} poetryBin
|
|
74
90
|
* @returns {string}
|
|
75
91
|
*/
|
|
76
|
-
_getPoetryShowAllOutput(manifestDir,
|
|
92
|
+
_getPoetryShowAllOutput(manifestDir, poetryBin) {
|
|
77
93
|
if (environmentVariableIsPopulated('TRUSTIFY_DA_POETRY_SHOW_ALL')) {
|
|
78
94
|
return Buffer.from(process.env['TRUSTIFY_DA_POETRY_SHOW_ALL'], 'base64').toString('utf-8');
|
|
79
95
|
}
|
|
80
|
-
let poetryBin = getCustomPath('poetry', opts);
|
|
81
96
|
return invokeCommand(poetryBin, ['show', '--no-ansi', '--all'], { cwd: manifestDir }).toString();
|
|
82
97
|
}
|
|
83
98
|
/**
|
|
@@ -111,11 +126,12 @@ export default class Python_poetry extends Base_pyproject {
|
|
|
111
126
|
* → transitiveMarkers['click']['colorama'] = "sys_platform == 'win32'"
|
|
112
127
|
* @param {string|null} lockDir
|
|
113
128
|
* @param {object} parsed - parsed pyproject.toml
|
|
114
|
-
* @returns {{directMarkers: Map<string, string>, transitiveMarkers: Map<string, Map<string, string>>}}
|
|
129
|
+
* @returns {{directMarkers: Map<string, string>, transitiveMarkers: Map<string, Map<string, string>>, hashMap: Map<string, Array<{alg: string, content: string}>>}}
|
|
115
130
|
*/
|
|
116
131
|
_extractMarkerData(lockDir, parsed) {
|
|
117
132
|
let directMarkers = new Map();
|
|
118
133
|
let transitiveMarkers = new Map();
|
|
134
|
+
let hashMap = new Map();
|
|
119
135
|
// Extract markers from PEP 621 dependency strings: "name[extras]>=ver ; marker"
|
|
120
136
|
let deps = parsed.project?.dependencies || [];
|
|
121
137
|
for (let dep of deps) {
|
|
@@ -142,18 +158,40 @@ export default class Python_poetry extends Base_pyproject {
|
|
|
142
158
|
transitiveMarkers.get(pkgKey).set(this._canonicalize(depName), markers);
|
|
143
159
|
}
|
|
144
160
|
}
|
|
161
|
+
let sha256Hex = this._extractSha256FromFiles(pkg.files);
|
|
162
|
+
if (sha256Hex) {
|
|
163
|
+
hashMap.set(pkgKey, [{ alg: "SHA-256", content: sha256Hex }]);
|
|
164
|
+
}
|
|
145
165
|
}
|
|
146
166
|
}
|
|
147
167
|
}
|
|
148
|
-
return { directMarkers, transitiveMarkers };
|
|
168
|
+
return { directMarkers, transitiveMarkers, hashMap };
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Extract a SHA-256 hex digest from poetry.lock files array.
|
|
172
|
+
* Prefers the sdist (.tar.gz) hash; falls back to the first wheel hash.
|
|
173
|
+
* @param {Array<{file: string, hash: string}>} [files]
|
|
174
|
+
* @returns {string|null}
|
|
175
|
+
*/
|
|
176
|
+
_extractSha256FromFiles(files) {
|
|
177
|
+
if (!Array.isArray(files) || files.length === 0) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
let sdist = files.find(f => f.file.endsWith('.tar.gz'));
|
|
181
|
+
let entry = sdist || files[0];
|
|
182
|
+
let hash = entry.hash;
|
|
183
|
+
if (hash && hash.startsWith('sha256:')) {
|
|
184
|
+
return hash.slice(7);
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
149
187
|
}
|
|
150
188
|
/**
|
|
151
189
|
* Parse poetry show --tree output into a dependency graph structure.
|
|
152
190
|
*
|
|
153
191
|
* @param {string} treeOutput
|
|
154
192
|
* @param {Map<string, string>} versionMap - canonical name -> resolved version
|
|
155
|
-
* @param {{directMarkers: Map<string, string>, transitiveMarkers: Map<string, Map<string, string>>}} markerData
|
|
156
|
-
* @returns {{directDeps: string[], graph: Map<string, {name: string, version: string, children: string[]}>}}
|
|
193
|
+
* @param {{directMarkers: Map<string, string>, transitiveMarkers: Map<string, Map<string, string>>, hashMap: Map<string, Array<{alg: string, content: string}>>}} markerData
|
|
194
|
+
* @returns {{directDeps: string[], graph: Map<string, {name: string, version: string, children: string[], hashes?: Array<{alg: string, content: string}>}>}}
|
|
157
195
|
*/
|
|
158
196
|
_parsePoetryTree(treeOutput, versionMap, markerData) {
|
|
159
197
|
let lines = treeOutput.split(/\r?\n/);
|
|
@@ -179,7 +217,12 @@ export default class Python_poetry extends Base_pyproject {
|
|
|
179
217
|
}
|
|
180
218
|
directDeps.push(key);
|
|
181
219
|
if (!graph.has(key)) {
|
|
182
|
-
|
|
220
|
+
let entry = { name, version, children: [] };
|
|
221
|
+
let hashes = markerData.hashMap.get(key);
|
|
222
|
+
if (hashes) {
|
|
223
|
+
entry.hashes = hashes;
|
|
224
|
+
}
|
|
225
|
+
graph.set(key, entry);
|
|
183
226
|
}
|
|
184
227
|
currentDirectDep = key;
|
|
185
228
|
stack = [{ key, depth: -1 }];
|
|
@@ -223,7 +266,12 @@ export default class Python_poetry extends Base_pyproject {
|
|
|
223
266
|
throw new Error(`poetry: package '${depName}' has no resolved version`);
|
|
224
267
|
}
|
|
225
268
|
if (!graph.has(depKey)) {
|
|
226
|
-
|
|
269
|
+
let entry = { name: depName, version, children: [] };
|
|
270
|
+
let hashes = markerData.hashMap.get(depKey);
|
|
271
|
+
if (hashes) {
|
|
272
|
+
entry.hashes = hashes;
|
|
273
|
+
}
|
|
274
|
+
graph.set(depKey, entry);
|
|
227
275
|
}
|
|
228
276
|
if (parentKey) {
|
|
229
277
|
let parentEntry = graph.get(parentKey);
|
|
@@ -1,26 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discover all pyproject.toml manifest paths in a uv workspace.
|
|
3
|
+
* Parses `[tool.uv.workspace]` from root pyproject.toml and glob-expands member patterns.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} workspaceRoot - Absolute or relative path to workspace root (must contain pyproject.toml and uv.lock)
|
|
6
|
+
* @param {{ workspaceDiscoveryIgnore?: string[], TRUSTIFY_DA_WORKSPACE_DISCOVERY_IGNORE?: string, [key: string]: unknown }} [opts={}]
|
|
7
|
+
* @returns {Promise<string[]>} Paths to pyproject.toml files (absolute)
|
|
8
|
+
*/
|
|
9
|
+
export function discoverUvWorkspaceMembers(workspaceRoot: string, opts?: {
|
|
10
|
+
workspaceDiscoveryIgnore?: string[];
|
|
11
|
+
TRUSTIFY_DA_WORKSPACE_DISCOVERY_IGNORE?: string;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}): Promise<string[]>;
|
|
1
14
|
export default class Python_uv extends Base_pyproject {
|
|
2
15
|
/**
|
|
3
|
-
*
|
|
4
|
-
* @param {string}
|
|
5
|
-
* @param {
|
|
6
|
-
* @param {Object} opts
|
|
7
|
-
* @returns {Promise<{directDeps: string[], graph: Map<string, {name: string, version: string, children: string[]}>}>}
|
|
16
|
+
* Parse uv.lock and attach SHA-256 hashes to graph entries.
|
|
17
|
+
* @param {string} lockFilePath - path to uv.lock
|
|
18
|
+
* @param {Map<string, import('./base_pyproject.js').GraphEntry>} graph
|
|
8
19
|
*/
|
|
9
|
-
|
|
10
|
-
directDeps: string[];
|
|
11
|
-
graph: Map<string, {
|
|
12
|
-
name: string;
|
|
13
|
-
version: string;
|
|
14
|
-
children: string[];
|
|
15
|
-
}>;
|
|
16
|
-
}>;
|
|
20
|
+
_attachHashesFromLockFile(lockFilePath: string, graph: Map<string, import("./base_pyproject.js").GraphEntry>): Promise<void>;
|
|
17
21
|
/**
|
|
18
22
|
* Get the uv export output, either from env var or by running the command.
|
|
19
23
|
* @param {string} manifestDir
|
|
20
24
|
* @param {Object} opts
|
|
21
25
|
* @returns {string}
|
|
22
26
|
*/
|
|
23
|
-
_getUvExportOutput(manifestDir: string, opts:
|
|
27
|
+
_getUvExportOutput(manifestDir: string, opts: Object): string;
|
|
24
28
|
/**
|
|
25
29
|
* Parse uv export output into a dependency graph using tree-sitter-requirements
|
|
26
30
|
* for package/version extraction and string parsing for "# via" comments.
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import fg from 'fast-glob';
|
|
3
4
|
import { parse as parseToml } from 'smol-toml';
|
|
4
5
|
import { environmentVariableIsPopulated, getCustomPath, invokeCommand } from '../tools.js';
|
|
6
|
+
import { filterManifestPathsByDiscoveryIgnore, resolveWorkspaceDiscoveryIgnore, toManifestGlobPatterns } from '../workspace.js';
|
|
5
7
|
import Base_pyproject from './base_pyproject.js';
|
|
6
8
|
import { evaluateMarker } from './marker_evaluator.js';
|
|
7
9
|
import { getParser, getPinnedVersionQuery } from './requirements_parser.js';
|
|
@@ -19,12 +21,54 @@ export default class Python_uv extends Base_pyproject {
|
|
|
19
21
|
* @param {string} workspaceDir - workspace root (for resolving editable install paths)
|
|
20
22
|
* @param {object} parsed - parsed pyproject.toml
|
|
21
23
|
* @param {Object} opts
|
|
22
|
-
* @returns {Promise<{directDeps: string[], graph: Map<string,
|
|
24
|
+
* @returns {Promise<{directDeps: string[], graph: Map<string, import('./base_pyproject.js').GraphEntry>}>}
|
|
23
25
|
*/
|
|
24
26
|
async _getDependencyData(manifestDir, workspaceDir, parsed, opts) {
|
|
25
27
|
let projectName = this._getProjectName(parsed);
|
|
26
28
|
let uvOutput = this._getUvExportOutput(manifestDir, opts);
|
|
27
|
-
|
|
29
|
+
let { directDeps, graph } = await this._parseUvExport(uvOutput, projectName, workspaceDir);
|
|
30
|
+
await this._attachHashesFromLockFile(path.join(workspaceDir, 'uv.lock'), graph);
|
|
31
|
+
return { directDeps, graph };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Parse uv.lock and attach SHA-256 hashes to graph entries.
|
|
35
|
+
* @param {string} lockFilePath - path to uv.lock
|
|
36
|
+
* @param {Map<string, import('./base_pyproject.js').GraphEntry>} graph
|
|
37
|
+
*/
|
|
38
|
+
async _attachHashesFromLockFile(lockFilePath, graph) {
|
|
39
|
+
let lockContent;
|
|
40
|
+
try {
|
|
41
|
+
lockContent = await fs.promises.readFile(lockFilePath, 'utf-8');
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
parsed = parseToml(lockContent);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
let packages = parsed.package;
|
|
54
|
+
if (!Array.isArray(packages)) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
for (let pkg of packages) {
|
|
58
|
+
if (!pkg.name) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
let hashStr = pkg.sdist?.hash || pkg.wheels?.[0]?.hash;
|
|
62
|
+
if (!hashStr || !hashStr.startsWith('sha256:')) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
let key = this._canonicalize(pkg.name);
|
|
66
|
+
let entry = graph.get(key);
|
|
67
|
+
if (!entry || entry.hashes) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
entry.hashes = [{ alg: "SHA-256", content: hashStr.slice(7) }];
|
|
71
|
+
}
|
|
28
72
|
}
|
|
29
73
|
/**
|
|
30
74
|
* Get the uv export output, either from env var or by running the command.
|
|
@@ -158,3 +202,68 @@ export default class Python_uv extends Base_pyproject {
|
|
|
158
202
|
return { directDeps, graph };
|
|
159
203
|
}
|
|
160
204
|
}
|
|
205
|
+
const DEFAULT_UV_DISCOVERY_IGNORE = [
|
|
206
|
+
'**/__pycache__/**',
|
|
207
|
+
'**/.venv/**',
|
|
208
|
+
];
|
|
209
|
+
/**
|
|
210
|
+
* Discover all pyproject.toml manifest paths in a uv workspace.
|
|
211
|
+
* Parses `[tool.uv.workspace]` from root pyproject.toml and glob-expands member patterns.
|
|
212
|
+
*
|
|
213
|
+
* @param {string} workspaceRoot - Absolute or relative path to workspace root (must contain pyproject.toml and uv.lock)
|
|
214
|
+
* @param {{ workspaceDiscoveryIgnore?: string[], TRUSTIFY_DA_WORKSPACE_DISCOVERY_IGNORE?: string, [key: string]: unknown }} [opts={}]
|
|
215
|
+
* @returns {Promise<string[]>} Paths to pyproject.toml files (absolute)
|
|
216
|
+
*/
|
|
217
|
+
export async function discoverUvWorkspaceMembers(workspaceRoot, opts = {}) {
|
|
218
|
+
const root = path.resolve(workspaceRoot);
|
|
219
|
+
const rootPyproject = path.join(root, 'pyproject.toml');
|
|
220
|
+
const uvLock = path.join(root, 'uv.lock');
|
|
221
|
+
if (!fs.existsSync(rootPyproject) || !fs.existsSync(uvLock)) {
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
224
|
+
let parsed;
|
|
225
|
+
try {
|
|
226
|
+
parsed = parseToml(fs.readFileSync(rootPyproject, 'utf-8'));
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
return [];
|
|
230
|
+
}
|
|
231
|
+
const workspaceConfig = parsed?.tool?.uv?.workspace;
|
|
232
|
+
if (!workspaceConfig) {
|
|
233
|
+
return [];
|
|
234
|
+
}
|
|
235
|
+
const memberPatterns = workspaceConfig.members;
|
|
236
|
+
if (!Array.isArray(memberPatterns) || memberPatterns.length === 0) {
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
const excludePatterns = Array.isArray(workspaceConfig.exclude) ? workspaceConfig.exclude : [];
|
|
240
|
+
const excludeGlobs = excludePatterns
|
|
241
|
+
.filter(p => typeof p === 'string' && p.trim())
|
|
242
|
+
.map(p => `${p.trim()}/pyproject.toml`);
|
|
243
|
+
const ignorePatterns = [...resolveWorkspaceDiscoveryIgnore(opts), ...DEFAULT_UV_DISCOVERY_IGNORE];
|
|
244
|
+
const globOpts = {
|
|
245
|
+
cwd: root,
|
|
246
|
+
absolute: true,
|
|
247
|
+
onlyFiles: true,
|
|
248
|
+
ignore: [...ignorePatterns, ...excludeGlobs],
|
|
249
|
+
followSymbolicLinks: false,
|
|
250
|
+
};
|
|
251
|
+
const patterns = toManifestGlobPatterns(memberPatterns.filter(p => typeof p === 'string'), 'pyproject.toml');
|
|
252
|
+
const manifestPaths = await fg(patterns, globOpts);
|
|
253
|
+
if (!manifestPaths.includes(rootPyproject) && hasProjectMetadata(parsed)) {
|
|
254
|
+
manifestPaths.unshift(rootPyproject);
|
|
255
|
+
}
|
|
256
|
+
return filterManifestPathsByDiscoveryIgnore(manifestPaths, root, ignorePatterns);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* @param {import('smol-toml').TomlTable} parsedPyProject
|
|
260
|
+
* @returns {boolean}
|
|
261
|
+
*/
|
|
262
|
+
function hasProjectMetadata(parsedPyProject) {
|
|
263
|
+
try {
|
|
264
|
+
return typeof parsedPyProject?.project?.name === 'string' && parsedPyProject.project.name.trim() !== '';
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -4,9 +4,10 @@ declare namespace _default {
|
|
|
4
4
|
export { provideComponent };
|
|
5
5
|
export { provideStack };
|
|
6
6
|
export { readLicenseFromManifest };
|
|
7
|
+
export function packageManagerName(): string;
|
|
7
8
|
}
|
|
8
9
|
export default _default;
|
|
9
|
-
export type Provided =
|
|
10
|
+
export type Provided = any;
|
|
10
11
|
/**
|
|
11
12
|
* @param {string} manifestName - the subject manifest name-type
|
|
12
13
|
* @returns {boolean} - return true if `Cargo.toml` is the manifest name-type
|
|
@@ -46,7 +47,10 @@ declare function provideStack(manifest: string, opts?: {}): Provided;
|
|
|
46
47
|
* Read project license from Cargo.toml, with fallback to LICENSE file.
|
|
47
48
|
* Supports the `license` field under `[package]` (single crate / workspace
|
|
48
49
|
* with root) and under `[workspace.package]` (virtual workspaces).
|
|
50
|
+
* When a member crate uses `license = { workspace = true }`, the actual
|
|
51
|
+
* license is resolved from the workspace root Cargo.toml.
|
|
49
52
|
* @param {string} manifestPath - path to Cargo.toml
|
|
53
|
+
* @param {object} [metadata] - cargo metadata output (needed for workspace license resolution)
|
|
50
54
|
* @returns {string|null} SPDX identifier or null
|
|
51
55
|
*/
|
|
52
|
-
declare function readLicenseFromManifest(manifestPath: string): string | null;
|
|
56
|
+
declare function readLicenseFromManifest(manifestPath: string, metadata?: object): string | null;
|