screwdriver-api 8.0.9 → 8.0.11

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": "8.0.9",
3
+ "version": "8.0.11",
4
4
  "description": "API server for the Screwdriver.cd service",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -111,7 +111,7 @@
111
111
  "screwdriver-config-parser": "^12.0.0",
112
112
  "screwdriver-coverage-bookend": "^3.0.0",
113
113
  "screwdriver-coverage-sonar": "^5.0.0",
114
- "screwdriver-data-schema": "^25.1.0",
114
+ "screwdriver-data-schema": "^25.1.3",
115
115
  "screwdriver-datastore-sequelize": "^10.0.0",
116
116
  "screwdriver-executor-base": "^11.0.0",
117
117
  "screwdriver-executor-docker": "^8.0.1",
@@ -135,18 +135,26 @@ async function getStage({ stageFactory, workflowGraph, jobName, pipelineId }) {
135
135
  }
136
136
 
137
137
  /**
138
- * Checks if all builds in stage are done running
138
+ * Get all builds in stage
139
139
  *
140
140
  * @param {Stage} stage Stage
141
141
  * @param {Event} event Event
142
- * @return {Boolean} Flag if stage is done
142
+ * @return {Promise<Build[]>} Builds in stage
143
143
  */
144
- async function isStageDone({ stage, event }) {
144
+ async function getStageJobBuilds({ stage, event }) {
145
145
  // Get all jobIds for jobs in the stage
146
146
  const stageJobIds = [...stage.jobIds, stage.setup];
147
147
 
148
148
  // Get all builds in a stage for this event
149
- const stageJobBuilds = await event.getBuilds({ params: { jobId: stageJobIds } });
149
+ return event.getBuilds({ params: { jobId: stageJobIds } });
150
+ }
151
+
152
+ /**
153
+ * Checks if all builds in stage are done running
154
+ * @param {Build[]} stageJobBuilds Builds in stage
155
+ * @returns {Boolean} Flag if stage is done
156
+ */
157
+ function isStageDone(stageJobBuilds) {
150
158
  let stageIsDone = false;
151
159
 
152
160
  if (stageJobBuilds && stageJobBuilds.length !== 0) {
@@ -292,10 +300,13 @@ async function updateBuildAndTriggerDownstreamJobs(config, build, server, userna
292
300
 
293
301
  // Start stage teardown build if stage is done
294
302
  if (stageTeardownBuild && stageTeardownBuild.status === 'CREATED') {
295
- const stageIsDone = await isStageDone({ stage, event: newEvent });
303
+ const stageJobBuilds = await getStageJobBuilds({ stage, event: newEvent });
304
+ const stageIsDone = isStageDone(stageJobBuilds);
296
305
 
297
306
  if (stageIsDone) {
298
307
  stageTeardownBuild.status = 'QUEUED';
308
+ stageTeardownBuild.parentBuildId = stageJobBuilds.map(b => b.id);
309
+
299
310
  await stageTeardownBuild.update();
300
311
  await stageTeardownBuild.start();
301
312
  }
@@ -39,8 +39,7 @@ const {
39
39
  getParallelBuilds,
40
40
  isStartFromMiddleOfCurrentStage,
41
41
  Status,
42
- getSameParentEvents,
43
- isVirtualJob
42
+ getSameParentEvents
44
43
  } = require('./triggers/helpers');
45
44
  const { getFullStageJobName } = require('../helper');
46
45
 
@@ -96,9 +95,11 @@ async function triggerNextJobs(config, app) {
96
95
 
97
96
  const downstreamOfNextJobsToBeProcessed = [];
98
97
 
99
- for (const [nextJobName, nextJobInfo] of Object.entries(currentPipelineNextJobs)) {
98
+ for (const [nextJobName] of Object.entries(currentPipelineNextJobs)) {
100
99
  const nextJob = await getJob(nextJobName, currentPipeline.id, jobFactory);
101
- const { stageName: nextJobStageName } = nextJobInfo;
100
+ const node = currentEvent.workflowGraph.nodes.find(n => n.name === trimJobName(nextJobName));
101
+ const isNextJobVirtual = node.virtual || false;
102
+ const nextJobStageName = node.stageName || null;
102
103
  const resource = `pipeline:${currentPipeline.id}:groupEvent:${currentEvent.groupEventId}`;
103
104
  let lock;
104
105
  let nextBuild;
@@ -124,12 +125,24 @@ async function triggerNextJobs(config, app) {
124
125
  isOrTrigger(currentEvent.workflowGraph, originalCurrentJobName, trimJobName(nextJobName)) ||
125
126
  isStartFromMiddleOfCurrentStage(currentJob.name, currentEvent.startFrom, currentEvent.workflowGraph)
126
127
  ) {
127
- nextBuild = await orTrigger.execute(currentEvent, currentPipeline.id, nextJob, parentBuilds);
128
+ nextBuild = await orTrigger.execute(
129
+ currentEvent,
130
+ currentPipeline.id,
131
+ nextJob,
132
+ parentBuilds,
133
+ isNextJobVirtual
134
+ );
128
135
  } else {
129
- nextBuild = await andTrigger.execute(nextJob, parentBuilds, joinListNames, nextJobStageName);
136
+ nextBuild = await andTrigger.execute(
137
+ nextJob,
138
+ parentBuilds,
139
+ joinListNames,
140
+ isNextJobVirtual,
141
+ nextJobStageName
142
+ );
130
143
  }
131
144
 
132
- if (isVirtualJob(nextJob) && nextBuild && nextBuild.status === Status.SUCCESS) {
145
+ if (isNextJobVirtual && nextBuild && nextBuild.status === Status.SUCCESS) {
133
146
  downstreamOfNextJobsToBeProcessed.push({
134
147
  build: nextBuild,
135
148
  event: currentEvent,
@@ -274,7 +287,9 @@ async function triggerNextJobs(config, app) {
274
287
  if (externalEvent) {
275
288
  for (const [nextJobName, nextJobInfo] of Object.entries(joinedPipeline.jobs)) {
276
289
  const nextJob = await getJob(nextJobName, joinedPipelineId, jobFactory);
277
- const { stageName: nextJobStageName } = nextJobInfo;
290
+ const node = externalEvent.workflowGraph.nodes.find(n => n.name === trimJobName(nextJobName));
291
+ const isNextJobVirtual = node.virtual || false;
292
+ const nextJobStageName = node.stageName || null;
278
293
 
279
294
  const { parentBuilds } = parseJobInfo({
280
295
  joinObj: joinedPipeline.jobs,
@@ -295,7 +310,8 @@ async function triggerNextJobs(config, app) {
295
310
  externalEvent,
296
311
  externalEvent.pipelineId,
297
312
  nextJob,
298
- parentBuilds
313
+ parentBuilds,
314
+ isNextJobVirtual
299
315
  );
300
316
  } else {
301
317
  // Re get join list when first time remote trigger since external event was empty and cannot get workflow graph then
@@ -311,11 +327,12 @@ async function triggerNextJobs(config, app) {
311
327
  parentBuilds,
312
328
  groupEventBuilds,
313
329
  joinListNames,
330
+ isNextJobVirtual,
314
331
  nextJobStageName
315
332
  );
316
333
  }
317
334
 
318
- if (isVirtualJob(nextJob) && nextBuild && nextBuild.status === Status.SUCCESS) {
335
+ if (isNextJobVirtual && nextBuild && nextBuild.status === Status.SUCCESS) {
319
336
  downstreamOfNextJobsToBeProcessed.push({
320
337
  build: nextBuild,
321
338
  event: currentEvent,
@@ -98,7 +98,6 @@ class AndTrigger extends JoinBase {
98
98
  nextBuild,
99
99
  nextJob,
100
100
  parentBuilds: newParentBuilds,
101
- parentBuildId: this.currentBuild.id,
102
101
  joinListNames,
103
102
  isNextJobVirtual,
104
103
  nextJobStageName
@@ -128,17 +128,6 @@ function isExternalTrigger(jobName) {
128
128
  return EXTERNAL_TRIGGER_ALL.test(jobName);
129
129
  }
130
130
 
131
- /**
132
- * Checks if job is virtual
133
- * @param {Job} job Job object
134
- * @returns {Boolean}
135
- */
136
- function isVirtualJob(job) {
137
- const { annotations } = job.permutations[0];
138
-
139
- return annotations ? annotations['screwdriver.cd/virtualJob'] === true : false;
140
- }
141
-
142
131
  /**
143
132
  * Checks if job has freezeWindows
144
133
  * @param {Job} job Job object
@@ -545,10 +534,9 @@ async function getBuildsForGroupEvent(groupEventId, buildFactory) {
545
534
  * @param {Object} arg
546
535
  * @param {ParentBuilds} arg.joinParentBuilds Parent builds object for join job
547
536
  * @param {Build} arg.nextBuild Next build
548
- * @param {Build} arg.build Build for current completed job
549
537
  * @returns {Promise<Build>} Updated next build
550
538
  */
551
- async function updateParentBuilds({ joinParentBuilds, nextBuild, build }) {
539
+ async function updateParentBuilds({ joinParentBuilds, nextBuild }) {
552
540
  // Override old parentBuilds info
553
541
  const newParentBuilds = merge({}, joinParentBuilds, nextBuild.parentBuilds, (objVal, srcVal) =>
554
542
  // passthrough objects, else mergeWith mutates source
@@ -556,59 +544,69 @@ async function updateParentBuilds({ joinParentBuilds, nextBuild, build }) {
556
544
  );
557
545
 
558
546
  nextBuild.parentBuilds = newParentBuilds;
559
- // nextBuild.parentBuildId may be int or Array, so it needs to be flattened
560
- nextBuild.parentBuildId = Array.from(new Set([build.id, nextBuild.parentBuildId || []].flat()));
561
547
 
562
548
  return nextBuild.update();
563
549
  }
564
550
 
565
551
  /**
566
- * Check if all parent builds of the new build are done
567
- * @param {Object} arg
568
- * @param {Build} arg.newBuild Updated build
552
+ * Get builds in join list from parent builds
553
+ * @param {newBuild} arg.newBuild Updated build
569
554
  * @param {String[]} arg.joinListNames Join list names
570
555
  * @param {Number} arg.pipelineId Pipeline ID
571
556
  * @param {BuildFactory} arg.buildFactory Build factory
572
- * @returns {Promise<{hasFailure: Boolean, done: Boolean}>} Object with done and hasFailure statuses
557
+ * @returns {Promise<Map<String, Build>>} Join builds
573
558
  */
574
- async function getParentBuildStatus({ newBuild, joinListNames, pipelineId, buildFactory }) {
559
+ async function getJoinBuilds({ newBuild, joinListNames, pipelineId, buildFactory }) {
575
560
  const upstream = newBuild.parentBuilds || {};
561
+ const joinBuilds = {};
576
562
 
577
- // Get buildId
578
- const joinBuildIds = joinListNames.map(name => {
563
+ for (const jobName of joinListNames) {
579
564
  let upstreamPipelineId = pipelineId;
580
- let upstreamJobName = name;
565
+ let upstreamJobName = jobName;
581
566
 
582
- if (isExternalTrigger(name)) {
583
- const { externalPipelineId, externalJobName } = getExternalPipelineAndJob(name);
567
+ if (isExternalTrigger(upstreamJobName)) {
568
+ const { externalPipelineId, externalJobName } = getExternalPipelineAndJob(jobName);
584
569
 
585
570
  upstreamPipelineId = externalPipelineId;
586
571
  upstreamJobName = externalJobName;
587
572
  }
588
573
 
589
574
  if (upstream[upstreamPipelineId] && upstream[upstreamPipelineId].jobs[upstreamJobName]) {
590
- return upstream[upstreamPipelineId].jobs[upstreamJobName];
575
+ const buildId = upstream[upstreamPipelineId].jobs[upstreamJobName];
576
+
577
+ const build = await buildFactory.get(buildId);
578
+
579
+ if (typeof build.endTime === 'string') {
580
+ build.endTime = new Date(build.endTime);
581
+ }
582
+
583
+ joinBuilds[jobName] = build;
591
584
  }
585
+ }
592
586
 
593
- return undefined;
594
- });
587
+ return joinBuilds;
588
+ }
595
589
 
590
+ /**
591
+ * Check if all parent builds of the new build are done
592
+ * @param {Object} arg
593
+ * @param {String[]} arg.joinListNames Join list names
594
+ * @param {String[]} arg.joinBuilds Join builds
595
+ * @returns {Promise<{hasFailure: Boolean, done: Boolean}>} Object with done and hasFailure statuses
596
+ */
597
+ async function getParentBuildStatus({ joinListNames, joinBuilds }) {
596
598
  // If buildId is empty, the job hasn't executed yet and the join is not done
597
- const isExecuted = !joinBuildIds.includes(undefined);
599
+ const isExecuted = joinListNames.every(name => joinBuilds[name] !== undefined);
600
+ const parentBuilds = Object.values(joinBuilds);
598
601
 
599
- // Get the status of the builds
600
- const buildIds = joinBuildIds.filter(buildId => buildId !== undefined);
601
- const promisesToAwait = buildIds.map(buildId => buildFactory.get(buildId));
602
- const joinedBuilds = await Promise.all(promisesToAwait);
603
-
604
- const hasFailure = joinedBuilds
602
+ const hasFailure = parentBuilds
605
603
  .map(build => {
606
604
  // Do not need to run the next build; terminal status
607
605
  return [Status.FAILURE, Status.ABORTED, Status.COLLAPSED, Status.UNSTABLE].includes(build.status);
608
606
  })
609
607
  .includes(true);
610
608
 
611
- const isDoneStatus = joinedBuilds.every(build => {
609
+ const isDoneStatus = parentBuilds.every(build => {
612
610
  // All builds are done
613
611
  return [Status.FAILURE, Status.SUCCESS, Status.ABORTED, Status.UNSTABLE, Status.COLLAPSED].includes(
614
612
  build.status
@@ -627,29 +625,40 @@ async function getParentBuildStatus({ newBuild, joinListNames, pipelineId, build
627
625
  * if no failure, start new build
628
626
  * Otherwise, do nothing
629
627
  * @param {Object} arg If the build is done or not
630
- * @param {Boolean} arg.done If the build is done or not
631
- * @param {Boolean} arg.hasFailure If the build has a failure or not
628
+ * @param {String[]} arg.joinListNames Join list names
632
629
  * @param {Build} arg.newBuild Next build
633
630
  * @param {Job} arg.job Next job
634
631
  * @param {String|undefined} arg.pipelineId Pipeline ID
635
632
  * @param {String|undefined} arg.stageName Stage name
633
+ * @param {Boolean} arg.isVirtualJob If the job is virtual or not
636
634
  * @param {Event} arg.event Event
637
- * @param {Build} arg.currentBuild Current build
635
+ * @param {BuildFactory} arg.buildFactory Build factory
638
636
  * @returns {Promise<Build|null>} The newly updated/created build
639
637
  */
640
- async function handleNewBuild({ done, hasFailure, newBuild, job, pipelineId, stageName, event, currentBuild }) {
641
- if (!done || Status.isStarted(newBuild.status)) {
642
- // The virtual job does not inherit metadata because the Launcher is not executed.
643
- // Therefore, it is necessary to take over the metadata from the previous build.
644
-
645
- // TODO There is a bug when virtual job requires [a, b] and if "a" and "b" are completed simultaneously.
646
- // https://github.com/screwdriver-cd/screwdriver/issues/3287
647
- if (isVirtualJob(job) && !hasFreezeWindows(job)) {
648
- newBuild.meta = merge({}, newBuild.meta, currentBuild.meta);
638
+ async function handleNewBuild({
639
+ joinListNames,
640
+ newBuild,
641
+ job,
642
+ pipelineId,
643
+ stageName,
644
+ isVirtualJob,
645
+ event,
646
+ buildFactory
647
+ }) {
648
+ const joinBuilds = await getJoinBuilds({
649
+ newBuild,
650
+ joinListNames,
651
+ pipelineId,
652
+ buildFactory
653
+ });
649
654
 
650
- await newBuild.update();
651
- }
655
+ /* CHECK IF ALL PARENT BUILDS OF NEW BUILD ARE DONE */
656
+ const { hasFailure, done } = await getParentBuildStatus({
657
+ joinBuilds,
658
+ joinListNames
659
+ });
652
660
 
661
+ if (!done || Status.isStarted(newBuild.status)) {
653
662
  return null;
654
663
  }
655
664
 
@@ -668,14 +677,28 @@ async function handleNewBuild({ done, hasFailure, newBuild, job, pipelineId, sta
668
677
  return null;
669
678
  }
670
679
 
680
+ /* Prepare to execute the build */
681
+ const parentBuilds = Object.values(joinBuilds);
682
+
683
+ parentBuilds.sort((l, r) => {
684
+ if (l.endTime && r.endTime) {
685
+ return l.endTime.getTime() - r.endTime.getTime();
686
+ }
687
+
688
+ // Move to tail if endTime is not set
689
+ return (l.endTime ? 0 : 1) - (r.endTime ? 0 : 1);
690
+ });
691
+ newBuild.parentBuildId = parentBuilds.map(build => build.id);
692
+
671
693
  // Bypass execution of the build if the job is virtual
672
- if (isVirtualJob(job) && !hasFreezeWindows(job)) {
694
+ if (isVirtualJob && !hasFreezeWindows(job)) {
673
695
  newBuild.status = Status.SUCCESS;
674
696
  newBuild.statusMessage = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessage;
675
697
  newBuild.statusMessageType = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessageType;
698
+
676
699
  // The virtual job does not inherit metadata because the Launcher is not executed.
677
700
  // Therefore, it is necessary to take over the metadata from the previous build.
678
- newBuild.meta = merge({}, newBuild.meta, currentBuild.meta);
701
+ newBuild.meta = parentBuilds.reduce((acc, build) => merge(acc, build.meta), {});
679
702
 
680
703
  return newBuild.update();
681
704
  }
@@ -934,9 +957,7 @@ async function createJoinObject(nextJobNames, current, eventFactory) {
934
957
  isExternal = true;
935
958
  }
936
959
 
937
- const node = event.workflowGraph.nodes.find(n => n.name === trimJobName(jobName));
938
- const jId = node.id;
939
- const stageName = node.stageName || null;
960
+ const jId = event.workflowGraph.nodes.find(n => n.name === trimJobName(jobName)).id;
940
961
 
941
962
  if (!joinObj[nextJobPipelineId]) joinObj[nextJobPipelineId] = {};
942
963
  const pipelineObj = joinObj[nextJobPipelineId];
@@ -956,7 +977,7 @@ async function createJoinObject(nextJobNames, current, eventFactory) {
956
977
  }
957
978
 
958
979
  if (!pipelineObj.jobs) pipelineObj.jobs = {};
959
- pipelineObj.jobs[nextJobName] = { id: jId, join: jobs, isExternal, stageName };
980
+ pipelineObj.jobs[nextJobName] = { id: jId, join: jobs, isExternal };
960
981
  }
961
982
 
962
983
  return joinObj;
@@ -1017,38 +1038,6 @@ async function ensureStageTeardownBuildExists({
1017
1038
  }
1018
1039
  }
1019
1040
 
1020
- /**
1021
- * Get parentBuildId from parentBuilds object
1022
- * @param {Object} arg
1023
- * @param {ParentBuilds} arg.parentBuilds Builds that triggered this build
1024
- * @param {String[]} arg.joinListNames Array of join job name
1025
- * @param {Number} arg.pipelineId Pipeline ID
1026
- * @returns {String[]} Array of parentBuildId
1027
- */
1028
- function getParentBuildIds({ currentBuildId, parentBuilds, joinListNames, pipelineId }) {
1029
- const parentBuildIds = joinListNames
1030
- .map(name => {
1031
- let parentBuildPipelineId = pipelineId;
1032
- let parentBuildJobName = name;
1033
-
1034
- if (isExternalTrigger(name)) {
1035
- const { externalPipelineId, externalJobName } = getExternalPipelineAndJob(name);
1036
-
1037
- parentBuildPipelineId = externalPipelineId;
1038
- parentBuildJobName = externalJobName;
1039
- }
1040
-
1041
- if (parentBuilds[parentBuildPipelineId] && parentBuilds[parentBuildPipelineId].jobs[parentBuildJobName]) {
1042
- return parentBuilds[parentBuildPipelineId].jobs[parentBuildJobName];
1043
- }
1044
-
1045
- return null;
1046
- })
1047
- .filter(Boolean); // Remove undefined or null values
1048
-
1049
- return Array.from(new Set([currentBuildId, ...parentBuildIds]));
1050
- }
1051
-
1052
1041
  /**
1053
1042
  * Extract a current pipeline's next jobs from pipeline join data
1054
1043
  * (Next jobs triggered as external are not included)
@@ -1208,13 +1197,13 @@ module.exports = {
1208
1197
  getSameParentEvents,
1209
1198
  mergeParentBuilds,
1210
1199
  updateParentBuilds,
1200
+ getJoinBuilds,
1211
1201
  getParentBuildStatus,
1212
1202
  handleNewBuild,
1213
1203
  ensureStageTeardownBuildExists,
1214
1204
  getBuildsForGroupEvent,
1215
1205
  createJoinObject,
1216
1206
  createExternalEvent,
1217
- getParentBuildIds,
1218
1207
  strToInt,
1219
1208
  createEvent,
1220
1209
  deleteBuild,
@@ -1225,7 +1214,6 @@ module.exports = {
1225
1214
  buildsToRestartFilter,
1226
1215
  trimJobName,
1227
1216
  isStartFromMiddleOfCurrentStage,
1228
- isVirtualJob,
1229
1217
  hasFreezeWindows,
1230
1218
  BUILD_STATUS_MESSAGES
1231
1219
  };
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const logger = require('screwdriver-logger');
4
- const { createInternalBuild, updateParentBuilds, getParentBuildStatus, handleNewBuild } = require('./helpers');
4
+ const { createInternalBuild, updateParentBuilds, handleNewBuild } = require('./helpers');
5
5
 
6
6
  /**
7
7
  * @typedef {import('screwdriver-models').EventFactory} EventFactory
@@ -41,8 +41,8 @@ class JoinBase {
41
41
  * @param {Build} nextBuild
42
42
  * @param {Job} nextJob
43
43
  * @param {import('./helpers').ParentBuilds} parentBuilds
44
- * @param {String} parentBuildId
45
44
  * @param {String[]} joinListNames
45
+ * @param {Boolean} isNextJobVirtual
46
46
  * @param {String} nextJobStageName
47
47
  * @returns {Promise<Build[]|null>}
48
48
  */
@@ -52,8 +52,8 @@ class JoinBase {
52
52
  nextBuild,
53
53
  nextJob,
54
54
  parentBuilds,
55
- parentBuildId,
56
55
  joinListNames,
56
+ isNextJobVirtual,
57
57
  nextJobStageName
58
58
  }) {
59
59
  let newBuild;
@@ -71,7 +71,7 @@ class JoinBase {
71
71
  event, // this is the parentBuild for the next build
72
72
  baseBranch: event.baseBranch || null,
73
73
  parentBuilds,
74
- parentBuildId,
74
+ parentBuildId: this.currentBuild.id,
75
75
  start: false
76
76
  });
77
77
  } else {
@@ -88,23 +88,15 @@ class JoinBase {
88
88
  return null;
89
89
  }
90
90
 
91
- /* CHECK IF ALL PARENT BUILDS OF NEW BUILD ARE DONE */
92
- const { hasFailure, done } = await getParentBuildStatus({
93
- newBuild,
94
- joinListNames,
95
- pipelineId,
96
- buildFactory: this.buildFactory
97
- });
98
-
99
91
  return handleNewBuild({
100
- done,
101
- hasFailure,
92
+ joinListNames,
102
93
  newBuild,
103
94
  job: nextJob,
104
95
  pipelineId,
96
+ isVirtualJob: isNextJobVirtual,
105
97
  stageName: nextJobStageName,
106
98
  event,
107
- currentBuild: this.currentBuild
99
+ buildFactory: this.buildFactory
108
100
  });
109
101
  }
110
102
  }
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const merge = require('lodash.mergewith');
4
- const { createInternalBuild, Status, BUILD_STATUS_MESSAGES, isVirtualJob, hasFreezeWindows } = require('./helpers');
4
+ const { createInternalBuild, Status, BUILD_STATUS_MESSAGES, hasFreezeWindows } = require('./helpers');
5
5
 
6
6
  /**
7
7
  * @typedef {import('screwdriver-models').BuildFactory} BuildFactory
@@ -39,15 +39,15 @@ class OrBase {
39
39
  * @param {Number} pipelineId
40
40
  * @param {Job} nextJob
41
41
  * @param {import('./helpers').ParentBuilds} parentBuilds
42
+ * @param {Boolean} isNextJobVirtual
42
43
  * @return {Promise<Build|null>}
43
44
  */
44
- async trigger(event, pipelineId, nextJob, parentBuilds) {
45
+ async trigger(event, pipelineId, nextJob, parentBuilds, isNextJobVirtual) {
45
46
  let nextBuild = await this.buildFactory.get({
46
47
  eventId: event.id,
47
48
  jobId: nextJob.id
48
49
  });
49
50
 
50
- const isNextJobVirtual = isVirtualJob(nextJob);
51
51
  const hasWindows = hasFreezeWindows(nextJob);
52
52
  const causeMessage = nextJob.name === event.startFrom ? event.causeMessage : '';
53
53
 
@@ -56,12 +56,16 @@ class OrBase {
56
56
  return nextBuild;
57
57
  }
58
58
 
59
+ nextBuild.parentBuildId = [this.currentBuild.id];
60
+
59
61
  // Bypass execution of the build if the job is virtual
60
62
  if (isNextJobVirtual && !hasWindows) {
61
63
  nextBuild.status = Status.SUCCESS;
62
64
  nextBuild.statusMessage = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessage;
63
65
  nextBuild.statusMessageType = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessageType;
64
- nextBuild.meta = merge({}, nextBuild.meta, this.currentBuild.meta);
66
+
67
+ // Overwrite metadata by current build's
68
+ nextBuild.meta = merge({}, this.currentBuild.meta);
65
69
 
66
70
  return nextBuild.update();
67
71
  }
@@ -93,7 +97,9 @@ class OrBase {
93
97
  nextBuild.status = Status.SUCCESS;
94
98
  nextBuild.statusMessage = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessage;
95
99
  nextBuild.statusMessageType = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessageType;
96
- nextBuild.meta = merge({}, nextBuild.meta, this.currentBuild.meta);
100
+
101
+ // Overwrite metadata by current build's
102
+ nextBuild.meta = merge({}, this.currentBuild.meta);
97
103
 
98
104
  await nextBuild.update();
99
105
  }
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { mergeParentBuilds, getParentBuildIds } = require('./helpers');
3
+ const { mergeParentBuilds } = require('./helpers');
4
4
  const { JoinBase } = require('./joinBase');
5
5
 
6
6
  /**
@@ -45,20 +45,12 @@ class RemoteJoin extends JoinBase {
45
45
 
46
46
  const newParentBuilds = mergeParentBuilds(parentBuilds, groupEventBuilds, this.currentEvent, externalEvent);
47
47
 
48
- const parentBuildId = getParentBuildIds({
49
- currentBuildId: this.currentBuild.id,
50
- parentBuilds: newParentBuilds,
51
- joinListNames,
52
- pipelineId: externalEvent.pipelineId
53
- });
54
-
55
48
  return this.processNextBuild({
56
49
  pipelineId: externalEvent.pipelineId,
57
50
  event: externalEvent,
58
51
  nextBuild,
59
52
  nextJob,
60
53
  parentBuilds: newParentBuilds,
61
- parentBuildId,
62
54
  joinListNames,
63
55
  isNextJobVirtual,
64
56
  nextJobStageName
@@ -74,7 +74,7 @@ module.exports = () => ({
74
74
 
75
75
  permutation.annotations = permutation.annotations || {};
76
76
 
77
- const annotations = permutation.annotations;
77
+ const { annotations } = permutation;
78
78
 
79
79
  if (annotations[adminAnnotation]) {
80
80
  logger.info(