screwdriver-api 8.0.161 → 8.0.162
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/package.json +1 -2
- package/plugins/versions.js +281 -52
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "screwdriver-api",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.162",
|
|
4
4
|
"description": "API server for the Screwdriver.cd service",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -100,7 +100,6 @@
|
|
|
100
100
|
"joi": "^17.7.0",
|
|
101
101
|
"js-yaml": "^3.14.1",
|
|
102
102
|
"jsonwebtoken": "^9.0.0",
|
|
103
|
-
"license-checker": "^25.0.1",
|
|
104
103
|
"lodash.isempty": "^4.4.0",
|
|
105
104
|
"lodash.mergewith": "^4.6.2",
|
|
106
105
|
"ndjson": "^2.0.0",
|
package/plugins/versions.js
CHANGED
|
@@ -3,70 +3,299 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const process = require('process');
|
|
6
|
-
const checker = require('license-checker');
|
|
7
6
|
const VError = require('verror');
|
|
8
7
|
const schema = require('screwdriver-data-schema');
|
|
9
8
|
|
|
9
|
+
const UNKNOWN = 'UNKNOWN';
|
|
10
|
+
const UNLICENSED = 'UNLICENSED';
|
|
11
|
+
const SD_REGEX = /^screwdriver-/;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Read JSON file
|
|
15
|
+
* @param {string} filePath File path
|
|
16
|
+
* @returns {Object}
|
|
17
|
+
*/
|
|
18
|
+
function readJson(filePath) {
|
|
19
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolve project root path
|
|
24
|
+
* @returns {string}
|
|
25
|
+
*/
|
|
26
|
+
function findProjectRoot() {
|
|
27
|
+
if (fs.existsSync(path.resolve(process.cwd(), './node_modules'))) {
|
|
28
|
+
return process.cwd();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return path.resolve(process.cwd(), '../..');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Normalize repository url
|
|
36
|
+
* @param {Object|string} repository Repository URL
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
function normalizeRepository(repository) {
|
|
40
|
+
const url = typeof repository === 'string' ? repository : repository && repository.url;
|
|
41
|
+
|
|
42
|
+
if (!url) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const normalizedUrl = url
|
|
47
|
+
.replace('http:', 'https:')
|
|
48
|
+
.replace('ssh://github.com', 'https://github.com')
|
|
49
|
+
.replace('git+ssh://git@', 'git://')
|
|
50
|
+
.replace('git+https://github.com', 'https://github.com')
|
|
51
|
+
.replace('git://github.com', 'https://github.com')
|
|
52
|
+
.replace('git@github.com:', 'https://github.com/')
|
|
53
|
+
.replace('www.github.com', 'github.com')
|
|
54
|
+
.replace('github:', '')
|
|
55
|
+
.replace(/\.git$/, '');
|
|
56
|
+
|
|
57
|
+
return normalizedUrl.startsWith('http') ? normalizedUrl : `https://github.com/${normalizedUrl}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
10
60
|
/**
|
|
11
|
-
*
|
|
12
|
-
* @
|
|
13
|
-
* @
|
|
61
|
+
* Normalize license
|
|
62
|
+
* @param {Array|string} value license
|
|
63
|
+
* @returns {string}
|
|
14
64
|
*/
|
|
65
|
+
function normalizeLicense(value) {
|
|
66
|
+
if (!value) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
const licenses = value
|
|
72
|
+
.map(item => {
|
|
73
|
+
if (typeof item === 'string') {
|
|
74
|
+
return item;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return item && (item.type || item.name);
|
|
78
|
+
})
|
|
79
|
+
.filter(Boolean);
|
|
80
|
+
|
|
81
|
+
return licenses.length === 1 ? licenses[0] : licenses;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (typeof value === 'object') {
|
|
85
|
+
return value.type || value.name;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Find license description from the document
|
|
93
|
+
* @param {string} text document
|
|
94
|
+
* @returns {string}
|
|
95
|
+
*/
|
|
96
|
+
function detectLicenseFromText(text) {
|
|
97
|
+
if (!text) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const body = text.toLowerCase();
|
|
102
|
+
|
|
103
|
+
if (body.includes('mit license')) {
|
|
104
|
+
return 'MIT';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (body.includes('apache license') && body.includes('version 2.0')) {
|
|
108
|
+
return 'Apache-2.0';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (body.includes('isc license')) {
|
|
112
|
+
return 'ISC';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (body.includes('bsd 3-clause')) {
|
|
116
|
+
return 'BSD-3-Clause';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (body.includes('bsd 2-clause')) {
|
|
120
|
+
return 'BSD-2-Clause';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Find license files
|
|
128
|
+
* @param {string} dir base directory
|
|
129
|
+
* @returns {Array}
|
|
130
|
+
*/
|
|
131
|
+
function getLicenseFiles(dir) {
|
|
132
|
+
return fs
|
|
133
|
+
.readdirSync(dir)
|
|
134
|
+
.filter(filename => {
|
|
135
|
+
const upper = filename.toUpperCase();
|
|
136
|
+
const basename = path.basename(upper, path.extname(upper));
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
basename === 'LICENSE' || basename === 'LICENCE' || basename === 'COPYING' || basename === 'COPYRIGHT'
|
|
140
|
+
);
|
|
141
|
+
})
|
|
142
|
+
.sort();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Find package directory
|
|
147
|
+
* @param {string} fromDir base directory
|
|
148
|
+
* @param {string} name package name
|
|
149
|
+
* @returns {string}
|
|
150
|
+
*/
|
|
151
|
+
function resolvePackageDir(fromDir, name) {
|
|
152
|
+
let current = fromDir;
|
|
153
|
+
|
|
154
|
+
while (current && current !== path.dirname(current)) {
|
|
155
|
+
const candidate = path.join(current, 'node_modules', name);
|
|
156
|
+
|
|
157
|
+
if (fs.existsSync(path.join(candidate, 'package.json'))) {
|
|
158
|
+
return candidate;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
current = path.dirname(current);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* List up packages infomation
|
|
169
|
+
* @param {string} pkgDir base dirctory
|
|
170
|
+
* @param {object} data result store
|
|
171
|
+
* @returns {object}
|
|
172
|
+
*/
|
|
173
|
+
function collectPackage(pkgDir, data) {
|
|
174
|
+
const packageJsonPath = path.join(pkgDir, 'package.json');
|
|
175
|
+
|
|
176
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
177
|
+
return data;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const json = readJson(packageJsonPath);
|
|
181
|
+
const key = `${json.name}@${json.version}`;
|
|
182
|
+
|
|
183
|
+
if (!json.name || !json.version || data[key]) {
|
|
184
|
+
return data;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const moduleInfo = {
|
|
188
|
+
licenses: UNKNOWN
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
if (json.private) {
|
|
192
|
+
moduleInfo.private = true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const repository = normalizeRepository(json.repository);
|
|
196
|
+
|
|
197
|
+
if (repository) {
|
|
198
|
+
moduleInfo.repository = repository;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let licenses = normalizeLicense(json.license || json.licenses);
|
|
202
|
+
|
|
203
|
+
if (!licenses && json.readme) {
|
|
204
|
+
licenses = detectLicenseFromText(json.readme);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const readmePath = path.join(pkgDir, 'README.md');
|
|
208
|
+
|
|
209
|
+
if (!licenses && fs.existsSync(readmePath)) {
|
|
210
|
+
licenses = detectLicenseFromText(fs.readFileSync(readmePath, 'utf8'));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
getLicenseFiles(pkgDir).forEach((filename, index) => {
|
|
214
|
+
const licenseFile = path.join(pkgDir, filename);
|
|
215
|
+
|
|
216
|
+
if (!fs.lstatSync(licenseFile).isFile()) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const content = fs.readFileSync(licenseFile, 'utf8');
|
|
221
|
+
|
|
222
|
+
if (!licenses || String(licenses).includes(UNKNOWN) || String(licenses).indexOf('Custom:') === 0) {
|
|
223
|
+
licenses = detectLicenseFromText(content) || `Custom: ${filename}`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (index === 0) {
|
|
227
|
+
moduleInfo.licenseFile = licenseFile;
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
moduleInfo.licenses = licenses || UNKNOWN;
|
|
232
|
+
|
|
233
|
+
if (json.private) {
|
|
234
|
+
moduleInfo.licenses = UNLICENSED;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
data[key] = moduleInfo;
|
|
238
|
+
|
|
239
|
+
[json.dependencies, json.optionalDependencies, json.peerDependencies].forEach(dependencies => {
|
|
240
|
+
Object.keys(dependencies || {}).forEach(depName => {
|
|
241
|
+
const childDir = resolvePackageDir(pkgDir, depName);
|
|
242
|
+
|
|
243
|
+
if (childDir) {
|
|
244
|
+
collectPackage(childDir, data);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return data;
|
|
250
|
+
}
|
|
251
|
+
|
|
15
252
|
const versionsTemplate = {
|
|
16
253
|
name: 'versions',
|
|
17
|
-
async register(server) {
|
|
18
|
-
// Designed to match Screwdriver specific packages
|
|
19
|
-
const SD_REGEX = /^screwdriver-/;
|
|
20
|
-
let start = process.cwd();
|
|
21
254
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
255
|
+
async register(server) {
|
|
256
|
+
try {
|
|
257
|
+
const data = collectPackage(findProjectRoot(), {});
|
|
25
258
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
},
|
|
32
|
-
(err, json) => {
|
|
33
|
-
if (err) {
|
|
34
|
-
return new VError(err, 'Unable to load package dependencies');
|
|
35
|
-
}
|
|
36
|
-
const depArray = Object.keys(json).map(key => ({ name: key, ...json[key] }));
|
|
37
|
-
const depDisplay = depArray.map(dep => ({
|
|
38
|
-
name: dep.name.split('@').slice(0, -1).join('@'),
|
|
39
|
-
repository: dep.repository || 'UNKNOWN',
|
|
40
|
-
licenses: dep.licenses || 'UNKNOWN'
|
|
259
|
+
const depArray = Object.keys(data)
|
|
260
|
+
.sort()
|
|
261
|
+
.map(key => ({
|
|
262
|
+
name: key,
|
|
263
|
+
...data[key]
|
|
41
264
|
}));
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
265
|
+
|
|
266
|
+
const depDisplay = depArray.map(dep => ({
|
|
267
|
+
name: dep.name.split('@').slice(0, -1).join('@'),
|
|
268
|
+
repository: dep.repository || UNKNOWN,
|
|
269
|
+
licenses: dep.licenses || UNKNOWN
|
|
270
|
+
}));
|
|
271
|
+
|
|
272
|
+
const sdVersions = depArray.filter(dep => SD_REGEX.test(dep.name)).map(dep => dep.name);
|
|
273
|
+
|
|
274
|
+
return server.route({
|
|
275
|
+
method: 'GET',
|
|
276
|
+
path: '/versions',
|
|
277
|
+
handler: (request, h) =>
|
|
278
|
+
h.response({
|
|
279
|
+
versions: sdVersions,
|
|
280
|
+
licenses: depDisplay
|
|
281
|
+
}),
|
|
282
|
+
config: {
|
|
283
|
+
description: 'API Package Versions',
|
|
284
|
+
notes: 'Returns list of Screwdriver package versions and third-party dependencies',
|
|
285
|
+
tags: ['api'],
|
|
286
|
+
plugins: {
|
|
287
|
+
'hapi-rate-limit': {
|
|
288
|
+
enabled: false
|
|
65
289
|
}
|
|
290
|
+
},
|
|
291
|
+
response: {
|
|
292
|
+
schema: schema.api.versions
|
|
66
293
|
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
)
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
} catch (err) {
|
|
297
|
+
throw new VError(err, 'Unable to load package dependencies');
|
|
298
|
+
}
|
|
70
299
|
}
|
|
71
300
|
};
|
|
72
301
|
|