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
def build_config(config)
initial_config = Gitlab::Config::Loader::Yaml.new(config).load!
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
 
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
end
 
def self.default
%w[build test deploy]
Config::EdgeStagesInjector.wrap_stages %w[build test deploy]
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
 
describe '#stages_value' 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
 
......
......@@ -42,7 +42,7 @@ describe Gitlab::Ci::Config::Entry::Stages do
 
describe '.default' 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
......@@ -51,6 +51,54 @@ describe Gitlab::Ci::Config do
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
 
context 'when using extendable hash' do
......
......@@ -26,7 +26,7 @@ module Gitlab
it 'returns valid build attributes' do
expect(subject).to eq({
stage: "test",
stage_idx: 1,
stage_idx: 2,
name: "rspec",
options: {
before_script: ["pwd"],
......@@ -56,7 +56,7 @@ module Gitlab
it 'returns valid build attributes' do
expect(subject).to eq({
stage: 'test',
stage_idx: 1,
stage_idx: 2,
name: 'rspec',
options: { script: ['rspec'] },
rules: [
......@@ -209,13 +209,16 @@ module Gitlab
end
 
let(:attributes) do
[{ name: "build",
[{ name: ".pre",
index: 0,
builds: [] },
{ name: "test",
{ name: "build",
index: 1,
builds: [] },
{ name: "test",
index: 2,
builds:
[{ stage_idx: 1,
[{ stage_idx: 2,
stage: "test",
name: "rspec",
allow_failure: false,
......@@ -225,9 +228,9 @@ module Gitlab
only: { refs: ["branches"] },
except: {} }] },
{ name: "deploy",
index: 2,
index: 3,
builds:
[{ stage_idx: 2,
[{ stage_idx: 3,
stage: "deploy",
name: "prod",
allow_failure: false,
......@@ -235,7 +238,10 @@ module Gitlab
yaml_variables: [],
options: { script: ["cap prod"] },
only: { refs: ["tags"] },
except: {} }] }]
except: {} }] },
{ name: ".post",
index: 4,
builds: [] }]
end
 
it 'returns stages seed attributes' do
......@@ -425,7 +431,7 @@ module Gitlab
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test",
stage_idx: 1,
stage_idx: 2,
name: "rspec",
options: {
before_script: ["pwd"],
......@@ -456,7 +462,7 @@ module Gitlab
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test",
stage_idx: 1,
stage_idx: 2,
name: "rspec",
options: {
before_script: ["pwd"],
......@@ -485,7 +491,7 @@ module Gitlab
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test",
stage_idx: 1,
stage_idx: 2,
name: "rspec",
options: {
before_script: ["pwd"],
......@@ -510,7 +516,7 @@ module Gitlab
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test",
stage_idx: 1,
stage_idx: 2,
name: "rspec",
options: {
before_script: ["pwd"],
......@@ -977,7 +983,7 @@ module Gitlab
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test",
stage_idx: 1,
stage_idx: 2,
name: "rspec",
options: {
before_script: ["pwd"],
......@@ -1272,7 +1278,7 @@ module Gitlab
expect(subject.builds.size).to eq(5)
expect(subject.builds[0]).to eq(
stage: "build",
stage_idx: 0,
stage_idx: 1,
name: "build1",
options: {
script: ["test"]
......@@ -1283,7 +1289,7 @@ module Gitlab
)
expect(subject.builds[2]).to eq(
stage: "test",
stage_idx: 1,
stage_idx: 2,
name: "test1",
options: {
script: ["test"],
......@@ -1398,7 +1404,7 @@ module Gitlab
expect(subject.size).to eq(1)
expect(subject.first).to eq({
stage: "test",
stage_idx: 1,
stage_idx: 2,
name: "normal_job",
options: {
script: ["test"]
......@@ -1442,7 +1448,7 @@ module Gitlab
expect(subject.size).to eq(2)
expect(subject.first).to eq({
stage: "build",
stage_idx: 0,
stage_idx: 1,
name: "job1",
options: {
script: ["execute-script-for-job"]
......@@ -1453,7 +1459,7 @@ module Gitlab
})
expect(subject.second).to eq({
stage: "build",
stage_idx: 0,
stage_idx: 1,
name: "job2",
options: {
script: ["execute-script-for-job"]
......@@ -1665,14 +1671,14 @@ module Gitlab
config = YAML.dump({ rspec: { script: "test", type: "acceptance" } })
expect do
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
 
it "returns errors if job stage is not a defined stage" do
config = YAML.dump({ types: %w(build test), rspec: { script: "test", type: "acceptance" } })
expect do
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
 
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