screwdriver-api 8.0.83 → 8.0.85

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.83",
3
+ "version": "8.0.85",
4
4
  "description": "API server for the Screwdriver.cd service",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -5,7 +5,7 @@ const hoek = require('@hapi/hoek');
5
5
  const merge = require('lodash.mergewith');
6
6
  const { PR_JOB_NAME, PR_STAGE_NAME, STAGE_TEARDOWN_PATTERN } = require('screwdriver-data-schema').config.regex;
7
7
  const { getFullStageJobName } = require('../../helper');
8
- const { updateVirtualBuildSuccess } = require('../triggers/helpers');
8
+ const { updateVirtualBuildSuccess, emitBuildStatusEvent } = require('../triggers/helpers');
9
9
  const TERMINAL_STATUSES = ['FAILURE', 'ABORTED', 'UNSTABLE', 'COLLAPSED'];
10
10
  const FINISHED_STATUSES = ['SUCCESS', ...TERMINAL_STATUSES];
11
11
  const NON_TERMINATED_STATUSES = ['CREATED', 'RUNNING', 'QUEUED', 'BLOCKED', 'FROZEN'];
@@ -60,25 +60,6 @@ function stopBuilds(builds, statusMessage) {
60
60
  return { unchangedBuilds, changedBuilds };
61
61
  }
62
62
 
63
- /**
64
- * Identify whether this build resulted in a previously failed job to become successful.
65
- *
66
- * @method isFixedBuild
67
- * @param {Build} build Build Object
68
- * @param {JobFactory} jobFactory Job Factory instance
69
- */
70
- async function isFixedBuild(build, jobFactory) {
71
- if (build.status !== 'SUCCESS') {
72
- return false;
73
- }
74
-
75
- const job = await jobFactory.get(build.jobId);
76
- const failureBuild = await job.getLatestBuild({ status: 'FAILURE' });
77
- const successBuild = await job.getLatestBuild({ status: 'SUCCESS' });
78
-
79
- return !!((failureBuild && !successBuild) || failureBuild.id > successBuild.id);
80
- }
81
-
82
63
  /**
83
64
  * Stops a frozen build from executing
84
65
  *
@@ -405,7 +386,6 @@ async function updateBuildAndTriggerDownstreamJobs(config, build, server, userna
405
386
  throw boom.badRequest(`Cannot update builds to ${desiredStatus}`);
406
387
  }
407
388
 
408
- let isFixed = Promise.resolve(false);
409
389
  let stopFrozen = null;
410
390
 
411
391
  // If the event is Aborted, check if there are any builds within the same event
@@ -429,7 +409,6 @@ async function updateBuildAndTriggerDownstreamJobs(config, build, server, userna
429
409
  await updateInitStep(build, server.app);
430
410
  } else {
431
411
  stopFrozen = stopFrozenBuild(build, currentStatus);
432
- isFixed = isFixedBuild(build, jobFactory);
433
412
  }
434
413
 
435
414
  const [newBuild, newEvent] = await Promise.all([build.update(), event.update(), stopFrozen]);
@@ -437,16 +416,7 @@ async function updateBuildAndTriggerDownstreamJobs(config, build, server, userna
437
416
  const pipeline = await job.pipeline;
438
417
 
439
418
  if (desiredStatus) {
440
- await server.events.emit('build_status', {
441
- settings: job.permutations[0].settings,
442
- status: newBuild.status,
443
- event: newEvent.toJson(),
444
- pipeline: pipeline.toJson(),
445
- jobName: job.name,
446
- build: newBuild.toJson(),
447
- buildLink: `${buildFactory.uiUri}/pipelines/${pipeline.id}/builds/${build.id}`,
448
- isFixed: await isFixed
449
- });
419
+ await emitBuildStatusEvent({ server, build: newBuild, pipeline, event: newEvent, job });
450
420
  }
451
421
 
452
422
  const skipFurther = /\[(skip further)\]/.test(newEvent.causeMessage);
@@ -485,7 +455,7 @@ async function updateBuildAndTriggerDownstreamJobs(config, build, server, userna
485
455
  } else if (newBuild.status === 'SUCCESS' && isStageTeardown && stageBuildHasFailure) {
486
456
  await removeJoinBuilds({ pipeline, job, build: newBuild, event: newEvent, stage }, server.app);
487
457
  } else {
488
- await triggerNextJobs({ pipeline, job, build: newBuild, username, scmContext, event: newEvent }, server.app);
458
+ await triggerNextJobs({ pipeline, job, build: newBuild, username, scmContext, event: newEvent }, server);
489
459
  }
490
460
 
491
461
  // Determine if stage teardown build should start
@@ -520,7 +490,13 @@ async function updateBuildAndTriggerDownstreamJobs(config, build, server, userna
520
490
  stageTeardownBuild.parentBuildId = stageJobBuilds.map(b => b.id);
521
491
 
522
492
  if (teardownNode && teardownNode.virtual) {
523
- await updateVirtualBuildSuccess(stageTeardownBuild);
493
+ await updateVirtualBuildSuccess({
494
+ server,
495
+ build: stageTeardownBuild,
496
+ pipeline,
497
+ event: newEvent,
498
+ job: stageTeardownJob
499
+ });
524
500
  await updateStageBuildStatus({ stageBuild, newStatus: 'SUCCESS', job: stageTeardownJob });
525
501
  } else {
526
502
  stageTeardownBuild.status = 'QUEUED';
@@ -65,14 +65,15 @@ async function deleteBuild(buildConfig, buildFactory) {
65
65
  /**
66
66
  * Trigger the next jobs of the current job
67
67
  * @param { import('./types/index').ServerConfig } config Configuration object
68
- * @param { import('./types/index').ServerApp } app Server app object
68
+ * @param { Object } server Server object
69
+ * @param { import('./types/index').ServerApp } server.app Server app object
69
70
  * @return {Promise<null>} Resolves to the newly created build or null
70
71
  */
71
- async function triggerNextJobs(config, app) {
72
+ async function triggerNextJobs(config, server) {
72
73
  const currentPipeline = config.pipeline;
73
74
  const currentJob = config.job;
74
75
  const currentBuild = config.build;
75
- const { jobFactory, buildFactory, eventFactory, pipelineFactory, stageFactory, stageBuildFactory } = app;
76
+ const { jobFactory, buildFactory, eventFactory, pipelineFactory, stageFactory, stageBuildFactory } = server.app;
76
77
 
77
78
  /** @type {EventModel} */
78
79
  const currentEvent = await eventFactory.get({ id: currentBuild.eventId });
@@ -92,8 +93,8 @@ async function triggerNextJobs(config, app) {
92
93
 
93
94
  // Trigger OrTrigger and AndTrigger for current pipeline jobs.
94
95
  // Helper function to handle triggering jobs in same pipeline
95
- const orTrigger = new OrTrigger(app, config);
96
- const andTrigger = new AndTrigger(app, config, currentEvent);
96
+ const orTrigger = new OrTrigger(server, config);
97
+ const andTrigger = new AndTrigger(server, config, currentEvent);
97
98
  const currentPipelineNextJobs = extractCurrentPipelineJoinData(pipelineJoinData, currentPipeline.id);
98
99
 
99
100
  const downstreamOfNextJobsToBeProcessed = [];
@@ -155,7 +156,8 @@ async function triggerNextJobs(config, app) {
155
156
  eventId: currentEvent.id
156
157
  });
157
158
 
158
- if (stageBuild) {
159
+ // The next build is only created (not started) when nextBuild is null
160
+ if (stageBuild && nextBuild) {
159
161
  await updateStageBuildStatus({ stageBuild, newStatus: nextBuild.status, job: nextJob });
160
162
  }
161
163
 
@@ -182,8 +184,8 @@ async function triggerNextJobs(config, app) {
182
184
 
183
185
  // Trigger RemoteJoin and RemoteTrigger for current and external pipeline jobs.
184
186
  // Helper function to handle triggering jobs in external pipeline
185
- const remoteTrigger = new RemoteTrigger(app, config);
186
- const remoteJoin = new RemoteJoin(app, config, currentEvent);
187
+ const remoteTrigger = new RemoteTrigger(server, config);
188
+ const remoteJoin = new RemoteJoin(server, config, currentEvent);
187
189
  const externalPipelineJoinData = extractExternalJoinData(pipelineJoinData, currentPipeline.id);
188
190
 
189
191
  for (const [joinedPipelineId, joinedPipeline] of Object.entries(externalPipelineJoinData)) {
@@ -389,7 +391,7 @@ async function triggerNextJobs(config, app) {
389
391
  }
390
392
 
391
393
  for (const nextConfig of downstreamOfNextJobsToBeProcessed) {
392
- await triggerNextJobs(nextConfig, app);
394
+ await triggerNextJobs(nextConfig, server);
393
395
  }
394
396
 
395
397
  return null;
@@ -606,19 +606,67 @@ async function getParentBuildStatus({ joinListNames, joinBuilds }) {
606
606
  return { hasFailure, done };
607
607
  }
608
608
 
609
+ /**
610
+ * Emit 'build_status' event to notify
611
+ * @param {Object} arg
612
+ * @param {Object} arg.server Server object
613
+ * @param {Build} arg.build Build
614
+ * @param {Pipeline} [arg.pipeline] Pipeline
615
+ * @param {Event} [arg.event] Event
616
+ * @param {Job} [arg.job] Job
617
+ * @returns {Promise}
618
+ */
619
+ async function emitBuildStatusEvent({ server, build, pipeline, event, job }) {
620
+ const { buildFactory } = server.app;
621
+
622
+ event = event || (await build.event); // eslint-disable-line no-param-reassign
623
+ job = job || (await build.job); // eslint-disable-line no-param-reassign
624
+ pipeline = pipeline || (await job.pipeline); // eslint-disable-line no-param-reassign
625
+
626
+ let isFixed = false;
627
+
628
+ if (build.status === Status.SUCCESS) {
629
+ const failureBuild = await job.getLatestBuild({ status: Status.FAILURE });
630
+ const successBuild = await job.getLatestBuild({ status: Status.SUCCESS });
631
+
632
+ // Identify whether this build resulted in a previously failed job to become successful.
633
+ isFixed = !!((failureBuild && !successBuild) || failureBuild.id > successBuild.id);
634
+ }
635
+
636
+ return server.events.emit('build_status', {
637
+ settings: job.permutations[0].settings,
638
+ status: build.status,
639
+ event: event.toJson(),
640
+ pipeline: pipeline.toJson(),
641
+ jobName: job.name,
642
+ build: build.toJson(),
643
+ buildLink: `${buildFactory.uiUri}/pipelines/${pipeline.id}/builds/${build.id}`,
644
+ isFixed
645
+ });
646
+ }
647
+
609
648
  /**
610
649
  * Update virtual build status to SUCCESS and init metadata
611
- * @param {Build} build
650
+ * @param {Object} arg
651
+ * @param {Object} arg.server Server object
652
+ * @param {Build} arg.build Build
653
+ * @param {Pipeline} [arg.pipeline] Pipeline
654
+ * @param {Event} [arg.event] Event
655
+ * @param {Job} [arg.job] Job
612
656
  * @returns {Promise<Build>}
613
657
  */
614
- async function updateVirtualBuildSuccess(build) {
658
+ async function updateVirtualBuildSuccess({ server, build, pipeline, event, job }) {
615
659
  build.status = Status.SUCCESS;
616
660
  build.statusMessage = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessage;
617
661
  build.statusMessageType = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessageType;
618
662
 
619
663
  await build.initMeta();
620
664
 
621
- return build.update();
665
+ const newBuild = await build.update();
666
+
667
+ await emitBuildStatusEvent({ server, build: newBuild, pipeline, event, job });
668
+
669
+ return newBuild;
622
670
  }
623
671
 
624
672
  /**
@@ -628,6 +676,7 @@ async function updateVirtualBuildSuccess(build) {
628
676
  * if no failure, start new build
629
677
  * Otherwise, do nothing
630
678
  * @param {Object} arg If the build is done or not
679
+ * @param {Object} arg.server Server object
631
680
  * @param {String[]} arg.joinListNames Join list names
632
681
  * @param {Build} arg.newBuild Next build
633
682
  * @param {Job} arg.job Next job
@@ -639,6 +688,7 @@ async function updateVirtualBuildSuccess(build) {
639
688
  * @returns {Promise<Build|null>} The newly updated/created build
640
689
  */
641
690
  async function handleNewBuild({
691
+ server,
642
692
  joinListNames,
643
693
  newBuild,
644
694
  job,
@@ -687,7 +737,7 @@ async function handleNewBuild({
687
737
 
688
738
  // Bypass execution of the build if the job is virtual
689
739
  if (isVirtualJob && !hasFreezeWindows(job)) {
690
- return updateVirtualBuildSuccess(newBuild);
740
+ return updateVirtualBuildSuccess({ server, build: newBuild, event, job });
691
741
  }
692
742
 
693
743
  // All join builds finished successfully, and it's clear that a new build has not been started before.
@@ -1203,6 +1253,7 @@ module.exports = {
1203
1253
  getJoinBuilds,
1204
1254
  getParentBuildStatus,
1205
1255
  handleNewBuild,
1256
+ emitBuildStatusEvent,
1206
1257
  ensureStageTeardownBuildExists,
1207
1258
  getBuildsForGroupEvent,
1208
1259
  createJoinObject,
@@ -14,19 +14,20 @@ const { createInternalBuild, updateParentBuilds, handleNewBuild } = require('./h
14
14
  class JoinBase {
15
15
  /**
16
16
  * Base class for AND trigger and RemoteJoin
17
- * @param {Object} app Server app object
18
- * @param {EventFactory} app.eventFactory Server app object
19
- * @param {BuildFactory} app.buildFactory Server app object
20
- * @param {JobFactory} app.jobFactory Server app object
17
+ * @param {Object} server Server object
18
+ * @param {EventFactory} server.app.eventFactory Server app object
19
+ * @param {BuildFactory} server.app.buildFactory Server app object
20
+ * @param {JobFactory} server.app.jobFactory Server app object
21
21
  * @param {Object} config Configuration object
22
22
  * @param {Build} config.build
23
23
  * @param {String} config.username
24
24
  * @param {String} config.scmContext
25
25
  */
26
- constructor(app, config) {
27
- this.eventFactory = app.eventFactory;
28
- this.buildFactory = app.buildFactory;
29
- this.jobFactory = app.jobFactory;
26
+ constructor(server, config) {
27
+ this.server = server;
28
+ this.eventFactory = server.app.eventFactory;
29
+ this.buildFactory = server.app.buildFactory;
30
+ this.jobFactory = server.app.jobFactory;
30
31
 
31
32
  this.currentBuild = config.build;
32
33
  this.username = config.username;
@@ -89,6 +90,7 @@ class JoinBase {
89
90
  }
90
91
 
91
92
  return handleNewBuild({
93
+ server: this.server,
92
94
  joinListNames,
93
95
  newBuild,
94
96
  job: nextJob,
@@ -13,19 +13,20 @@ const { createInternalBuild, Status, updateVirtualBuildSuccess, hasFreezeWindows
13
13
  class OrBase {
14
14
  /**
15
15
  * Trigger the next jobs of the current job
16
- * @param {Object} app Server app object
17
- * @param {BuildFactory} app.buildFactory
18
- * @param {JobFactory} app.jobFactory
19
- * @param {PipelineFactory} app.pipelineFactory
16
+ * @param {Object} server Server object
17
+ * @param {BuildFactory} server.app.buildFactory
18
+ * @param {JobFactory} server.app.jobFactory
19
+ * @param {PipelineFactory} server.app.pipelineFactory
20
20
  * @param {Object} config Configuration object
21
21
  * @param {Build} config.currentBuild
22
22
  * @param {String} config.username
23
23
  * @param {String} config.scmContext
24
24
  */
25
- constructor(app, config) {
26
- this.buildFactory = app.buildFactory;
27
- this.jobFactory = app.jobFactory;
28
- this.pipelineFactory = app.pipelineFactory;
25
+ constructor(server, config) {
26
+ this.server = server;
27
+ this.buildFactory = server.app.buildFactory;
28
+ this.jobFactory = server.app.jobFactory;
29
+ this.pipelineFactory = server.app.pipelineFactory;
29
30
 
30
31
  this.currentBuild = config.build;
31
32
  this.username = config.username;
@@ -59,7 +60,7 @@ class OrBase {
59
60
 
60
61
  // Bypass execution of the build if the job is virtual
61
62
  if (isNextJobVirtual && !hasWindows) {
62
- return updateVirtualBuildSuccess(nextBuild);
63
+ return updateVirtualBuildSuccess({ server: this.server, build: nextBuild, event, job: nextJob });
63
64
  }
64
65
 
65
66
  nextBuild.status = Status.QUEUED;
@@ -86,7 +87,7 @@ class OrBase {
86
87
 
87
88
  // Bypass execution of the build if the job is virtual
88
89
  if (isNextJobVirtual && !hasWindows) {
89
- await updateVirtualBuildSuccess(nextBuild);
90
+ await updateVirtualBuildSuccess({ server: this.server, build: nextBuild, event, job: nextJob });
90
91
  }
91
92
 
92
93
  return nextBuild;