screwdriver-api 8.0.47 → 8.0.49

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.
@@ -361,18 +361,32 @@ scms:
361
361
  # # SCM clone type (https or ssh)
362
362
  # cloneType: SCM_GITLAB_RO_CLONE_TYPE
363
363
  webhooks:
364
- # Obtains the SCM token for a given user. If a user does not have a valid SCM token registered with Screwdriver, it will use this user's token instead.
365
- username: SCM_USERNAME
366
- # Ignore commits made by these users
367
- ignoreCommitsBy:
368
- __name: IGNORE_COMMITS_BY
369
- __format: json
370
- # Restrict PR: all, none, branch, or fork
371
- restrictPR: RESTRICT_PR
372
- # Chain PR: true or false
373
- chainPR: CHAIN_PR
374
- # Upper limit on incoming uploads to builds
375
- maxBytes: WEBHOOK_MAX_BYTES
364
+ # Object keyed by scm name with value webhook settings.
365
+ # Value of webhook settings is an object with the following properties:
366
+ # Example:
367
+ # {
368
+ # "github": {
369
+ # # Obtains the SCM token for a given user. If a user does not have a valid SCM token registered with Screwdriver, it will use this user's token instead.
370
+ # "username": "sd-buildbot",
371
+ # # Ignore commits made by these users
372
+ # "ignoreCommitsBy": [],
373
+ # # Restrict PR: all, none, branch, or fork
374
+ # "restrictPR": "none",
375
+ # # Chain PR: true or false
376
+ # "chainPR": false,
377
+ # # Upper limit on incoming uploads to builds
378
+ # "maxBytes": 1048576 #1MB
379
+ # },
380
+ # "github.example.com": {
381
+ # "username": "someuser",
382
+ # "ignoreCommitsBy": ["someuser", "anotheruser"],
383
+ # "restrictPR": "branch",
384
+ # "chainPR": true,
385
+ # "maxBytes": 2097152 #2MB
386
+ # }
387
+ # }
388
+ __name: WEBHOOK_SETTINGS
389
+ __format: json
376
390
 
377
391
  bookends:
378
392
  # Object keyed by cluster name with value setup/teardown bookend.
@@ -277,18 +277,42 @@ scms: {}
277
277
  # accessToken: headlesstoken
278
278
  # # SCM clone type (https or ssh)
279
279
  # cloneType: https
280
- webhooks:
281
- # Obtains the SCM token for a given user. If a user does not have a valid SCM token registered with Screwdriver, it will use this user's token instead.
282
- username: sd-buildbot
283
- # Ignore commits made by these users
284
- ignoreCommitsBy: []
285
- # Restrict PR: all, none, branch, or fork
286
- restrictPR: none
287
- # Chain PR: true or false
288
- chainPR: false
289
- # Upper limit on incoming uploads to builds
290
- maxBytes: 1048576 # 1MB
291
-
280
+ webhooks:
281
+ github:
282
+ # Obtains the SCM token for a given user. If a user does not have a valid SCM token registered with Screwdriver, it will use this user's token instead.
283
+ username: sd-buildbot
284
+ # Ignore commits made by these users
285
+ ignoreCommitsBy: []
286
+ # Restrict PR: all, none, branch, or fork
287
+ restrictPR: none
288
+ # Chain PR: true or false
289
+ chainPR: false
290
+ # Upper limit on incoming uploads to builds
291
+ maxBytes: 1048576 #1MB
292
+ # Object keyed by scm name with value webhook settings.
293
+ # Value of webhook settings is an object with the following properties:
294
+ # Example:
295
+ # {
296
+ # "github:github.com": {
297
+ # # Obtains the SCM token for a given user. If a user does not have a valid SCM token registered with Screwdriver, it will use this user's token instead.
298
+ # "username": "sd-buildbot",
299
+ # # Ignore commits made by these users
300
+ # "ignoreCommitsBy": [],
301
+ # # Restrict PR: all, none, branch, or fork
302
+ # "restrictPR": "none",
303
+ # # Chain PR: true or false
304
+ # "chainPR": false,
305
+ # # Upper limit on incoming uploads to builds
306
+ # "maxBytes": 1048576 #1MB
307
+ # },
308
+ # "github.example.com": {
309
+ # "username": "someuser",
310
+ # "ignoreCommitsBy": ["someuser", "anotheruser"],
311
+ # "restrictPR": "branch",
312
+ # "chainPR": true,
313
+ # "maxBytes": 2097152 #2MB
314
+ # }
315
+ # }
292
316
  coverage:
293
317
  default: "false"
294
318
  plugin: sonar
package/lib/server.js CHANGED
@@ -80,8 +80,6 @@ function handlePreResponseLogs(request, h) {
80
80
  * @param {String} config.httpd.uri Public routable address
81
81
  * @param {Object} config.httpd.tls TLS Configuration
82
82
  * @param {Object} config.webhooks Webhooks settings
83
- * @param {String} config.webhooks.restrictPR Restrict PR setting
84
- * @param {Boolean} config.webhooks.chainPR Chain PR flag
85
83
  * @param {Object} config.ecosystem List of hosts in the ecosystem
86
84
  * @param {Object} config.ecosystem.ui URL for the User Interface
87
85
  * @param {Factory} config.pipelineFactory Pipeline Factory instance
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screwdriver-api",
3
- "version": "8.0.47",
3
+ "version": "8.0.49",
4
4
  "description": "API server for the Screwdriver.cd service",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -374,7 +374,14 @@ async function updateBuildAndTriggerDownstreamJobs(config, build, server, userna
374
374
  const stageIsDone = isStageDone(stageJobBuilds);
375
375
 
376
376
  if (stageIsDone) {
377
- const teardownNode = newEvent.workflowGraph.nodes.find(n => n.id === stageTeardownJob.id);
377
+ // Determine the actual job name in the graph by stripping PR prefix (e.g., "PR-123:")
378
+ // if this is a PR build, since workflowGraph nodes do not include PR-specific prefixes.
379
+ const prMatch = stage.name.match(PR_STAGE_NAME);
380
+ const teardownOriginalJobName = prMatch
381
+ ? stageTeardownName.replace(`${prMatch[1]}:`, '')
382
+ : stageTeardownName;
383
+
384
+ const teardownNode = newEvent.workflowGraph.nodes.find(n => n.name === teardownOriginalJobName);
378
385
 
379
386
  // Update teardown build
380
387
  stageTeardownBuild = await createOrUpdateStageTeardownBuild(
@@ -7,6 +7,19 @@ const { ValidationError } = require('joi');
7
7
  const { startHookEvent } = require('./helper');
8
8
 
9
9
  const DEFAULT_MAX_BYTES = 1048576;
10
+ const MAX_BYTES_UPPER_BOUND = 5242880; // 5MB
11
+ const providerSchema = joi
12
+ .object({
13
+ username: joi.string().required(),
14
+ ignoreCommitsBy: joi.array().items(joi.string()).optional(),
15
+ restrictPR: joi
16
+ .string()
17
+ .valid('all', 'none', 'branch', 'fork', 'all-admin', 'none-admin', 'branch-admin', 'fork-admin')
18
+ .optional(),
19
+ chainPR: joi.boolean().optional(),
20
+ maxBytes: joi.number().integer().optional()
21
+ })
22
+ .unknown(false);
10
23
 
11
24
  /**
12
25
  * Webhook API Plugin
@@ -29,16 +42,7 @@ const webhooksPlugin = {
29
42
  async register(server, options) {
30
43
  const pluginOptions = joi.attempt(
31
44
  options,
32
- joi.object().keys({
33
- username: joi.string().required(),
34
- ignoreCommitsBy: joi.array().items(joi.string()).optional(),
35
- restrictPR: joi
36
- .string()
37
- .valid('all', 'none', 'branch', 'fork', 'all-admin', 'none-admin', 'branch-admin', 'fork-admin')
38
- .optional(),
39
- chainPR: joi.boolean().optional(),
40
- maxBytes: joi.number().integer().optional()
41
- }),
45
+ joi.object().pattern(joi.string(), providerSchema).min(1).required(),
42
46
  'Invalid config for plugin-webhooks'
43
47
  );
44
48
 
@@ -55,7 +59,9 @@ const webhooksPlugin = {
55
59
  }
56
60
  },
57
61
  payload: {
58
- maxBytes: parseInt(pluginOptions.maxBytes, 10) || DEFAULT_MAX_BYTES
62
+ maxBytes: MAX_BYTES_UPPER_BOUND,
63
+ parse: false,
64
+ output: 'stream'
59
65
  },
60
66
  handler: async (request, h) => {
61
67
  const { pipelineFactory, queueWebhook } = request.server.app;
@@ -63,8 +69,33 @@ const webhooksPlugin = {
63
69
  const { executor, queueWebhookEnabled } = queueWebhook;
64
70
  const message = 'Unable to process this kind of event';
65
71
  let hookId;
72
+ let webhookSettings;
66
73
 
67
74
  try {
75
+ const scmContexts = await scm.getScmContexts();
76
+
77
+ scmContexts.forEach(scmContext => {
78
+ if (pluginOptions[scmContext]) {
79
+ webhookSettings = pluginOptions[scmContext];
80
+ }
81
+ });
82
+
83
+ if (!webhookSettings) {
84
+ logger.error(`No webhook settings found for scm context: ${scmContexts.join(', ')}`);
85
+ throw boom.internal();
86
+ }
87
+
88
+ let size = 0;
89
+
90
+ for await (const chunk of request.payload) {
91
+ size += chunk.length;
92
+ if (size > (webhookSettings.maxBytes || DEFAULT_MAX_BYTES)) {
93
+ throw boom.entityTooLarge(
94
+ `Payload size exceeds the maximum limit of ${webhookSettings.maxBytes || DEFAULT_MAX_BYTES} bytes`
95
+ );
96
+ }
97
+ }
98
+
68
99
  const parsed = await scm.parseHook(request.headers, request.payload);
69
100
 
70
101
  if (!parsed) {
@@ -72,7 +103,7 @@ const webhooksPlugin = {
72
103
  return h.response({ message }).code(204);
73
104
  }
74
105
 
75
- parsed.pluginOptions = pluginOptions;
106
+ parsed.pluginOptions = webhookSettings;
76
107
 
77
108
  const { type } = parsed;
78
109