diff --git a/config/regex.js b/config/regex.js index 4954014e..cd791da2 100644 --- a/config/regex.js +++ b/config/regex.js @@ -51,6 +51,8 @@ module.exports = { INTERNAL_TRIGGER: /^~([\w-]+)$/, // Stages can only be named with A-Z,a-z,0-9,-,_ STAGE_NAME: /^[\w-]+$/, + // Stage name prefixed with @stage. Ex: stage@prod + QUALIFIED_STAGE_NAME: /^stage@([\w-]+)$/, // Stage trigger like ~stage@deploy or ~stage@stageName:jobName or stage@deploy or stage@stageName:jobName STAGE_TRIGGER: /^~?stage@([\w-]+)(?::([\w-]+))?$/, // Don't combine EXTERNAL_TRIGGER and EXTERNAL_TRIGGER_AND for backward compatibility diff --git a/models/event.js b/models/event.js index fbb626fd..a70f1b04 100644 --- a/models/event.js +++ b/models/event.js @@ -10,6 +10,7 @@ const parentBuildId = require('./build').get.extract('parentBuildId'); const parentBuilds = require('./build').get.extract('parentBuilds'); const buildId = require('./build').get.extract('id'); const prNum = Scm.hook.extract('prNum'); +const { QUALIFIED_STAGE_NAME } = require('../config/regex'); const MODEL = { id: Joi.number().integer().positive().description('Identifier of this event').example(123345), @@ -48,7 +49,7 @@ const MODEL = { .description('SHA of the configuration pipeline this project depends on') .example('ccc49349d3cffbd12ea9e3d41521480b4aa5de5f'), startFrom: Joi.alternatives() - .try(trigger, jobName) + .try(trigger, jobName, QUALIFIED_STAGE_NAME) .description('Event start point - a job name or trigger name (~commit/~pr)') .example('~commit'), type: Joi.string().valid('pr', 'pipeline').max(10).description('Type of the event').example('pr'), diff --git a/test/models/event.test.js b/test/models/event.test.js index 66eb6c0a..2399da93 100644 --- a/test/models/event.test.js +++ b/test/models/event.test.js @@ -6,8 +6,37 @@ const { validate } = require('../helper'); describe('model event', () => { describe('base', () => { + const schema = models.event.base; + const baseYamlFile = 'event.yaml'; + it('validates the base', () => { - assert.isNull(validate('event.yaml', models.event.base).error); + assert.isNull(validate(baseYamlFile, schema).error); + }); + + describe('tests for startFrom', () => { + [null, '', '~some-internal-job-name', '~stage@integration', '~stage@integration:setup'].forEach( + startFrom => { + it('validates the invalid startFrom', () => { + assert.isNotNull(validate(baseYamlFile, schema, { startFrom }).error); + }); + } + ); + + [ + '~commit', + '~pr', + '~tag', + '~release', + 'some-internal-job-name', + 'stage@integration', + 'stage@integration:setup', + 'sd@123:some-external-job-name', + '~sd@123:some-external-job-name' + ].forEach(startFrom => { + it('validates the valid startFrom', () => { + assert.isNull(validate(baseYamlFile, schema, { startFrom }).error); + }); + }); }); }); diff --git a/test/models/templatetag.test.js b/test/models/templatetag.test.js index 020e91d4..86b55e6d 100644 --- a/test/models/templatetag.test.js +++ b/test/models/templatetag.test.js @@ -11,7 +11,7 @@ describe('model template tag', () => { }); [null, '', 'some_invalid_type', 'JOBS', 'PIPELINES'].forEach(validType => { - it('validates the invalid states', () => { + it('validates the invalid types', () => { assert.isNotNull( validate('templatetag.yaml', models.templateTag.base, { templateType: validType }).error ); @@ -19,7 +19,7 @@ describe('model template tag', () => { }); ['JOB', 'PIPELINE'].forEach(validType => { - it('validates the valid states', () => { + it('validates the valid types', () => { assert.isNull(validate('templatetag.yaml', models.templateTag.base, { templateType: validType }).error); }); });