Commit b53bd7c0 authored by Marius Bobin's avatar Marius Bobin Committed by Thong Kuah
Browse files

Add .:pre:. and .:post:. stages to pipelines

Adds two new predefined and always present stages to pipelines.
.:pre:. will always be the first stage in a pipeline
.:post:. will always be the last stage in a pipeline
parent 0544ea42
---
title: Add two new predefined stages to pipelines
merge_request: 18205
author:
type: added
...@@ -78,8 +78,13 @@ module Gitlab ...@@ -78,8 +78,13 @@ module Gitlab
def build_config(config) def build_config(config)
initial_config = Gitlab::Config::Loader::Yaml.new(config).load! initial_config = Gitlab::Config::Loader::Yaml.new(config).load!
initial_config = Config::External::Processor.new(initial_config, @context).perform initial_config = Config::External::Processor.new(initial_config, @context).perform
initial_config = Config::Extendable.new(initial_config).to_hash
   
Config::Extendable.new(initial_config).to_hash if Feature.enabled?(:ci_pre_post_pipeline_stages, @context.project, default_enabled: true)
initial_config = Config::EdgeStagesInjector.new(initial_config).to_hash
end
initial_config
end end
   
def build_context(project:, sha:, user:) def build_context(project:, sha:, user:)
......
# frozen_string_literal: true
module Gitlab
module Ci
class Config
class EdgeStagesInjector
PRE_PIPELINE = '.pre'
POST_PIPELINE = '.post'
EDGES = [PRE_PIPELINE, POST_PIPELINE].freeze
def self.wrap_stages(stages)
stages = stages.to_a - EDGES
stages.unshift PRE_PIPELINE
stages.push POST_PIPELINE
stages
end
def initialize(config)
@config = config.to_h.deep_dup
end
def to_hash
if config.key?(:stages)
process(:stages)
elsif config.key?(:types)
process(:types)
else
config
end
end
private
attr_reader :config
delegate :wrap_stages, to: :class
def process(keyword)
stages = extract_stages(keyword)
return config if stages.empty?
stages = wrap_stages(stages)
config[keyword] = stages
config
end
def extract_stages(keyword)
stages = config[keyword]
return [] unless stages.is_a?(Array)
stages
end
end
end
end
end
...@@ -15,7 +15,7 @@ module Gitlab ...@@ -15,7 +15,7 @@ module Gitlab
end end
   
def self.default def self.default
%w[build test deploy] Config::EdgeStagesInjector.wrap_stages %w[build test deploy]
end end
end end
end end
......
# frozen_string_literal: true
require 'fast_spec_helper'
describe Gitlab::Ci::Config::EdgeStagesInjector do
describe '#call' do
subject { described_class.new(config).to_hash }
context 'without stages' do
let(:config) do
{
test: { script: 'test' }
}
end
it { is_expected.to match config }
end
context 'with values' do
let(:config) do
{
stages: %w[stage1 stage2],
test: { script: 'test' }
}
end
let(:expected_stages) do
%w[.pre stage1 stage2 .post]
end
it { is_expected.to match(config.merge(stages: expected_stages)) }
end
context 'with bad values' do
let(:config) do
{
stages: 'stage1',
test: { script: 'test' }
}
end
it { is_expected.to match(config) }
end
context 'with collision values' do
let(:config) do
{
stages: %w[.post stage1 .pre .post stage2],
test: { script: 'test' }
}
end
let(:expected_stages) do
%w[.pre stage1 stage2 .post]
end
it { is_expected.to match(config.merge(stages: expected_stages)) }
end
context 'with types' do
let(:config) do
{
types: %w[stage1 stage2],
test: { script: 'test' }
}
end
let(:expected_config) do
{
types: %w[.pre stage1 stage2 .post],
test: { script: 'test' }
}
end
it { is_expected.to match expected_config }
end
context 'with types' do
let(:config) do
{
types: %w[.post stage1 .pre .post stage2],
test: { script: 'test' }
}
end
let(:expected_config) do
{
types: %w[.pre stage1 stage2 .post],
test: { script: 'test' }
}
end
it { is_expected.to match expected_config }
end
end
describe '.wrap_stages' do
subject { described_class.wrap_stages(stages) }
context 'with empty value' do
let(:stages) {}
it { is_expected.to eq %w[.pre .post] }
end
context 'with values' do
let(:stages) { %w[s1 .pre] }
it { is_expected.to eq %w[.pre s1 .post] }
end
end
end
...@@ -215,7 +215,7 @@ describe Gitlab::Ci::Config::Entry::Root do ...@@ -215,7 +215,7 @@ describe Gitlab::Ci::Config::Entry::Root do
   
describe '#stages_value' do describe '#stages_value' do
it 'returns an array of root stages' do it 'returns an array of root stages' do
expect(root.stages_value).to eq %w[build test deploy] expect(root.stages_value).to eq %w[.pre build test deploy .post]
end end
end end
   
......
...@@ -42,7 +42,7 @@ describe Gitlab::Ci::Config::Entry::Stages do ...@@ -42,7 +42,7 @@ describe Gitlab::Ci::Config::Entry::Stages do
   
describe '.default' do describe '.default' do
it 'returns default stages' do it 'returns default stages' do
expect(described_class.default).to eq %w[build test deploy] expect(described_class.default).to eq %w[.pre build test deploy .post]
end end
end end
end end
...@@ -51,6 +51,54 @@ describe Gitlab::Ci::Config do ...@@ -51,6 +51,54 @@ describe Gitlab::Ci::Config do
end end
end end
end end
describe '#stages' do
subject(:subject) { config.stages }
context 'with default stages' do
let(:default_stages) do
%w[.pre build test deploy .post]
end
it { is_expected.to eq default_stages }
end
context 'with custom stages' do
let(:yml) do
<<-EOS
stages:
- stage1
- stage2
job1:
stage: stage1
script:
- ls
EOS
end
it { is_expected.to eq %w[.pre stage1 stage2 .post] }
end
context 'with feature disabled' do
before do
stub_feature_flags(ci_pre_post_pipeline_stages: false)
end
let(:yml) do
<<-EOS
stages:
- stage1
- stage2
job1:
stage: stage1
script:
- ls
EOS
end
it { is_expected.to eq %w[stage1 stage2] }
end
end
end end
   
context 'when using extendable hash' do context 'when using extendable hash' do
......
...@@ -26,7 +26,7 @@ module Gitlab ...@@ -26,7 +26,7 @@ module Gitlab
it 'returns valid build attributes' do it 'returns valid build attributes' do
expect(subject).to eq({ expect(subject).to eq({
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 2,
name: "rspec", name: "rspec",
options: { options: {
before_script: ["pwd"], before_script: ["pwd"],
...@@ -56,7 +56,7 @@ module Gitlab ...@@ -56,7 +56,7 @@ module Gitlab
it 'returns valid build attributes' do it 'returns valid build attributes' do
expect(subject).to eq({ expect(subject).to eq({
stage: 'test', stage: 'test',
stage_idx: 1, stage_idx: 2,
name: 'rspec', name: 'rspec',
options: { script: ['rspec'] }, options: { script: ['rspec'] },
rules: [ rules: [
...@@ -209,13 +209,16 @@ module Gitlab ...@@ -209,13 +209,16 @@ module Gitlab
end end
   
let(:attributes) do let(:attributes) do
[{ name: "build", [{ name: ".pre",
index: 0, index: 0,
builds: [] }, builds: [] },
{ name: "test", { name: "build",
index: 1, index: 1,
builds: [] },
{ name: "test",
index: 2,
builds: builds:
[{ stage_idx: 1, [{ stage_idx: 2,
stage: "test", stage: "test",
name: "rspec", name: "rspec",
allow_failure: false, allow_failure: false,
...@@ -225,9 +228,9 @@ module Gitlab ...@@ -225,9 +228,9 @@ module Gitlab
only: { refs: ["branches"] }, only: { refs: ["branches"] },
except: {} }] }, except: {} }] },
{ name: "deploy", { name: "deploy",
index: 2, index: 3,
builds: builds:
[{ stage_idx: 2, [{ stage_idx: 3,
stage: "deploy", stage: "deploy",
name: "prod", name: "prod",
allow_failure: false, allow_failure: false,
...@@ -235,7 +238,10 @@ module Gitlab ...@@ -235,7 +238,10 @@ module Gitlab
yaml_variables: [], yaml_variables: [],
options: { script: ["cap prod"] }, options: { script: ["cap prod"] },
only: { refs: ["tags"] }, only: { refs: ["tags"] },
except: {} }] }] except: {} }] },
{ name: ".post",
index: 4,
builds: [] }]
end end
   
it 'returns stages seed attributes' do it 'returns stages seed attributes' do
...@@ -425,7 +431,7 @@ module Gitlab ...@@ -425,7 +431,7 @@ module Gitlab
expect(config_processor.stage_builds_attributes("test").size).to eq(1) expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({ expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 2,
name: "rspec", name: "rspec",
options: { options: {
before_script: ["pwd"], before_script: ["pwd"],
...@@ -456,7 +462,7 @@ module Gitlab ...@@ -456,7 +462,7 @@ module Gitlab
expect(config_processor.stage_builds_attributes("test").size).to eq(1) expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({ expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 2,
name: "rspec", name: "rspec",
options: { options: {
before_script: ["pwd"], before_script: ["pwd"],
...@@ -485,7 +491,7 @@ module Gitlab ...@@ -485,7 +491,7 @@ module Gitlab
expect(config_processor.stage_builds_attributes("test").size).to eq(1) expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({ expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 2,
name: "rspec", name: "rspec",
options: { options: {
before_script: ["pwd"], before_script: ["pwd"],
...@@ -510,7 +516,7 @@ module Gitlab ...@@ -510,7 +516,7 @@ module Gitlab
expect(config_processor.stage_builds_attributes("test").size).to eq(1) expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({ expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 2,
name: "rspec", name: "rspec",
options: { options: {
before_script: ["pwd"], before_script: ["pwd"],
...@@ -977,7 +983,7 @@ module Gitlab ...@@ -977,7 +983,7 @@ module Gitlab
expect(config_processor.stage_builds_attributes("test").size).to eq(1) expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({ expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 2,
name: "rspec", name: "rspec",
options: { options: {
before_script: ["pwd"], before_script: ["pwd"],
...@@ -1272,7 +1278,7 @@ module Gitlab ...@@ -1272,7 +1278,7 @@ module Gitlab
expect(subject.builds.size).to eq(5) expect(subject.builds.size).to eq(5)
expect(subject.builds[0]).to eq( expect(subject.builds[0]).to eq(
stage: "build", stage: "build",
stage_idx: 0, stage_idx: 1,
name: "build1", name: "build1",
options: { options: {
script: ["test"] script: ["test"]
...@@ -1283,7 +1289,7 @@ module Gitlab ...@@ -1283,7 +1289,7 @@ module Gitlab
) )
expect(subject.builds[2]).to eq( expect(subject.builds[2]).to eq(
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 2,
name: "test1", name: "test1",
options: { options: {
script: ["test"], script: ["test"],
...@@ -1398,7 +1404,7 @@ module Gitlab ...@@ -1398,7 +1404,7 @@ module Gitlab
expect(subject.size).to eq(1) expect(subject.size).to eq(1)
expect(subject.first).to eq({ expect(subject.first).to eq({
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 2,
name: "normal_job", name: "normal_job",
options: { options: {
script: ["test"] script: ["test"]
...@@ -1442,7 +1448,7 @@ module Gitlab ...@@ -1442,7 +1448,7 @@ module Gitlab
expect(subject.size).to eq(2) expect(subject.size).to eq(2)
expect(subject.first).to eq({ expect(subject.first).to eq({
stage: "build", stage: "build",
stage_idx: 0, stage_idx: 1,
name: "job1", name: "job1",
options: { options: {
script: ["execute-script-for-job"] script: ["execute-script-for-job"]
...@@ -1453,7 +1459,7 @@ module Gitlab ...@@ -1453,7 +1459,7 @@ module Gitlab
}) })
expect(subject.second).to eq({ expect(subject.second).to eq({
stage: "build", stage: "build",
stage_idx: 0, stage_idx: 1,
name: "job2", name: "job2",
options: { options: {
script: ["execute-script-for-job"] script: ["execute-script-for-job"]
...@@ -1665,14 +1671,14 @@ module Gitlab ...@@ -1665,14 +1671,14 @@ module Gitlab
config = YAML.dump({ rspec: { script: "test", type: "acceptance" } }) config = YAML.dump({ rspec: { script: "test", type: "acceptance" } })
expect do expect do
Gitlab::Ci::YamlProcessor.new(config) Gitlab::Ci::YamlProcessor.new(config)
end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "rspec job: stage parameter should be .pre, build, test, deploy, .post")
end end
   
it "returns errors if job stage is not a defined stage" do it "returns errors if job stage is not a defined stage" do
config = YAML.dump({ types: %w(build test), rspec: { script: "test", type: "acceptance" } }) config = YAML.dump({ types: %w(build test), rspec: { script: "test", type: "acceptance" } })
expect do expect do
Gitlab::Ci::YamlProcessor.new(config) Gitlab::Ci::YamlProcessor.new(config)
end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "rspec job: stage parameter should be .pre, build, test, .post")
end end
   
it "returns errors if stages is not an array" do it "returns errors if stages is not an array" do
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment