screwdriver-api 4.1.180 → 4.1.184

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.
@@ -60,7 +60,8 @@ async function registerResourcePlugins(server, config) {
60
60
  'isAdmin',
61
61
  'shutdown',
62
62
  'release',
63
- 'validator'
63
+ 'validator',
64
+ 'processHooks'
64
65
  ];
65
66
 
66
67
  if (hoek.reach(config, 'coverage.coveragePlugin')) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screwdriver-api",
3
- "version": "4.1.180",
3
+ "version": "4.1.184",
4
4
  "description": "API server for the Screwdriver.cd service",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+
3
+ const boom = require('@hapi/boom');
4
+ const joi = require('joi');
5
+ const schema = require('screwdriver-data-schema');
6
+ const idSchema = schema.models.build.base.extract('id');
7
+
8
+ module.exports = config => ({
9
+ method: 'POST',
10
+ path: '/builds/{id}/artifacts/unzip',
11
+ options: {
12
+ description: 'Extract a ZIP for build artifacts',
13
+ notes: 'Extract a specific ZIP for build artifacts',
14
+ tags: ['api', 'builds', 'artifacts'],
15
+ auth: {
16
+ strategies: ['token'],
17
+ scope: ['build']
18
+ },
19
+
20
+ handler: async (req, h) => {
21
+ const buildId = req.params.id;
22
+ const { username, scope } = req.auth.credentials;
23
+ const isBuild = scope.includes('build');
24
+ const { buildFactory } = req.server.app;
25
+
26
+ if (isBuild && username !== buildId) {
27
+ return boom.forbidden(`Credential only valid for ${username}`);
28
+ }
29
+
30
+ return buildFactory
31
+ .get(buildId)
32
+ .then(async buildModel => {
33
+ if (!buildModel) {
34
+ throw boom.notFound('Build does not exist');
35
+ }
36
+ await buildModel.unzipArtifacts();
37
+ return h.response().code(202);
38
+ })
39
+ .catch(err => {
40
+ throw err;
41
+ });
42
+ },
43
+ validate: {
44
+ params: joi.object({
45
+ id: idSchema
46
+ })
47
+ }
48
+ }
49
+ });
@@ -11,6 +11,7 @@ const createRoute = require('./create');
11
11
  const stepGetRoute = require('./steps/get');
12
12
  const listStepsRoute = require('./steps/list');
13
13
  const artifactGetRoute = require('./artifacts/get');
14
+ const artifactUnzipRoute = require('./artifacts/unzip');
14
15
  const stepUpdateRoute = require('./steps/update');
15
16
  const stepLogsRoute = require('./steps/logs');
16
17
  const listSecretsRoute = require('./listSecrets');
@@ -528,11 +529,13 @@ async function handleNewBuild({ done, hasFailure, newBuild, jobName, pipelineId
528
529
  return null;
529
530
  }
530
531
 
531
- // If all join builds finished successfully, start new build
532
- newBuild.status = 'QUEUED';
533
- const queuedBuild = await newBuild.update();
532
+ // If all join builds finished successfully and it's clear that a new build has not been started before, start new build
533
+ if ([ 'CREATED', null, undefined ].includes(newBuild.status)) {
534
+ newBuild.status = 'QUEUED';
535
+ const queuedBuild = await newBuild.update();
534
536
 
535
- return queuedBuild.start();
537
+ return queuedBuild.start();
538
+ }
536
539
  }
537
540
 
538
541
  return null;
@@ -1110,7 +1113,8 @@ const buildsPlugin = {
1110
1113
  listSecretsRoute(),
1111
1114
  tokenRoute(),
1112
1115
  metricsRoute(),
1113
- artifactGetRoute(options)
1116
+ artifactGetRoute(options),
1117
+ artifactUnzipRoute(),
1114
1118
  ]);
1115
1119
  }
1116
1120
  };
@@ -32,6 +32,9 @@ server.register({
32
32
  #### Returns a list of builds associated with the event
33
33
  `GET /events/{id}/builds`
34
34
 
35
+ `GET /events/{id}/builds?fetchSteps=false&readOnly=true`
36
+
37
+
35
38
  #### Get build metrics for a single event
36
39
  `GET /events/{id}/metrics`
37
40
 
@@ -149,7 +149,7 @@ module.exports = () => ({
149
149
 
150
150
  const [files, prInfo] = await Promise.all([
151
151
  scm.getChangedFiles({
152
- payload: null,
152
+ webhookConfig: null,
153
153
  type: 'pr',
154
154
  ...scmConfig
155
155
  }),
@@ -24,14 +24,23 @@ module.exports = () => ({
24
24
  handler: async (request, h) => {
25
25
  const { eventFactory } = request.server.app;
26
26
  const event = await eventFactory.get(request.params.id);
27
+ const { fetchSteps, readOnly } = request.query;
27
28
 
28
29
  if (!event) {
29
30
  throw boom.notFound('Event does not exist');
30
31
  }
31
32
 
32
- const buildsModel = await event.getBuilds();
33
+ const config = readOnly ? { readOnly: true } : {};
33
34
 
34
- const data = await Promise.all(buildsModel.map(async buildModel => buildModel.toJsonWithSteps()));
35
+ const buildsModel = await event.getBuilds(config);
36
+
37
+ let data;
38
+
39
+ if (fetchSteps) {
40
+ data = await Promise.all(buildsModel.map(async buildModel => buildModel.toJsonWithSteps()));
41
+ } else {
42
+ data = await Promise.all(buildsModel.map(async buildModel => buildModel.toJson()));
43
+ }
35
44
 
36
45
  return h.response(data);
37
46
  },
@@ -41,7 +50,21 @@ module.exports = () => ({
41
50
  validate: {
42
51
  params: joi.object({
43
52
  id: eventIdSchema
44
- })
53
+ }),
54
+ query: schema.api.pagination.concat(
55
+ joi.object({
56
+ readOnly: joi
57
+ .boolean()
58
+ .truthy('true')
59
+ .falsy('false')
60
+ .default(false),
61
+ fetchSteps: joi
62
+ .boolean()
63
+ .truthy('true')
64
+ .falsy('false')
65
+ .default(true)
66
+ })
67
+ )
45
68
  }
46
69
  }
47
70
  });
@@ -46,6 +46,8 @@ Example payload:
46
46
  #### Get list of builds for a single job
47
47
  `GET /jobs/{id}/builds`
48
48
 
49
+ `GET /jobs/{id}/builds?fetchSteps=false&readOnly=true`
50
+
49
51
  `GET /jobs/{id}/builds?page=2&count=30&sort=ascending`
50
52
 
51
53
  `GET /jobs/{id}/builds?page=2&count=30&sort=ascending&sortBy=id`
@@ -23,7 +23,7 @@ module.exports = () => ({
23
23
 
24
24
  handler: async (request, h) => {
25
25
  const factory = request.server.app.jobFactory;
26
- const { sort, sortBy, page, count } = request.query;
26
+ const { sort, sortBy, page, count, fetchSteps, readOnly } = request.query;
27
27
 
28
28
  return factory
29
29
  .get(request.params.id)
@@ -32,7 +32,9 @@ module.exports = () => ({
32
32
  throw boom.notFound('Job does not exist');
33
33
  }
34
34
 
35
- const config = { sort, sortBy: 'createTime' };
35
+ const config = readOnly
36
+ ? { sort, sortBy: 'createTime', readOnly: true }
37
+ : { sort, sortBy: 'createTime' };
36
38
 
37
39
  if (sortBy) {
38
40
  config.sortBy = sortBy;
@@ -45,7 +47,13 @@ module.exports = () => ({
45
47
  return job.getBuilds(config);
46
48
  })
47
49
  .then(async builds => {
48
- const data = await Promise.all(builds.map(b => b.toJsonWithSteps()));
50
+ let data;
51
+
52
+ if (fetchSteps) {
53
+ data = await Promise.all(builds.map(b => b.toJsonWithSteps()));
54
+ } else {
55
+ data = await Promise.all(builds.map(b => b.toJson()));
56
+ }
49
57
 
50
58
  return h.response(data);
51
59
  })
@@ -60,7 +68,20 @@ module.exports = () => ({
60
68
  params: joi.object({
61
69
  id: jobIdSchema
62
70
  }),
63
- query: schema.api.pagination
71
+ query: schema.api.pagination.concat(
72
+ joi.object({
73
+ readOnly: joi
74
+ .boolean()
75
+ .truthy('true')
76
+ .falsy('false')
77
+ .default(false),
78
+ fetchSteps: joi
79
+ .boolean()
80
+ .truthy('true')
81
+ .falsy('false')
82
+ .default(true)
83
+ })
84
+ )
64
85
  }
65
86
  }
66
87
  });
@@ -0,0 +1,33 @@
1
+ # Process Hooks Plugin
2
+ > Hapi processHooks plugin for the Screwdriver API
3
+
4
+ ## Usage
5
+
6
+ ### Register plugin
7
+
8
+ ```javascript
9
+ const Hapi = require('@hapi/hapi');
10
+ const server = new Hapi.Server();
11
+ const processHooksPlugin = require('./');
12
+
13
+ server.connection({ port: 3000 });
14
+
15
+ server.register({
16
+ register: processHooksPlugin,
17
+ options: {}
18
+ }, () => {
19
+ server.start((err) => {
20
+ if (err) {
21
+ throw err;
22
+ }
23
+ console.log('Server running at:', server.info.uri);
24
+ });
25
+ });
26
+ ```
27
+
28
+ ### Routes
29
+
30
+ #### Start pipeline events from scm webhook config
31
+
32
+ `POST /processHooks`
33
+
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+
3
+ const logger = require('screwdriver-logger');
4
+ const { startHookEvent } = require('../webhooks/helper');
5
+
6
+ /**
7
+ * Process Hooks API Plugin
8
+ * - Start pipeline events with scm webhook config via queue-service
9
+ * @method register
10
+ * @param {Hapi} server Hapi Server
11
+ * @param {Object} options Configuration
12
+ * @param {Function} next Function to call when done
13
+ */
14
+ const processHooksPlugin = {
15
+ name: 'processHooks',
16
+ async register(server, options) {
17
+ server.route({
18
+ method: 'POST',
19
+ path: '/processHooks',
20
+ options: {
21
+ description: 'Handle process hook events',
22
+ notes: 'Acts on pull request, pushes, comments, etc.',
23
+ tags: ['api', 'processHook'],
24
+ auth: {
25
+ strategies: ['token'],
26
+ scope: ['webhook_worker']
27
+ },
28
+ plugins: {
29
+ 'hapi-rate-limit': {
30
+ enabled: false
31
+ }
32
+ },
33
+ handler: async (request, h) => {
34
+ try {
35
+ return await startHookEvent(request, h, request.payload);
36
+ } catch (err) {
37
+ logger.error(`Error starting hook events for ${request.payload.hookId}:${err}`);
38
+
39
+ throw err;
40
+ }
41
+ }
42
+ }
43
+ });
44
+ }
45
+ };
46
+
47
+ module.exports = processHooksPlugin;