Skip to content

Commit

Permalink
fix(2767): Remove stage build in build remove (#606)
Browse files Browse the repository at this point in the history
  • Loading branch information
tkyi authored Feb 9, 2024
1 parent 6afb3a4 commit 0ef2d3d
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 15 deletions.
36 changes: 31 additions & 5 deletions lib/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const logger = require('screwdriver-logger');
const { EXTERNAL_TRIGGER } = require('screwdriver-data-schema').config.regex;
const { SCM_STATE_MAP, SCM_STATUSES } = require('screwdriver-data-schema').plugins.scm;
const BaseModel = require('./base');
const { getStageFromSetupJobName } = require('./helper');

// Symbols for private members
const executor = Symbol('executor');
Expand Down Expand Up @@ -646,15 +647,40 @@ class BuildModel extends BaseModel {
}

/**
* Remove all steps associated with this build and the build itself
* Remove all steps associated with this build, any stageBuild associated
* with this build, and the build itself
* @return {Promise} Resolves to null if remove successfully
*/
remove() {
async remove() {
// eslint-disable-next-line global-require
const stepFactory = require('./stepFactory');
const factory = stepFactory.getInstance();
const StepFactory = require('./stepFactory');
const stepFactory = StepFactory.getInstance();
const job = await this.job;
const nextStageName = getStageFromSetupJobName(job.name);

// Delete stageBuild if current build is from a stage setup job
if (nextStageName) {
/* eslint-disable global-require */
const StageFactory = require('./stageFactory');
const StageBuildFactory = require('./stageBuildFactory');
/* eslint-enable global-require */
const stageFactory = StageFactory.getInstance();
const stageBuildFactory = StageBuildFactory.getInstance();
const pipeline = await this.pipeline;
const nextStage = await stageFactory.get({
name: nextStageName,
pipelineId: pipeline.id
});
const nextStageBuild = await stageBuildFactory.get({
stageId: nextStage.id,
eventId: this.eventId
});

await nextStageBuild.remove();
}

return factory.removeSteps({ buildId: this.id }).then(() => super.remove());
// Remove steps
return stepFactory.removeSteps({ buildId: this.id }).then(() => super.remove());
}

/**
Expand Down
13 changes: 6 additions & 7 deletions lib/buildFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ const logger = require('screwdriver-logger');
const BaseFactory = require('./baseFactory');
const Build = require('./build');
const { STATUS_QUERY, LATEST_BUILD_QUERY, getQueries } = require('./rawQueries');
const helper = require('./helper');
const STAGE_SETUP_PATTERN = /^stage@([\w-]+)(?::setup)$/;
const { getBuildClusterName, getBookendKey, getStageFromSetupJobName } = require('./helper');

let instance;

Expand Down Expand Up @@ -218,20 +217,20 @@ class BuildFactory extends BaseFactory {
provider = permutation.provider;
}

const buildClusterName = await helper.getBuildClusterName({
const buildClusterName = await getBuildClusterName({
annotations,
pipeline,
multiBuildClusterEnabled: String(this.multiBuildClusterEnabled) === 'true',
provider
});

// Create stage build if current job is stage setup
const stageSetupJob = job.name.match(STAGE_SETUP_PATTERN);
const nextStageName = getStageFromSetupJobName(job.name);

if (stageSetupJob) {
if (nextStageName) {
const stage = await stageFactory.get({
pipelineId: pipeline.id,
name: stageSetupJob[1]
name: nextStageName
});

await stageBuildFactory.create({
Expand All @@ -242,7 +241,7 @@ class BuildFactory extends BaseFactory {
}

// Set correct bookend key
const bookendKey = await helper.getBookendKey({
const bookendKey = await getBookendKey({
buildClusterName,
annotations,
pipeline,
Expand Down
15 changes: 14 additions & 1 deletion lib/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const EXECUTOR_ANNOTATION = 'screwdriver.cd/executor';
const EXECUTOR_ANNOTATION_BETA = 'beta.screwdriver.cd/executor';
const SCM_ORG_REGEX = /^([^/]+)\/.*/;
const STAGE_PREFIX = 'stage@';
const STAGE_SETUP_PATTERN = /^stage@([\w-]+)(?::setup)$/; // stage@jobName:setup;

/**
* Get the value of the annotation that matches name
Expand Down Expand Up @@ -409,6 +410,17 @@ function getFullStageJobName({ stageName, jobName }) {
return `${STAGE_PREFIX}${stageName}:${jobName}`;
}

/**
* Check if next job is stage setup job, get stage name from that name
* @param {String} jobName Job name
* @return {String} Stage name
*/
function getStageFromSetupJobName(jobName) {
const stageSetupJobName = jobName.match(STAGE_SETUP_PATTERN);

return stageSetupJobName ? stageSetupJobName[1] : null;
}

module.exports = {
getAnnotations,
convertToBool,
Expand All @@ -417,5 +429,6 @@ module.exports = {
getBuildClusterName,
getToken,
getBookendKey,
getFullStageJobName
getFullStageJobName,
getStageFromSetupJobName
};
32 changes: 30 additions & 2 deletions test/lib/build.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -982,16 +982,44 @@ describe('Build Model', () => {
stepsMock = [step0Mock, step1Mock, step2Mock];
datastore.remove.resolves({});
stepFactoryMock.list.resolves(stepsMock);
jobFactoryMock.get.resolves(jobMock);
});

it('remove build and build steps', () => {
it('removes build and build steps', () => {
return build.remove().then(() => {
assert.calledOnce(stepFactoryMock.removeSteps); // remove steps in one shot
assert.calledOnce(datastore.remove); // remove the build
});
});

it('removes build and build steps and stageBuild', () => {
const stageBuildMock = {
remove: sinon.stub().resolves({})
};

jobFactoryMock.get.resolves({
id: jobId,
name: 'stage@main:setup',
pipeline: Promise.resolve({
id: pipelineId,
configPipelineId,
scmUri,
scmContext,
admin: Promise.resolve(adminUser),
token: Promise.resolve('foo')
}),
isPR: sinon.stub().returns(false)
});
stageBuildFactoryMock.get.resolves(stageBuildMock);

return build.remove().then(() => {
assert.calledOnce(stepFactoryMock.removeSteps); // remove steps in one shot
assert.calledOnce(datastore.remove); // remove the build
assert.calledOnce(stageBuildMock.remove); // remove the build
});
});

it('fail if removeSteps returns error', () => {
it('fails if removeSteps returns error', () => {
stepFactoryMock.removeSteps.rejects(new Error('error removing step'));

return build
Expand Down

0 comments on commit 0ef2d3d

Please sign in to comment.