screwdriver-api 7.0.202 → 7.0.203
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "screwdriver-api",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.203",
|
|
4
4
|
"description": "API server for the Screwdriver.cd service",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -83,10 +83,12 @@
|
|
|
83
83
|
"@hapi/inert": "^7.0.0",
|
|
84
84
|
"@hapi/vision": "^7.0.0",
|
|
85
85
|
"@promster/hapi": "^14.0.0",
|
|
86
|
+
"archiver": "^7.0.1",
|
|
86
87
|
"async": "^3.2.4",
|
|
87
88
|
"badge-maker": "^3.3.1",
|
|
88
89
|
"config": "^3.3.8",
|
|
89
90
|
"dayjs": "^1.11.7",
|
|
91
|
+
"got": "^11.8.3",
|
|
90
92
|
"hapi-auth-bearer-token": "^8.0.0",
|
|
91
93
|
"hapi-auth-jwt2": "^10.4.0",
|
|
92
94
|
"hapi-rate-limit": "^7.1.0",
|
package/plugins/auth/token.js
CHANGED
|
@@ -40,7 +40,7 @@ module.exports = () => ({
|
|
|
40
40
|
|
|
41
41
|
const build = await buildFactory.get(request.params.buildId);
|
|
42
42
|
const job = await jobFactory.get(build.jobId);
|
|
43
|
-
const pipeline = pipelineFactory.get(job.pipelineId);
|
|
43
|
+
const pipeline = await pipelineFactory.get(job.pipelineId);
|
|
44
44
|
|
|
45
45
|
profile = request.server.plugins.auth.generateProfile({
|
|
46
46
|
username: request.params.buildId,
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const archiver = require('archiver');
|
|
4
|
+
const boom = require('@hapi/boom');
|
|
5
|
+
const request = require('got');
|
|
6
|
+
const joi = require('joi');
|
|
7
|
+
const jwt = require('jsonwebtoken');
|
|
8
|
+
const logger = require('screwdriver-logger');
|
|
9
|
+
const { PassThrough } = require('stream');
|
|
10
|
+
const schema = require('screwdriver-data-schema');
|
|
11
|
+
const { v4: uuidv4 } = require('uuid');
|
|
12
|
+
const idSchema = schema.models.build.base.extract('id');
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
module.exports = config => ({
|
|
16
|
+
method: 'GET',
|
|
17
|
+
path: '/builds/{id}/artifacts',
|
|
18
|
+
options: {
|
|
19
|
+
description: 'Get a zipped file including all build artifacts',
|
|
20
|
+
notes: 'Redirects to store with proper token',
|
|
21
|
+
tags: ['api', 'builds', 'artifacts'],
|
|
22
|
+
auth: {
|
|
23
|
+
strategies: ['session', 'token'],
|
|
24
|
+
scope: ['user', 'build', 'pipeline']
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
handler: async (req, h) => {
|
|
28
|
+
const { name: artifact, id: buildId } = req.params;
|
|
29
|
+
const { credentials } = req.auth;
|
|
30
|
+
const { canAccessPipeline } = req.server.plugins.pipelines;
|
|
31
|
+
const { buildFactory, eventFactory } = req.server.app;
|
|
32
|
+
|
|
33
|
+
return buildFactory.get(buildId)
|
|
34
|
+
.then(build => {
|
|
35
|
+
if (!build) {
|
|
36
|
+
throw boom.notFound('Build does not exist');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return eventFactory.get(build.eventId);
|
|
40
|
+
})
|
|
41
|
+
.then(event => {
|
|
42
|
+
if (!event) {
|
|
43
|
+
throw boom.notFound('Event does not exist');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return canAccessPipeline(credentials, event.pipelineId, 'pull', req.server.app);
|
|
47
|
+
})
|
|
48
|
+
.then(async () => {
|
|
49
|
+
const token = jwt.sign({
|
|
50
|
+
buildId, artifact, scope: ['user']
|
|
51
|
+
}, config.authConfig.jwtPrivateKey, {
|
|
52
|
+
algorithm: 'RS256',
|
|
53
|
+
expiresIn: '10m',
|
|
54
|
+
jwtid: uuidv4()
|
|
55
|
+
});
|
|
56
|
+
const baseUrl = `${config.ecosystem.store}/v1/builds/${buildId}/ARTIFACTS`;
|
|
57
|
+
|
|
58
|
+
// Fetch the manifest
|
|
59
|
+
const manifest = await request({
|
|
60
|
+
url: `${baseUrl}/manifest.txt?token=${token}`,
|
|
61
|
+
method: 'GET'
|
|
62
|
+
}).text();
|
|
63
|
+
const manifestArray = manifest.trim().split('\n');
|
|
64
|
+
|
|
65
|
+
// Create a stream and set up archiver
|
|
66
|
+
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
67
|
+
// PassThrough stream to make archiver readable by Hapi
|
|
68
|
+
const passThrough = new PassThrough();
|
|
69
|
+
|
|
70
|
+
archive.on('error', (err) => {
|
|
71
|
+
logger.error('Archiver error:', err);
|
|
72
|
+
passThrough.emit('error', err); // Propagate the error to the PassThrough stream
|
|
73
|
+
});
|
|
74
|
+
passThrough.on('error', (err) => {
|
|
75
|
+
logger.error('PassThrough stream error:', err);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Pipe the archive to PassThrough so it can be sent as a response
|
|
79
|
+
archive.pipe(passThrough);
|
|
80
|
+
|
|
81
|
+
// Fetch the artifact files and append to the archive
|
|
82
|
+
try {
|
|
83
|
+
for (const file of manifestArray) {
|
|
84
|
+
if (file) {
|
|
85
|
+
const fileStream = request.stream(`${baseUrl}/${file}?token=${token}&type=download`);
|
|
86
|
+
|
|
87
|
+
fileStream.on('error', (err) => {
|
|
88
|
+
logger.error(`Error downloading file: ${file}`, err);
|
|
89
|
+
archive.emit('error', err);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
archive.append(fileStream, { name: file });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Finalize the archive after all files are appended
|
|
96
|
+
archive.finalize();
|
|
97
|
+
} catch (err) {
|
|
98
|
+
logger.error('Error while streaming artifact files:', err);
|
|
99
|
+
archive.emit('error', err);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Respond with the PassThrough stream (which is now readable by Hapi)
|
|
103
|
+
return h.response(passThrough)
|
|
104
|
+
.type('application/zip')
|
|
105
|
+
.header('Content-Disposition', 'attachment; filename="SD_ARTIFACTS.zip"');
|
|
106
|
+
})
|
|
107
|
+
.catch(err => {
|
|
108
|
+
throw err;
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
validate: {
|
|
112
|
+
params: joi.object({
|
|
113
|
+
id: idSchema
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
package/plugins/builds/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const createRoute = require('./create');
|
|
|
10
10
|
const stepGetRoute = require('./steps/get');
|
|
11
11
|
const listStepsRoute = require('./steps/list');
|
|
12
12
|
const artifactGetRoute = require('./artifacts/get');
|
|
13
|
+
const artifactGetAllRoute = require('./artifacts/getAll');
|
|
13
14
|
const artifactUnzipRoute = require('./artifacts/unzip');
|
|
14
15
|
const stepUpdateRoute = require('./steps/update');
|
|
15
16
|
const stepLogsRoute = require('./steps/logs');
|
|
@@ -508,6 +509,7 @@ const buildsPlugin = {
|
|
|
508
509
|
tokenRoute(),
|
|
509
510
|
metricsRoute(),
|
|
510
511
|
artifactGetRoute(options),
|
|
512
|
+
artifactGetAllRoute(options),
|
|
511
513
|
artifactUnzipRoute()
|
|
512
514
|
]);
|
|
513
515
|
}
|