screwdriver-api 8.0.39 → 8.0.41

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.39",
3
+ "version": "8.0.41",
4
4
  "description": "API server for the Screwdriver.cd service",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -3,8 +3,8 @@
3
3
  const boom = require('@hapi/boom');
4
4
  const hoek = require('@hapi/hoek');
5
5
  const merge = require('lodash.mergewith');
6
+ const { PR_JOB_NAME, PR_STAGE_NAME, STAGE_TEARDOWN_PATTERN } = require('screwdriver-data-schema').config.regex;
6
7
  const { getFullStageJobName } = require('../../helper');
7
- const STAGE_TEARDOWN_PATTERN = /^stage@([\w-]+)(?::teardown)$/;
8
8
  const TERMINAL_STATUSES = ['FAILURE', 'ABORTED', 'UNSTABLE', 'COLLAPSED'];
9
9
  const FINISHED_STATUSES = ['FAILURE', 'SUCCESS', 'ABORTED', 'UNSTABLE', 'COLLAPSED'];
10
10
 
@@ -121,13 +121,18 @@ function updateBuildStatus(build, desiredStatus, statusMessage, statusMessageTyp
121
121
  * @return {Stage} Stage for node
122
122
  */
123
123
  async function getStage({ stageFactory, workflowGraph, jobName, pipelineId }) {
124
- const currentNode = workflowGraph.nodes.find(node => node.name === jobName);
124
+ const prJobName = jobName.match(PR_JOB_NAME);
125
+ const nodeName = prJobName ? prJobName[2] : jobName;
126
+
127
+ const currentNode = workflowGraph.nodes.find(node => node.name === nodeName);
125
128
  let stage = null;
126
129
 
127
130
  if (currentNode && currentNode.stageName) {
131
+ const stageName = prJobName ? `${prJobName[1]}:${currentNode.stageName}` : currentNode.stageName;
132
+
128
133
  stage = await stageFactory.get({
129
134
  pipelineId,
130
- name: currentNode.stageName
135
+ name: stageName
131
136
  });
132
137
  }
133
138
 
@@ -143,26 +148,31 @@ async function getStage({ stageFactory, workflowGraph, jobName, pipelineId }) {
143
148
  * @return {Promise<Build[]>} Builds in stage
144
149
  */
145
150
  async function getStageJobBuilds({ stage, event, jobFactory }) {
151
+ const prStageName = stage.name.match(PR_STAGE_NAME);
152
+ const stageName = prStageName ? prStageName[2] : stage.name;
153
+
146
154
  // Get all jobIds for jobs in the stage
147
155
  const stageNodes = event.workflowGraph.nodes.filter(n => {
148
156
  const jobName = n.name.split(':')[1];
149
157
 
150
- return n.stageName === stage.name && jobName !== 'teardown';
158
+ return n.stageName === stageName && jobName !== 'teardown';
151
159
  });
160
+
152
161
  const stageJobIds = await Promise.all(
153
162
  stageNodes.map(async n => {
154
163
  if (n.id) {
155
164
  return n.id;
156
165
  }
157
166
 
158
- const job = await jobFactory.get({ pipelineId: event.pipelineId, name: n.name });
167
+ const jobName = prStageName ? `${prStageName[1]}:${n.name}` : n.name;
168
+ const job = await jobFactory.get({ pipelineId: event.pipelineId, name: jobName });
159
169
 
160
- return job.id;
170
+ return job ? job.id : null;
161
171
  })
162
172
  );
163
173
 
164
174
  // Get all builds in a stage for this event
165
- return event.getBuilds({ params: { jobId: stageJobIds } });
175
+ return event.getBuilds({ params: { jobId: stageJobIds.filter(id => id !== null) } });
166
176
  }
167
177
 
168
178
  /**
@@ -333,6 +343,14 @@ async function updateBuildAndTriggerDownstreamJobs(config, build, server, userna
333
343
  }
334
344
 
335
345
  stageBuildHasFailure = TERMINAL_STATUSES.includes(stageBuild.status);
346
+
347
+ // Create a stage teardown build
348
+ if (!isStageTeardown) {
349
+ await createOrUpdateStageTeardownBuild(
350
+ { pipeline, job, build, username, scmContext, event, stage },
351
+ server.app
352
+ );
353
+ }
336
354
  }
337
355
 
338
356
  // Guard against triggering non-successful or unstable builds
@@ -341,13 +359,6 @@ async function updateBuildAndTriggerDownstreamJobs(config, build, server, userna
341
359
  // Check for failed jobs and remove any child jobs in created state
342
360
  if (newBuild.status === 'FAILURE') {
343
361
  await removeJoinBuilds({ pipeline, job, build: newBuild, event: newEvent, stage }, server.app);
344
-
345
- if (stage && !isStageTeardown) {
346
- await createOrUpdateStageTeardownBuild(
347
- { pipeline, job, build, username, scmContext, event, stage },
348
- server.app
349
- );
350
- }
351
362
  }
352
363
  // Do not continue downstream is current job is stage teardown and statusBuild has failure
353
364
  } else if (newBuild.status === 'SUCCESS' && isStageTeardown && stageBuildHasFailure) {
@@ -373,6 +384,7 @@ async function updateBuildAndTriggerDownstreamJobs(config, build, server, userna
373
384
  stageTeardownBuild.status = 'QUEUED';
374
385
  stageTeardownBuild.parentBuildId = stageJobBuilds.map(b => b.id);
375
386
 
387
+ // TODO: Handle if a teardown job is virtual
376
388
  await stageTeardownBuild.update();
377
389
  await stageTeardownBuild.start();
378
390
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  const logger = require('screwdriver-logger');
4
4
  const workflowParser = require('screwdriver-workflow-parser');
5
+ const { STAGE_TEARDOWN_PATTERN } = require('screwdriver-data-schema').config.regex;
5
6
  const hoek = require('@hapi/hoek');
6
7
  const getRoute = require('./get');
7
8
  const getBuildStatusesRoute = require('./getBuildStatuses');
@@ -39,7 +40,8 @@ const {
39
40
  getParallelBuilds,
40
41
  isStartFromMiddleOfCurrentStage,
41
42
  Status,
42
- getSameParentEvents
43
+ getSameParentEvents,
44
+ getNextJobStageName
43
45
  } = require('./triggers/helpers');
44
46
  const { getFullStageJobName } = require('../helper');
45
47
 
@@ -99,7 +101,7 @@ async function triggerNextJobs(config, app) {
99
101
  const nextJob = await getJob(nextJobName, currentPipeline.id, jobFactory);
100
102
  const node = currentEvent.workflowGraph.nodes.find(n => n.name === trimJobName(nextJobName));
101
103
  const isNextJobVirtual = node.virtual || false;
102
- const nextJobStageName = node.stageName || null;
104
+ const nextJobStageName = getNextJobStageName({ stageName: node.stageName, nextJobName });
103
105
  const resource = `pipeline:${currentPipeline.id}:groupEvent:${currentEvent.groupEventId}`;
104
106
  let lock;
105
107
  let nextBuild;
@@ -289,7 +291,7 @@ async function triggerNextJobs(config, app) {
289
291
  const nextJob = await getJob(nextJobName, joinedPipelineId, jobFactory);
290
292
  const node = externalEvent.workflowGraph.nodes.find(n => n.name === trimJobName(nextJobName));
291
293
  const isNextJobVirtual = node.virtual || false;
292
- const nextJobStageName = node.stageName || null;
294
+ const nextJobStageName = getNextJobStageName({ stageName: node.stageName, nextJobName });
293
295
 
294
296
  const { parentBuilds } = parseJobInfo({
295
297
  joinObj: joinedPipeline.jobs,
@@ -469,29 +471,33 @@ const buildsPlugin = {
469
471
 
470
472
  for (const nextJobName of Object.keys(pipelineJoinData[pid].jobs)) {
471
473
  try {
472
- const nextJob = pipelineJoinData[pid].jobs[nextJobName];
474
+ const isNextJobStageTeardown = STAGE_TEARDOWN_PATTERN.test(nextJobName);
473
475
 
474
- buildConfig.jobId = nextJob.id;
475
- if (!isExternal) {
476
- buildConfig.eventId = event.id;
477
- } else {
478
- buildConfig.eventId = hoek.reach(pipelineJoinData[pid], 'event.id');
479
- }
476
+ if (!isNextJobStageTeardown) {
477
+ const nextJob = pipelineJoinData[pid].jobs[nextJobName];
480
478
 
481
- if (buildConfig.eventId) {
482
- if (current.stage) {
483
- const stageTeardownName = getFullStageJobName({
484
- stageName: current.stage.name,
485
- jobName: 'teardown'
486
- });
479
+ buildConfig.jobId = nextJob.id;
480
+ if (!isExternal) {
481
+ buildConfig.eventId = event.id;
482
+ } else {
483
+ buildConfig.eventId = hoek.reach(pipelineJoinData[pid], 'event.id');
484
+ }
487
485
 
488
- // Do not remove stage teardown builds as they need to be executed on stage failure as well.
489
- if (nextJobName !== stageTeardownName) {
490
- deletePromises.push(deleteBuild(buildConfig, buildFactory));
486
+ if (buildConfig.eventId) {
487
+ if (current.stage) {
488
+ const stageTeardownName = getFullStageJobName({
489
+ stageName: current.stage.name,
490
+ jobName: 'teardown'
491
+ });
492
+
493
+ // Do not remove stage teardown builds as they need to be executed on stage failure as well.
494
+ if (nextJobName !== stageTeardownName) {
495
+ deletePromises.push(deleteBuild(buildConfig, buildFactory));
496
+ }
491
497
  }
492
- }
493
498
 
494
- deletePromises.push(deleteBuild(buildConfig, buildFactory));
499
+ deletePromises.push(deleteBuild(buildConfig, buildFactory));
500
+ }
495
501
  }
496
502
  } catch (err) {
497
503
  logger.error(
@@ -4,7 +4,7 @@ const logger = require('screwdriver-logger');
4
4
  const workflowParser = require('screwdriver-workflow-parser');
5
5
  const merge = require('lodash.mergewith');
6
6
  const schema = require('screwdriver-data-schema');
7
- const { EXTERNAL_TRIGGER_ALL, STAGE_SETUP_PATTERN } = schema.config.regex;
7
+ const { EXTERNAL_TRIGGER_ALL, STAGE_SETUP_PATTERN, PR_JOB_NAME } = schema.config.regex;
8
8
  const { getFullStageJobName } = require('../../helper');
9
9
  const BUILD_STATUS_MESSAGES = {
10
10
  SKIP_VIRTUAL_JOB: {
@@ -373,27 +373,15 @@ async function createInternalBuild(config) {
373
373
  return null;
374
374
  }
375
375
 
376
- /**
377
- * Return PR job or not
378
- * PR job name certainly has ":". e.g. "PR-1:jobName"
379
- * @param {String} jobName
380
- * @returns {Boolean}
381
- */
382
- function isPR(jobName) {
383
- return jobName.startsWith('PR-');
384
- }
385
-
386
376
  /**
387
377
  * Trim Job name to follow data-schema
388
378
  * @param {String} jobName
389
379
  * @returns {String} trimmed jobName
390
380
  */
391
381
  function trimJobName(jobName) {
392
- if (isPR(jobName)) {
393
- return jobName.split(':')[1];
394
- }
382
+ const matched = jobName.match(PR_JOB_NAME);
395
383
 
396
- return jobName;
384
+ return matched ? matched[2] : jobName;
397
385
  }
398
386
 
399
387
  /**
@@ -1175,6 +1163,22 @@ function getStageName(workflowGraph, jobName) {
1175
1163
  return jobNode ? jobNode.stageName : null;
1176
1164
  }
1177
1165
 
1166
+ /**
1167
+ * get the stage name of a next job (foo or PR-123:foo)
1168
+ * @param {String} nextJobName Next jobob name
1169
+ * @param {String} stageName Stage name (Not have PR-xxx)
1170
+ * @return {String} Stage name
1171
+ */
1172
+ function getNextJobStageName({ stageName, nextJobName }) {
1173
+ if (!stageName) {
1174
+ return null;
1175
+ }
1176
+
1177
+ const matched = nextJobName.match(PR_JOB_NAME);
1178
+
1179
+ return matched ? `${matched[1]}:${stageName}` : stageName;
1180
+ }
1181
+
1178
1182
  /**
1179
1183
  * Check if the current job is a stage setup and the next job is a non-setup job in the same stage
1180
1184
  * @param {String} currentJobName Current job
@@ -1215,5 +1219,6 @@ module.exports = {
1215
1219
  trimJobName,
1216
1220
  isStartFromMiddleOfCurrentStage,
1217
1221
  hasFreezeWindows,
1222
+ getNextJobStageName,
1218
1223
  BUILD_STATUS_MESSAGES
1219
1224
  };
package/plugins/helper.js CHANGED
@@ -2,8 +2,10 @@
2
2
 
3
3
  const boom = require('@hapi/boom');
4
4
  const dayjs = require('dayjs');
5
+ const schema = require('screwdriver-data-schema');
5
6
  const STAGE_PREFIX = 'stage@';
6
7
  const STAGE_TEARDOWN_PATTERN = /^stage@([\w-]+):teardown$/;
8
+ const { PR_STAGE_NAME } = schema.config.regex;
7
9
 
8
10
  /**
9
11
  * Set default start time and end time
@@ -111,7 +113,11 @@ async function getScmUri({ pipeline, pipelineFactory }) {
111
113
  * @return {String} Full stage name
112
114
  */
113
115
  function getFullStageJobName({ stageName, jobName }) {
114
- return `${STAGE_PREFIX}${stageName}:${jobName}`;
116
+ const prStage = stageName.match(PR_STAGE_NAME);
117
+
118
+ return prStage
119
+ ? `${prStage[1]}:${STAGE_PREFIX}${prStage[2]}:${jobName}` // PR-123:stage@staging:deploy
120
+ : `${STAGE_PREFIX}${stageName}:${jobName}`; // stage@staging:deploy
115
121
  }
116
122
 
117
123
  /**