From cea3593b3858e77a872670263c58c8bbfefd9f8e Mon Sep 17 00:00:00 2001 From: Dayanand Sagar Date: Thu, 12 Dec 2024 14:11:49 -0800 Subject: [PATCH] feat(3181): Skip execution of a virtual job when an event is started from that job (#635) BREAKING CHANGE: Virtual job at the beginning of the event is no longer queued for execution. API changes are needed to process such build to mark its status as 'SUCCESS' and trigger its downstream jobs. --- lib/build.js | 91 +++++++++++++++++------------------ lib/eventFactory.js | 7 +++ package.json | 2 +- test/lib/eventFactory.test.js | 89 +++++++++++++++++++++++++++++++++- 4 files changed, 141 insertions(+), 48 deletions(-) diff --git a/lib/build.js b/lib/build.js index 7ca9bf65..5e56c922 100644 --- a/lib/build.js +++ b/lib/build.js @@ -526,56 +526,55 @@ class BuildModel extends BaseModel { return this.job.then(job => Promise.all([job.pipeline, job.prNum]) .then(([pipeline, prNum]) => - getBlockedByIds(pipeline, job).then(blockedBy => { - const config = { - build: this, - buildId: this.id, - eventId: this.eventId, - jobId: job.id, - jobState: job.state, - jobArchived: job.archived, - jobName: job.name, - annotations: hoek.reach(job.permutations[0], 'annotations', { default: {} }), - blockedBy, - pipelineId: pipeline.id, - pipeline: { - id: pipeline.id, - name: pipeline.name, - scmContext: pipeline.scmContext, - configPipelineId: pipeline.configPipelineId - }, - causeMessage: causeMessage || '', - freezeWindows: hoek.reach(job.permutations[0], 'freezeWindows', { default: [] }), - apiUri: this[apiUri], - container: this.container, - tokenGen: this[tokenGen], - token: getToken(this, job, pipeline), - isPR: job.isPR(), - prParentJobId: job.prParentJobId - }; - - if (template && !hoek.deepEqual(template, {})) { - config.template = template; - } + getBlockedByIds(pipeline, job) + .then(blockedBy => { + const config = { + build: this, + buildId: this.id, + eventId: this.eventId, + jobId: job.id, + jobState: job.state, + jobArchived: job.archived, + jobName: job.name, + annotations: hoek.reach(job.permutations[0], 'annotations', { default: {} }), + blockedBy, + pipelineId: pipeline.id, + pipeline: { + id: pipeline.id, + name: pipeline.name, + scmContext: pipeline.scmContext, + configPipelineId: pipeline.configPipelineId + }, + causeMessage: causeMessage || '', + freezeWindows: hoek.reach(job.permutations[0], 'freezeWindows', { default: [] }), + apiUri: this[apiUri], + container: this.container, + tokenGen: this[tokenGen], + token: getToken(this, job, pipeline), + isPR: job.isPR(), + prParentJobId: job.prParentJobId + }; + + if (template && !hoek.deepEqual(template, {})) { + config.template = template; + } - if (prNum) { - config.prNum = prNum; - } + if (prNum) { + config.prNum = prNum; + } - if (this.buildClusterName) { - config.buildClusterName = this.buildClusterName; - } + if (this.buildClusterName) { + config.buildClusterName = this.buildClusterName; + } - if (hoek.reach(job.permutations[0], 'provider')) { - config.provider = job.permutations[0].provider; - } + if (hoek.reach(job.permutations[0], 'provider')) { + config.provider = job.permutations[0].provider; + } - return Promise.all([ - this[executor].start(config), - this.updateCommitStatus(pipeline) // update github - ]); - }) - ) + return this[executor].start(config); + }) + .then(() => this.updateCommitStatus(pipeline)) + ) // update github .then(() => this) ); } diff --git a/lib/eventFactory.js b/lib/eventFactory.js index 491ddc73..aa6e99bc 100644 --- a/lib/eventFactory.js +++ b/lib/eventFactory.js @@ -406,6 +406,13 @@ function createBuilds(config) { buildConfig.configPipelineSha = pipelineConfig.configPipelineSha; + const { annotations, freezeWindows } = j.permutations[0]; + const isVirtualJob = annotations ? annotations['screwdriver.cd/virtualJob'] === true : false; + const hasFreezeWindows = freezeWindows ? freezeWindows.length > 0 : false; + + // Bypass execution of the build if the job is virtual + buildConfig.start = !isVirtualJob || hasFreezeWindows; + return startBuild({ decoratedCommit, subscribedConfigSha, diff --git a/package.json b/package.json index 1bbdbee2..494f11e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "screwdriver-models", - "version": "29.0.0", + "version": "31.0.0", "description": "Screwdriver models", "main": "index.js", "scripts": { diff --git a/test/lib/eventFactory.test.js b/test/lib/eventFactory.test.js index 8d488038..a06c2e5e 100644 --- a/test/lib/eventFactory.test.js +++ b/test/lib/eventFactory.test.js @@ -179,6 +179,8 @@ describe('Event Factory', () => { { name: '~pr' }, { name: '~commit' }, { name: 'main' }, + { name: 'firstVirtual' }, + { name: 'secondVirtual' }, { name: 'disabledJob' }, { name: 'stage@integration:setup', stageName: 'integration' }, { name: 'int-deploy', stageName: 'integration' }, @@ -198,6 +200,8 @@ describe('Event Factory', () => { { src: '~sd@123:main', dest: 'main' }, { src: '~pr', dest: 'main' }, { src: '~commit', dest: 'main' }, + { src: '~commit', dest: 'firstVirtual' }, + { src: '~commit', dest: 'secondVirtual' }, { src: 'main', dest: 'disabledJob' }, { src: 'main', dest: 'stage@integration:setup' }, { src: 'stage@integration:setup', dest: 'int-deploy' }, @@ -236,6 +240,8 @@ describe('Event Factory', () => { { name: '~pr' }, { name: '~commit' }, { name: 'main' }, + { name: 'firstVirtual' }, + { name: 'secondVirtual' }, { name: 'disabledJob' }, { name: 'stage@integration:setup', stageName: 'integration' }, { name: 'int-deploy', stageName: 'integration' }, @@ -255,6 +261,8 @@ describe('Event Factory', () => { { src: '~sd@123:main', dest: 'main' }, { src: '~pr', dest: 'main' }, { src: '~commit', dest: 'main' }, + { src: '~commit', dest: 'firstVirtual' }, + { src: '~commit', dest: 'secondVirtual' }, { src: 'main', dest: 'disabledJob' }, { src: 'main', dest: 'stage@integration:setup' }, { src: 'stage@integration:setup', dest: 'int-deploy' }, @@ -471,6 +479,38 @@ describe('Event Factory', () => { ], state: 'ENABLED', isPR: sinon.stub().returns(false) + }, + + { + id: 16, + pipelineId: 8765, + name: 'firstVirtual', + permutations: [ + { + requires: ['~commit', '~pr'], + annotations: { + 'screwdriver.cd/virtualJob': true + } + } + ], + state: 'ENABLED', + isPR: sinon.stub().returns(false) + }, + { + id: 17, + pipelineId: 8765, + name: 'secondVirtual', + permutations: [ + { + requires: ['~commit', '~pr'], + freezeWindows: ['* 10-21 ? * *'], + annotations: { + 'screwdriver.cd/virtualJob': true + } + } + ], + state: 'ENABLED', + isPR: sinon.stub().returns(false) } ]; @@ -901,12 +941,59 @@ describe('Event Factory', () => { return eventFactory.create(config).then(model => { assert.instanceOf(model, Event); assert.notCalled(jobFactoryMock.create); + assert.callCount(buildFactoryMock.create, 6); assert.calledWith( buildFactoryMock.create, sinon.match({ parentBuildId: 12345, eventId: model.id, - jobId: 1 + jobId: 1, + start: true + }) + ); + assert.calledWith( + buildFactoryMock.create, + sinon.match({ + parentBuildId: 12345, + eventId: model.id, + jobId: 5, + start: true + }) + ); + assert.calledWith( + buildFactoryMock.create, + sinon.match({ + parentBuildId: 12345, + eventId: model.id, + jobId: 6, + start: true + }) + ); + assert.calledWith( + buildFactoryMock.create, + sinon.match({ + parentBuildId: 12345, + eventId: model.id, + jobId: 7, + start: true + }) + ); + assert.calledWith( + buildFactoryMock.create, + sinon.match({ + parentBuildId: 12345, + eventId: model.id, + jobId: 16, + start: false // should skip execution of virtual job without freeze windows + }) + ); + assert.calledWith( + buildFactoryMock.create, + sinon.match({ + parentBuildId: 12345, + eventId: model.id, + jobId: 17, + start: true }) ); assert.calledOnce(pipelineMock.sync);