screwdriver-api 7.0.140 → 7.0.142

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.140",
3
+ "version": "7.0.142",
4
4
  "description": "API server for the Screwdriver.cd service",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -35,7 +35,8 @@ const {
35
35
  getBuildsForGroupEvent,
36
36
  buildsToRestartFilter,
37
37
  trimJobName,
38
- getParallelBuilds
38
+ getParallelBuilds,
39
+ Status
39
40
  } = require('./triggers/helpers');
40
41
 
41
42
  /**
@@ -87,10 +88,14 @@ async function triggerNextJobs(config, app) {
87
88
  const andTrigger = new AndTrigger(app, config, currentEvent);
88
89
  const currentPipelineNextJobs = extractCurrentPipelineJoinData(pipelineJoinData, currentPipeline.id);
89
90
 
91
+ const downstreamOfNextJobsToBeProcessed = [];
92
+
90
93
  for (const [nextJobName, nextJob] of Object.entries(currentPipelineNextJobs)) {
91
94
  const nextJobId = nextJob.id || (await getJobId(nextJobName, currentPipeline.id, jobFactory));
95
+ const { isVirtual: isNextJobVirtual, stageName: nextJobStageName } = nextJob;
92
96
  const resource = `pipeline:${currentPipeline.id}:event:${currentEvent.id}`;
93
97
  let lock;
98
+ let nextBuild;
94
99
 
95
100
  try {
96
101
  lock = await locker.lock(resource);
@@ -110,9 +115,34 @@ async function triggerNextJobs(config, app) {
110
115
  * joinList doesn't include D, so start A
111
116
  */
112
117
  if (isOrTrigger(currentEvent.workflowGraph, originalCurrentJobName, trimJobName(nextJobName))) {
113
- await orTrigger.execute(currentEvent, currentPipeline.id, nextJobName, nextJobId, parentBuilds);
118
+ nextBuild = await orTrigger.execute(
119
+ currentEvent,
120
+ currentPipeline.id,
121
+ nextJobName,
122
+ nextJobId,
123
+ parentBuilds,
124
+ isNextJobVirtual
125
+ );
114
126
  } else {
115
- await andTrigger.execute(nextJobName, nextJobId, parentBuilds, joinListNames);
127
+ nextBuild = await andTrigger.execute(
128
+ nextJobName,
129
+ nextJobId,
130
+ parentBuilds,
131
+ joinListNames,
132
+ isNextJobVirtual,
133
+ nextJobStageName
134
+ );
135
+ }
136
+
137
+ if (isNextJobVirtual && nextBuild.status === Status.SUCCESS) {
138
+ downstreamOfNextJobsToBeProcessed.push({
139
+ build: nextBuild,
140
+ event: currentEvent,
141
+ job: await nextBuild.job,
142
+ pipeline: currentPipeline,
143
+ scmContext: config.scmContext,
144
+ username: config.username
145
+ });
116
146
  }
117
147
  } catch (err) {
118
148
  logger.error(
@@ -199,6 +229,7 @@ async function triggerNextJobs(config, app) {
199
229
 
200
230
  for (const [nextJobName, nextJob] of Object.entries(joinedPipeline.jobs)) {
201
231
  const nextJobId = nextJob.id || (await getJobId(nextJobName, currentPipeline.id, jobFactory));
232
+ const { isVirtual: isNextJobVirtual, stageName: nextJobStageName } = nextJob;
202
233
 
203
234
  const { parentBuilds } = parseJobInfo({
204
235
  joinObj: joinedPipeline.jobs,
@@ -209,16 +240,19 @@ async function triggerNextJobs(config, app) {
209
240
  nextPipelineId: joinedPipelineId
210
241
  });
211
242
 
243
+ let nextBuild;
244
+
212
245
  try {
213
246
  if (resource) lock = await locker.lock(resource);
214
247
 
215
248
  if (isOrTrigger(externalEvent.workflowGraph, remoteTriggerName, nextJobName)) {
216
- await remoteTrigger.execute(
249
+ nextBuild = await remoteTrigger.execute(
217
250
  externalEvent,
218
251
  externalEvent.pipelineId,
219
252
  nextJobName,
220
253
  nextJobId,
221
- parentBuilds
254
+ parentBuilds,
255
+ isNextJobVirtual
222
256
  );
223
257
  } else {
224
258
  // Re get join list when first time remote trigger since external event was empty and cannot get workflow graph then
@@ -228,15 +262,30 @@ async function triggerNextJobs(config, app) {
228
262
  : workflowParser.getSrcForJoin(externalEvent.workflowGraph, { jobName: nextJobName });
229
263
  const joinListNames = joinList.map(j => j.name);
230
264
 
231
- await remoteJoin.execute(
265
+ nextBuild = await remoteJoin.execute(
232
266
  externalEvent,
233
267
  nextJobName,
234
268
  nextJobId,
235
269
  parentBuilds,
236
270
  groupEventBuilds,
237
- joinListNames
271
+ joinListNames,
272
+ isNextJobVirtual,
273
+ nextJobStageName
238
274
  );
239
275
  }
276
+
277
+ if (isNextJobVirtual && nextBuild.status === Status.SUCCESS) {
278
+ const nextJobModel = await nextBuild.job;
279
+
280
+ downstreamOfNextJobsToBeProcessed.push({
281
+ build: nextBuild,
282
+ event: currentEvent,
283
+ job: nextJobModel,
284
+ pipeline: await nextJobModel.pipeline,
285
+ scmContext: config.scmContext,
286
+ username: config.username
287
+ });
288
+ }
240
289
  } catch (err) {
241
290
  logger.error(
242
291
  `Error in triggerJobsInExternalPipeline:${joinedPipelineId} from pipeline:${currentPipeline.id}-${currentJob.name}-event:${currentEvent.id} `,
@@ -248,6 +297,10 @@ async function triggerNextJobs(config, app) {
248
297
  }
249
298
  }
250
299
 
300
+ for (const nextConfig of downstreamOfNextJobsToBeProcessed) {
301
+ await triggerNextJobs(nextConfig, app);
302
+ }
303
+
251
304
  return null;
252
305
  }
253
306
 
@@ -56,9 +56,11 @@ class AndTrigger extends JoinBase {
56
56
  * @param {String} nextJobId
57
57
  * @param {Record<String, Object>} parentBuilds
58
58
  * @param {String[]} joinListNames
59
+ * @param {Boolean} isNextJobVirtual
60
+ * @param {String} nextJobStageName
59
61
  * @returns {Promise<Build>}
60
62
  */
61
- async execute(nextJobName, nextJobId, parentBuilds, joinListNames) {
63
+ async execute(nextJobName, nextJobId, parentBuilds, joinListNames, isNextJobVirtual, nextJobStageName) {
62
64
  logger.info(`Fetching finished builds for event ${this.currentEvent.id}`);
63
65
 
64
66
  const relatedBuilds = await this.fetchRelatedBuilds();
@@ -90,7 +92,9 @@ class AndTrigger extends JoinBase {
90
92
  nextJobId,
91
93
  parentBuilds: newParentBuilds,
92
94
  parentBuildId: this.currentBuild.id,
93
- joinListNames
95
+ joinListNames,
96
+ isNextJobVirtual,
97
+ nextJobStageName
94
98
  });
95
99
  }
96
100
  }
@@ -601,17 +601,18 @@ async function getParentBuildStatus({ newBuild, joinListNames, pipelineId, build
601
601
  * @param {Build} arg.newBuild Next build
602
602
  * @param {String|undefined} arg.jobName Job name
603
603
  * @param {String|undefined} arg.pipelineId Pipeline ID
604
- * @param {Object|undefined} arg.stage Stage
604
+ * @param {String|undefined} arg.stageName Stage name
605
+ * @param {Boolean} arg.isVirtualJob If the job is virtual or not
605
606
  * @returns {Promise<Build|null>} The newly updated/created build
606
607
  */
607
- async function handleNewBuild({ done, hasFailure, newBuild, jobName, pipelineId, stage }) {
608
+ async function handleNewBuild({ done, hasFailure, newBuild, jobName, pipelineId, stageName, isVirtualJob }) {
608
609
  if (!done || Status.isStarted(newBuild.status)) {
609
610
  return null;
610
611
  }
611
612
 
612
613
  // Delete new build since previous build failed
613
614
  if (hasFailure) {
614
- const stageTeardownName = stage ? getFullStageJobName({ stageName: stage.name, jobName: 'teardown' }) : '';
615
+ const stageTeardownName = stageName ? getFullStageJobName({ stageName, jobName: 'teardown' }) : '';
615
616
 
616
617
  // New build is not stage teardown job
617
618
  if (jobName !== stageTeardownName) {
@@ -624,7 +625,14 @@ async function handleNewBuild({ done, hasFailure, newBuild, jobName, pipelineId,
624
625
  return null;
625
626
  }
626
627
 
627
- // All join builds finished successfully and it's clear that a new build has not been started before.
628
+ // Bypass execution of the build if the job is virtual
629
+ if (isVirtualJob) {
630
+ newBuild.status = Status.SUCCESS;
631
+
632
+ return newBuild.update();
633
+ }
634
+
635
+ // All join builds finished successfully, and it's clear that a new build has not been started before.
628
636
  // Start new build.
629
637
  newBuild.status = Status.QUEUED;
630
638
  await newBuild.update();
@@ -790,7 +798,10 @@ async function createJoinObject(nextJobNames, current, eventFactory) {
790
798
  isExternal = true;
791
799
  }
792
800
 
793
- const jId = event.workflowGraph.nodes.find(n => n.name === trimJobName(jobName)).id;
801
+ const node = event.workflowGraph.nodes.find(n => n.name === trimJobName(jobName));
802
+ const jId = node.id;
803
+ const isVirtual = node.virtual || false;
804
+ const stageName = node.stageName || null;
794
805
 
795
806
  if (!joinObj[nextJobPipelineId]) joinObj[nextJobPipelineId] = {};
796
807
  const pipelineObj = joinObj[nextJobPipelineId];
@@ -810,7 +821,7 @@ async function createJoinObject(nextJobNames, current, eventFactory) {
810
821
  }
811
822
 
812
823
  if (!pipelineObj.jobs) pipelineObj.jobs = {};
813
- pipelineObj.jobs[nextJobName] = { id: jId, join: jobs, isExternal };
824
+ pipelineObj.jobs[nextJobName] = { id: jId, join: jobs, isExternal, isVirtual, stageName };
814
825
  }
815
826
 
816
827
  return joinObj;
@@ -9,7 +9,6 @@ const { createInternalBuild, updateParentBuilds, getParentBuildStatus, handleNew
9
9
  * @typedef {import('screwdriver-models').JobFactory} JobFactory
10
10
  * @typedef {import('screwdriver-models/lib/event')} Event
11
11
  * @typedef {import('screwdriver-models/lib/build')} Build
12
- * @typedef {import('screwdriver-models/lib/stage')} Stage
13
12
  */
14
13
 
15
14
  class JoinBase {
@@ -21,7 +20,6 @@ class JoinBase {
21
20
  * @param {JobFactory} app.jobFactory Server app object
22
21
  * @param {Object} config Configuration object
23
22
  * @param {Build} config.build
24
- * @param {Stage} config.stage
25
23
  * @param {String} config.username
26
24
  * @param {String} config.scmContext
27
25
  */
@@ -31,7 +29,6 @@ class JoinBase {
31
29
  this.jobFactory = app.jobFactory;
32
30
 
33
31
  this.currentBuild = config.build;
34
- this.stage = config.stage;
35
32
  this.username = config.username;
36
33
  this.scmContext = config.scmContext;
37
34
  }
@@ -47,6 +44,8 @@ class JoinBase {
47
44
  * @param {import('./helpers').ParentBuilds} parentBuilds
48
45
  * @param {String} parentBuildId
49
46
  * @param {String[]} joinListNames
47
+ * @param {Boolean} isNextJobVirtual
48
+ * @param {String} nextJobStageName
50
49
  * @returns {Promise<Build[]|null>}
51
50
  */
52
51
  async processNextBuild({
@@ -57,7 +56,9 @@ class JoinBase {
57
56
  nextJobId,
58
57
  parentBuilds,
59
58
  parentBuildId,
60
- joinListNames
59
+ joinListNames,
60
+ isNextJobVirtual,
61
+ nextJobStageName
61
62
  }) {
62
63
  let newBuild;
63
64
 
@@ -103,9 +104,10 @@ class JoinBase {
103
104
  done,
104
105
  hasFailure,
105
106
  newBuild,
106
- nextJobName,
107
+ jobName: nextJobName,
107
108
  pipelineId,
108
- stage: this.stage
109
+ isVirtualJob: isNextJobVirtual,
110
+ stageName: nextJobStageName
109
111
  });
110
112
  }
111
113
  }
@@ -15,10 +15,11 @@ class OrTrigger extends OrBase {
15
15
  * @param {String} nextJobName
16
16
  * @param {Number} nextJobId
17
17
  * @param {import('./helpers').ParentBuilds} parentBuilds
18
+ * @param {Boolean} isNextJobVirtual
18
19
  * @return {Promise<Build|null>}
19
20
  */
20
- async execute(event, pipelineId, nextJobName, nextJobId, parentBuilds) {
21
- return this.trigger(event, pipelineId, nextJobName, nextJobId, parentBuilds);
21
+ async execute(event, pipelineId, nextJobName, nextJobId, parentBuilds, isNextJobVirtual) {
22
+ return this.trigger(event, pipelineId, nextJobName, nextJobId, parentBuilds, isNextJobVirtual);
22
23
  }
23
24
  }
24
25
 
@@ -39,10 +39,11 @@ class OrBase {
39
39
  * @param {String} nextJobName
40
40
  * @param {Number} nextJobId
41
41
  * @param {import('./helpers').ParentBuilds} parentBuilds
42
+ * @param {Boolean} isNextJobVirtual
42
43
  * @return {Promise<Build|null>}
43
44
  */
44
- async trigger(event, pipelineId, nextJobName, nextJobId, parentBuilds) {
45
- const nextBuild = await this.buildFactory.get({
45
+ async trigger(event, pipelineId, nextJobName, nextJobId, parentBuilds, isNextJobVirtual) {
46
+ let nextBuild = await this.buildFactory.get({
46
47
  eventId: event.id,
47
48
  jobId: nextJobId
48
49
  });
@@ -52,13 +53,20 @@ class OrBase {
52
53
  return nextBuild;
53
54
  }
54
55
 
56
+ // Bypass execution of the build if the job is virtual
57
+ if (isNextJobVirtual) {
58
+ nextBuild.status = Status.SUCCESS;
59
+
60
+ return nextBuild.update();
61
+ }
62
+
55
63
  nextBuild.status = Status.QUEUED;
56
64
  await nextBuild.update();
57
65
 
58
66
  return nextBuild.start();
59
67
  }
60
68
 
61
- return createInternalBuild({
69
+ nextBuild = await createInternalBuild({
62
70
  jobFactory: this.jobFactory,
63
71
  buildFactory: this.buildFactory,
64
72
  pipelineId,
@@ -70,8 +78,17 @@ class OrBase {
70
78
  baseBranch: event.baseBranch || null,
71
79
  parentBuilds,
72
80
  parentBuildId: this.currentBuild.id,
73
- start: true
81
+ start: !isNextJobVirtual
74
82
  });
83
+
84
+ // Bypass execution of the build if the job is virtual
85
+ if (isNextJobVirtual) {
86
+ nextBuild.status = Status.SUCCESS;
87
+
88
+ nextBuild = nextBuild.update();
89
+ }
90
+
91
+ return nextBuild;
75
92
  }
76
93
  }
77
94
 
@@ -28,9 +28,20 @@ class RemoteJoin extends JoinBase {
28
28
  * @param {import('./helpers').ParentBuilds} parentBuilds
29
29
  * @param {Build[]} groupEventBuilds Builds of the downstream pipeline, where only the latest ones for each job are included that have the same groupEventId as the externalEvent
30
30
  * @param {String[]} joinListNames
31
+ * @param {Boolean} isNextJobVirtual
32
+ * @param {String} nextJobStageName
31
33
  * @returns {Promise<Build|null>}
32
34
  */
33
- async execute(externalEvent, nextJobName, nextJobId, parentBuilds, groupEventBuilds, joinListNames) {
35
+ async execute(
36
+ externalEvent,
37
+ nextJobName,
38
+ nextJobId,
39
+ parentBuilds,
40
+ groupEventBuilds,
41
+ joinListNames,
42
+ isNextJobVirtual,
43
+ nextJobStageName
44
+ ) {
34
45
  // When restart case, should we create a new build ?
35
46
  const nextBuild = groupEventBuilds.find(b => b.jobId === nextJobId && b.eventId === externalEvent.id);
36
47
 
@@ -51,7 +62,9 @@ class RemoteJoin extends JoinBase {
51
62
  nextJobId,
52
63
  parentBuilds: newParentBuilds,
53
64
  parentBuildId,
54
- joinListNames
65
+ joinListNames,
66
+ isNextJobVirtual,
67
+ nextJobStageName
55
68
  });
56
69
  }
57
70
  }
@@ -15,10 +15,11 @@ class RemoteTrigger extends OrBase {
15
15
  * @param {String} nextJobName
16
16
  * @param {String} nextJobId
17
17
  * @param {import('./helpers').ParentBuilds} parentBuilds
18
+ * @param {Boolean} isNextJobVirtual
18
19
  * @returns {Promise<Build|null>}
19
20
  */
20
- async execute(event, pipelineId, nextJobName, nextJobId, parentBuilds) {
21
- return this.trigger(event, pipelineId, nextJobName, nextJobId, parentBuilds);
21
+ async execute(event, pipelineId, nextJobName, nextJobId, parentBuilds, isNextJobVirtual) {
22
+ return this.trigger(event, pipelineId, nextJobName, nextJobId, parentBuilds, isNextJobVirtual);
22
23
  }
23
24
  }
24
25
 
@@ -338,7 +338,7 @@ module.exports = () => ({
338
338
  );
339
339
  } else {
340
340
  await triggerNextJobs(
341
- { pipeline, job, build: newBuild, username, scmContext, event: newEvent, stage },
341
+ { pipeline, job, build: newBuild, username, scmContext, event: newEvent },
342
342
  request.server.app
343
343
  );
344
344
  }