screwdriver-api 7.0.69 → 7.0.71
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 +2 -2
- package/plugins/builds/index.js +130 -19
- package/plugins/builds/update.js +114 -8
- package/plugins/helper.js +13 -1
- package/plugins/pipelines/README.md +0 -2
- package/plugins/pipelines/templates/get.js +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "screwdriver-api",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.71",
|
|
4
4
|
"description": "API server for the Screwdriver.cd service",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"screwdriver-executor-queue": "^4.0.0",
|
|
115
115
|
"screwdriver-executor-router": "^3.0.0",
|
|
116
116
|
"screwdriver-logger": "^2.0.0",
|
|
117
|
-
"screwdriver-models": "^29.
|
|
117
|
+
"screwdriver-models": "^29.17.2",
|
|
118
118
|
"screwdriver-notifications-email": "^3.0.0",
|
|
119
119
|
"screwdriver-notifications-slack": "^5.0.0",
|
|
120
120
|
"screwdriver-request": "^2.0.1",
|
package/plugins/builds/index.js
CHANGED
|
@@ -20,6 +20,7 @@ const tokenRoute = require('./token');
|
|
|
20
20
|
const metricsRoute = require('./metrics');
|
|
21
21
|
const { EXTERNAL_TRIGGER_ALL } = schema.config.regex;
|
|
22
22
|
const locker = require('../lock');
|
|
23
|
+
const { getFullStageJobName } = require('../helper');
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Checks if job is external trigger
|
|
@@ -230,7 +231,7 @@ async function createExternalBuild(config) {
|
|
|
230
231
|
* @param {String} [config.jobName] Job name
|
|
231
232
|
* @param {String} config.username Username of build
|
|
232
233
|
* @param {String} config.scmContext SCM context
|
|
233
|
-
* @param {Object} config.parentBuilds
|
|
234
|
+
* @param {Object} [config.parentBuilds] Builds that triggered this build
|
|
234
235
|
* @param {String} config.baseBranch Branch name
|
|
235
236
|
* @param {Number} [config.parentBuildId] Parent build ID
|
|
236
237
|
* @param {Boolean} [config.start] Whether to start the build or not
|
|
@@ -272,6 +273,7 @@ async function createInternalBuild(config) {
|
|
|
272
273
|
} else {
|
|
273
274
|
job = await jobFactory.get(jobId);
|
|
274
275
|
}
|
|
276
|
+
|
|
275
277
|
const internalBuildConfig = {
|
|
276
278
|
jobId: job.id,
|
|
277
279
|
sha: event.sha,
|
|
@@ -526,9 +528,10 @@ async function getParentBuildStatus({ newBuild, joinListNames, pipelineId, build
|
|
|
526
528
|
* @param {Build} newBuild Next build
|
|
527
529
|
* @param {String} [jobName] Job name
|
|
528
530
|
* @param {String} [pipelineId] Pipeline ID
|
|
531
|
+
* @param {Object} [stage] Stage
|
|
529
532
|
* @return {Promise} The newly updated/created build
|
|
530
533
|
*/
|
|
531
|
-
async function handleNewBuild({ done, hasFailure, newBuild, jobName, pipelineId }) {
|
|
534
|
+
async function handleNewBuild({ done, hasFailure, newBuild, jobName, pipelineId, stage }) {
|
|
532
535
|
if (!done) {
|
|
533
536
|
return null;
|
|
534
537
|
}
|
|
@@ -538,10 +541,19 @@ async function handleNewBuild({ done, hasFailure, newBuild, jobName, pipelineId
|
|
|
538
541
|
|
|
539
542
|
// Delete new build since previous build failed
|
|
540
543
|
if (hasFailure) {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
)
|
|
544
|
-
|
|
544
|
+
let stageTeardownName = '';
|
|
545
|
+
|
|
546
|
+
if (stage) {
|
|
547
|
+
stageTeardownName = getFullStageJobName({ stageName: stage.name, jobName: 'teardown' });
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// New build is not stage teardown job
|
|
551
|
+
if (jobName !== stageTeardownName) {
|
|
552
|
+
logger.info(
|
|
553
|
+
`Failure occurred in upstream job, removing new build - build:${newBuild.id} pipeline:${pipelineId}-${jobName} event:${newBuild.eventId} `
|
|
554
|
+
);
|
|
555
|
+
await newBuild.remove();
|
|
556
|
+
}
|
|
545
557
|
|
|
546
558
|
return null;
|
|
547
559
|
}
|
|
@@ -709,6 +721,89 @@ function getParentBuildIds({ currentBuildId, parentBuilds, joinListNames, pipeli
|
|
|
709
721
|
return Array.from(new Set([currentBuildId, ...parentBuildIds]));
|
|
710
722
|
}
|
|
711
723
|
|
|
724
|
+
/**
|
|
725
|
+
* Create stage teardown build if it doesn't already exist
|
|
726
|
+
* @param {Factory} jobFactory Job factory
|
|
727
|
+
* @param {Factory} buildFactory Build factory
|
|
728
|
+
* @param {Object} current Current object
|
|
729
|
+
* @param {String} stageTeardownName Stage teardown name
|
|
730
|
+
* @param {String} username Username
|
|
731
|
+
* @param {String} scmContext SCM context
|
|
732
|
+
*/
|
|
733
|
+
async function ensureStageTeardownBuildExists({
|
|
734
|
+
jobFactory,
|
|
735
|
+
buildFactory,
|
|
736
|
+
current,
|
|
737
|
+
stageTeardownName,
|
|
738
|
+
username,
|
|
739
|
+
scmContext
|
|
740
|
+
}) {
|
|
741
|
+
// Check if stage teardown build already exists
|
|
742
|
+
const stageTeardownJob = await jobFactory.get({
|
|
743
|
+
pipelineId: current.pipeline.id,
|
|
744
|
+
name: stageTeardownName
|
|
745
|
+
});
|
|
746
|
+
const existingStageTeardownBuild = await buildFactory.get({
|
|
747
|
+
eventId: current.event.id,
|
|
748
|
+
jobId: stageTeardownJob.id
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
// Doesn't exist, create stage teardown job
|
|
752
|
+
if (!existingStageTeardownBuild) {
|
|
753
|
+
await createInternalBuild({
|
|
754
|
+
jobFactory,
|
|
755
|
+
buildFactory,
|
|
756
|
+
pipelineId: current.pipeline.id,
|
|
757
|
+
jobName: stageTeardownName,
|
|
758
|
+
username,
|
|
759
|
+
scmContext,
|
|
760
|
+
event: current.event, // this is the parentBuild for the next build
|
|
761
|
+
baseBranch: current.event.baseBranch || null,
|
|
762
|
+
start: false
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Delete nextBuild, create teardown build if it doesn't exist, and return teardown build or return null
|
|
769
|
+
* @param {String} nextJobName Next job name
|
|
770
|
+
* @param {Object} current Object with stage, event, pipeline info
|
|
771
|
+
* @param {Object} buildConfig Build config
|
|
772
|
+
* @param {Factory} jobFactory Job factory
|
|
773
|
+
* @param {Factory} buildFactory Build factory
|
|
774
|
+
* @param {String} username Username
|
|
775
|
+
* @param {String} scmContext Scm context
|
|
776
|
+
* @return {Array} Array of promises
|
|
777
|
+
*/
|
|
778
|
+
async function handleStageFailure({
|
|
779
|
+
nextJobName,
|
|
780
|
+
current,
|
|
781
|
+
buildConfig,
|
|
782
|
+
jobFactory,
|
|
783
|
+
buildFactory,
|
|
784
|
+
username,
|
|
785
|
+
scmContext
|
|
786
|
+
}) {
|
|
787
|
+
const buildDeletePromises = [];
|
|
788
|
+
const stageTeardownName = getFullStageJobName({ stageName: current.stage.name, jobName: 'teardown' });
|
|
789
|
+
|
|
790
|
+
// Remove next build
|
|
791
|
+
if (buildConfig.eventId && nextJobName !== stageTeardownName) {
|
|
792
|
+
buildDeletePromises.push(deleteBuild(buildConfig, buildFactory));
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
await ensureStageTeardownBuildExists({
|
|
796
|
+
jobFactory,
|
|
797
|
+
buildFactory,
|
|
798
|
+
current,
|
|
799
|
+
stageTeardownName,
|
|
800
|
+
username,
|
|
801
|
+
scmContext
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
return buildDeletePromises;
|
|
805
|
+
}
|
|
806
|
+
|
|
712
807
|
/**
|
|
713
808
|
* Build API Plugin
|
|
714
809
|
* @method register
|
|
@@ -732,21 +827,19 @@ const buildsPlugin = {
|
|
|
732
827
|
* @return {Promise} Resolves to the removed build or null
|
|
733
828
|
*/
|
|
734
829
|
server.expose('removeJoinBuilds', async (config, app) => {
|
|
735
|
-
const { pipeline, job, build } = config;
|
|
736
|
-
const { eventFactory, buildFactory } = app;
|
|
737
|
-
const event = await eventFactory.get({ id: build.eventId });
|
|
830
|
+
const { pipeline, job, build, username, scmContext, event, stage } = config;
|
|
831
|
+
const { eventFactory, buildFactory, jobFactory } = app;
|
|
738
832
|
const current = {
|
|
739
833
|
pipeline,
|
|
740
834
|
job,
|
|
741
835
|
build,
|
|
742
|
-
event
|
|
836
|
+
event,
|
|
837
|
+
stage
|
|
743
838
|
};
|
|
744
|
-
|
|
745
839
|
const nextJobsTrigger = workflowParser.getNextJobs(current.event.workflowGraph, {
|
|
746
840
|
trigger: current.job.name,
|
|
747
841
|
chainPR: pipeline.chainPR
|
|
748
842
|
});
|
|
749
|
-
|
|
750
843
|
const pipelineJoinData = await createJoinObject(nextJobsTrigger, current, eventFactory);
|
|
751
844
|
const buildConfig = {};
|
|
752
845
|
const deletePromises = [];
|
|
@@ -765,7 +858,20 @@ const buildsPlugin = {
|
|
|
765
858
|
buildConfig.eventId = hoek.reach(pipelineJoinData[pid], 'event.id');
|
|
766
859
|
}
|
|
767
860
|
|
|
768
|
-
if
|
|
861
|
+
// if nextBuild is stage teardown, just return nextBuild
|
|
862
|
+
if (current.stage) {
|
|
863
|
+
const buildDeletePromises = await handleStageFailure({
|
|
864
|
+
nextJobName,
|
|
865
|
+
current,
|
|
866
|
+
buildConfig,
|
|
867
|
+
jobFactory,
|
|
868
|
+
buildFactory,
|
|
869
|
+
username,
|
|
870
|
+
scmContext
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
deletePromises.concat(buildDeletePromises);
|
|
874
|
+
} else if (buildConfig.eventId) {
|
|
769
875
|
deletePromises.push(deleteBuild(buildConfig, buildFactory));
|
|
770
876
|
}
|
|
771
877
|
} catch (err) {
|
|
@@ -776,6 +882,7 @@ const buildsPlugin = {
|
|
|
776
882
|
}
|
|
777
883
|
}
|
|
778
884
|
}
|
|
885
|
+
|
|
779
886
|
await Promise.all(deletePromises);
|
|
780
887
|
});
|
|
781
888
|
|
|
@@ -810,21 +917,20 @@ const buildsPlugin = {
|
|
|
810
917
|
* @return {Promise} Resolves to the newly created build or null
|
|
811
918
|
*/
|
|
812
919
|
server.expose('triggerNextJobs', async (config, app) => {
|
|
813
|
-
const { pipeline, job, build } = config;
|
|
920
|
+
const { pipeline, job, build, event, stage } = config;
|
|
814
921
|
const { eventFactory, pipelineFactory, buildFactory, jobFactory } = app;
|
|
815
|
-
const event = await eventFactory.get({ id: build.eventId });
|
|
816
922
|
const current = {
|
|
817
923
|
pipeline,
|
|
818
924
|
job,
|
|
819
925
|
build,
|
|
820
|
-
event
|
|
926
|
+
event,
|
|
927
|
+
stage
|
|
821
928
|
};
|
|
822
929
|
|
|
823
930
|
const nextJobsTrigger = workflowParser.getNextJobs(current.event.workflowGraph, {
|
|
824
931
|
trigger: current.job.name,
|
|
825
932
|
chainPR: pipeline.chainPR
|
|
826
933
|
});
|
|
827
|
-
|
|
828
934
|
const pipelineJoinData = await createJoinObject(nextJobsTrigger, current, eventFactory);
|
|
829
935
|
|
|
830
936
|
// Helper function to handle triggering jobs in same pipeline
|
|
@@ -877,12 +983,14 @@ const buildsPlugin = {
|
|
|
877
983
|
return existNextBuild;
|
|
878
984
|
}
|
|
879
985
|
|
|
986
|
+
// Current build is not part of stage
|
|
880
987
|
existNextBuild.status = 'QUEUED';
|
|
881
988
|
await existNextBuild.update();
|
|
882
989
|
|
|
883
990
|
return existNextBuild.start();
|
|
884
991
|
}
|
|
885
992
|
|
|
993
|
+
// Handle join case. Fan-out/fan-in Workflow
|
|
886
994
|
logger.info(`Fetching finished builds for event ${event.id}`);
|
|
887
995
|
let finishedInternalBuilds = await getFinishedBuilds(current.event, buildFactory);
|
|
888
996
|
|
|
@@ -967,7 +1075,8 @@ const buildsPlugin = {
|
|
|
967
1075
|
hasFailure,
|
|
968
1076
|
newBuild,
|
|
969
1077
|
jobName: nextJobName,
|
|
970
|
-
pipelineId: current.pipeline.id
|
|
1078
|
+
pipelineId: current.pipeline.id,
|
|
1079
|
+
stage: current.stage
|
|
971
1080
|
});
|
|
972
1081
|
};
|
|
973
1082
|
|
|
@@ -1107,7 +1216,8 @@ const buildsPlugin = {
|
|
|
1107
1216
|
hasFailure,
|
|
1108
1217
|
newBuild,
|
|
1109
1218
|
jobName: nextJobName,
|
|
1110
|
-
pipelineId: externalPipelineId
|
|
1219
|
+
pipelineId: externalPipelineId,
|
|
1220
|
+
stage: current.stage
|
|
1111
1221
|
});
|
|
1112
1222
|
}
|
|
1113
1223
|
}
|
|
@@ -1163,6 +1273,7 @@ const buildsPlugin = {
|
|
|
1163
1273
|
await locker.unlock(lock, resource);
|
|
1164
1274
|
}
|
|
1165
1275
|
}
|
|
1276
|
+
|
|
1166
1277
|
if (triggerCurrentPipelineAsExternal || !isCurrentPipeline) {
|
|
1167
1278
|
let resource;
|
|
1168
1279
|
let lock;
|
package/plugins/builds/update.js
CHANGED
|
@@ -5,7 +5,10 @@ const hoek = require('@hapi/hoek');
|
|
|
5
5
|
const schema = require('screwdriver-data-schema');
|
|
6
6
|
const joi = require('joi');
|
|
7
7
|
const idSchema = schema.models.build.base.extract('id');
|
|
8
|
-
const { getScmUri, getUserPermissions } = require('../helper');
|
|
8
|
+
const { getScmUri, getUserPermissions, getFullStageJobName } = require('../helper');
|
|
9
|
+
const STAGE_TEARDOWN_PATTERN = /^stage@([\w-]+)(?::teardown)$/;
|
|
10
|
+
const TERMINAL_STATUSES = ['FAILURE', 'ABORTED', 'UNSTABLE', 'COLLAPSED'];
|
|
11
|
+
const FINISHED_STATUSES = ['FAILURE', 'SUCCESS', 'ABORTED', 'UNSTABLE', 'COLLAPSED'];
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* Identify whether this build resulted in a previously failed job to become successful.
|
|
@@ -124,7 +127,7 @@ async function validateUserPermission(build, request) {
|
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
/**
|
|
127
|
-
*
|
|
130
|
+
* Set build status to desired status, set build statusMessage
|
|
128
131
|
* @param {Object} build Build Model
|
|
129
132
|
* @param {String} desiredStatus New Status
|
|
130
133
|
* @param {String} statusMessage User passed status message
|
|
@@ -155,6 +158,51 @@ function updateBuildStatus(build, desiredStatus, statusMessage, username) {
|
|
|
155
158
|
}
|
|
156
159
|
}
|
|
157
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Get stage for current node
|
|
163
|
+
* @param {StageFactory} stageFactory Stage factory
|
|
164
|
+
* @param {Object} workflowGraph Workflow graph
|
|
165
|
+
* @param {String} jobName Job name
|
|
166
|
+
* @param {Number} pipelineId Pipeline ID
|
|
167
|
+
* @return {Stage} Stage for node
|
|
168
|
+
*/
|
|
169
|
+
async function getStage({ stageFactory, workflowGraph, jobName, pipelineId }) {
|
|
170
|
+
const currentNode = workflowGraph.nodes.find(node => node.name === jobName);
|
|
171
|
+
let stage = null;
|
|
172
|
+
|
|
173
|
+
if (currentNode && currentNode.stageName) {
|
|
174
|
+
stage = await stageFactory.get({
|
|
175
|
+
pipelineId,
|
|
176
|
+
name: currentNode.stageName
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return Promise.resolve(stage);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Checks if all builds in stage are done running
|
|
185
|
+
* @param {Object} stage Stage
|
|
186
|
+
* @param {Object} event Event
|
|
187
|
+
* @return {Boolean} Flag if stage is done
|
|
188
|
+
*/
|
|
189
|
+
async function isStageDone({ stage, event }) {
|
|
190
|
+
// Get all jobIds for jobs in the stage
|
|
191
|
+
const stageJobIds = stage.jobIds;
|
|
192
|
+
|
|
193
|
+
stageJobIds.push(stage.setup);
|
|
194
|
+
|
|
195
|
+
// Get all builds in a stage for this event
|
|
196
|
+
const stageJobBuilds = await event.getBuilds({ params: { jobId: stageJobIds } });
|
|
197
|
+
let stageIsDone = false;
|
|
198
|
+
|
|
199
|
+
if (stageJobBuilds && stageJobBuilds.length !== 0) {
|
|
200
|
+
stageIsDone = !stageJobBuilds.some(b => !FINISHED_STATUSES.includes(b.status));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return stageIsDone;
|
|
204
|
+
}
|
|
205
|
+
|
|
158
206
|
module.exports = () => ({
|
|
159
207
|
method: 'PUT',
|
|
160
208
|
path: '/builds/{id}',
|
|
@@ -168,13 +216,14 @@ module.exports = () => ({
|
|
|
168
216
|
},
|
|
169
217
|
|
|
170
218
|
handler: async (request, h) => {
|
|
171
|
-
const { buildFactory, eventFactory, jobFactory } = request.server.app;
|
|
219
|
+
const { buildFactory, eventFactory, jobFactory, stageFactory, stageBuildFactory } = request.server.app;
|
|
172
220
|
const { id } = request.params;
|
|
173
221
|
const { statusMessage, stats, status: desiredStatus } = request.payload;
|
|
174
222
|
const { username, scmContext, scope } = request.auth.credentials;
|
|
175
223
|
const isBuild = scope.includes('build') || scope.includes('temporal');
|
|
176
224
|
const { triggerNextJobs, removeJoinBuilds } = request.server.plugins.builds;
|
|
177
225
|
|
|
226
|
+
// Check token permissions
|
|
178
227
|
if (isBuild && username !== id) {
|
|
179
228
|
return boom.forbidden(`Credential only valid for ${username}`);
|
|
180
229
|
}
|
|
@@ -226,7 +275,6 @@ module.exports = () => ({
|
|
|
226
275
|
}
|
|
227
276
|
|
|
228
277
|
const [newBuild, newEvent] = await Promise.all([build.update(), event.update(), stopFrozen]);
|
|
229
|
-
|
|
230
278
|
const job = await newBuild.job;
|
|
231
279
|
const pipeline = await job.pipeline;
|
|
232
280
|
|
|
@@ -245,16 +293,74 @@ module.exports = () => ({
|
|
|
245
293
|
|
|
246
294
|
const skipFurther = /\[(skip further)\]/.test(newEvent.causeMessage);
|
|
247
295
|
|
|
248
|
-
//
|
|
249
|
-
//
|
|
296
|
+
// Update stageBuild status if it has changed;
|
|
297
|
+
// if stageBuild status is currently terminal, do not update
|
|
298
|
+
const stage = await getStage({
|
|
299
|
+
stageFactory,
|
|
300
|
+
workflowGraph: newEvent.workflowGraph,
|
|
301
|
+
jobName: job.name,
|
|
302
|
+
pipelineId: pipeline.id
|
|
303
|
+
});
|
|
304
|
+
const isStageTeardown = STAGE_TEARDOWN_PATTERN.test(job.name);
|
|
305
|
+
let stageBuildHasFailure = false;
|
|
306
|
+
|
|
307
|
+
if (stage) {
|
|
308
|
+
const stageBuild = await stageBuildFactory.get({
|
|
309
|
+
stageId: stage.id,
|
|
310
|
+
eventId: newEvent.id
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
if (stageBuild.status !== newBuild.status) {
|
|
314
|
+
if (!TERMINAL_STATUSES.includes(stageBuild.status)) {
|
|
315
|
+
stageBuild.status = newBuild.status;
|
|
316
|
+
await stageBuild.update();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
250
319
|
|
|
320
|
+
stageBuildHasFailure = TERMINAL_STATUSES.includes(stageBuild.status);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Guard against triggering non-successful or unstable builds
|
|
324
|
+
// Don't further trigger pipeline if intend to skip further jobs
|
|
251
325
|
if (newBuild.status !== 'SUCCESS' || skipFurther) {
|
|
252
326
|
// Check for failed jobs and remove any child jobs in created state
|
|
253
327
|
if (newBuild.status === 'FAILURE') {
|
|
254
|
-
await removeJoinBuilds(
|
|
328
|
+
await removeJoinBuilds(
|
|
329
|
+
{ pipeline, job, build: newBuild, username, scmContext, event: newEvent, stage },
|
|
330
|
+
request.server.app
|
|
331
|
+
);
|
|
255
332
|
}
|
|
333
|
+
// Do not continue downstream is current job is stage teardown and statusBuild has failure
|
|
334
|
+
} else if (newBuild.status === 'SUCCESS' && isStageTeardown && stageBuildHasFailure) {
|
|
335
|
+
await removeJoinBuilds(
|
|
336
|
+
{ pipeline, job, build: newBuild, username, scmContext, event: newEvent, stage },
|
|
337
|
+
request.server.app
|
|
338
|
+
);
|
|
256
339
|
} else {
|
|
257
|
-
await triggerNextJobs(
|
|
340
|
+
await triggerNextJobs(
|
|
341
|
+
{ pipeline, job, build: newBuild, username, scmContext, event: newEvent, stage },
|
|
342
|
+
request.server.app
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Determine if stage teardown build should start
|
|
347
|
+
// (if stage teardown build exists, and stageBuild.status is negative,
|
|
348
|
+
// and there are no active stage builds, and teardown build is not started)
|
|
349
|
+
if (stage && FINISHED_STATUSES.includes(newBuild.status)) {
|
|
350
|
+
const stageTeardownName = getFullStageJobName({ stageName: stage.name, jobName: 'teardown' });
|
|
351
|
+
const stageTeardownJob = await jobFactory.get({ pipelineId: pipeline.id, name: stageTeardownName });
|
|
352
|
+
const stageTeardownBuild = await buildFactory.get({ eventId: newEvent.id, jobId: stageTeardownJob.id });
|
|
353
|
+
|
|
354
|
+
// Start stage teardown build if stage is done
|
|
355
|
+
if (stageTeardownBuild && stageTeardownBuild.status === 'CREATED') {
|
|
356
|
+
const stageIsDone = await isStageDone({ stage, event: newEvent });
|
|
357
|
+
|
|
358
|
+
if (stageIsDone) {
|
|
359
|
+
stageTeardownBuild.status = 'QUEUED';
|
|
360
|
+
await stageTeardownBuild.update();
|
|
361
|
+
await stageTeardownBuild.start();
|
|
362
|
+
}
|
|
363
|
+
}
|
|
258
364
|
}
|
|
259
365
|
|
|
260
366
|
return h.response(await newBuild.toJsonWithSteps()).code(200);
|
package/plugins/helper.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const boom = require('@hapi/boom');
|
|
4
4
|
const dayjs = require('dayjs');
|
|
5
|
+
const STAGE_PREFIX = 'stage@';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Set default start time and end time
|
|
@@ -102,10 +103,21 @@ async function getScmUri({ pipeline, pipelineFactory }) {
|
|
|
102
103
|
return scmUri;
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Returns full stage name with correct formatting and setup or teardown suffix (e.g. stage@deploy:setup)
|
|
108
|
+
* @param {String} stageName Stage name
|
|
109
|
+
* @param {String} type Type of stage job, either 'setup' or 'teardown'
|
|
110
|
+
* @return {String} Full stage name
|
|
111
|
+
*/
|
|
112
|
+
function getFullStageJobName({ stageName, jobName }) {
|
|
113
|
+
return `${STAGE_PREFIX}${stageName}:${jobName}`;
|
|
114
|
+
}
|
|
115
|
+
|
|
105
116
|
module.exports = {
|
|
106
117
|
getReadOnlyInfo,
|
|
107
118
|
getScmUri,
|
|
108
119
|
getUserPermissions,
|
|
109
120
|
setDefaultTimeRange,
|
|
110
|
-
validTimeRange
|
|
121
|
+
validTimeRange,
|
|
122
|
+
getFullStageJobName
|
|
111
123
|
};
|