Commit b27ccc99 authored by Fabio Pitino's avatar Fabio Pitino

Merge branch 'add-static-source-to-ci-pipeline-218685' into 'master'

Add ability to use YAML string to create CI pipelines

See merge request gitlab-org/gitlab!34706
parents d5116c5d 5c251887
...@@ -357,6 +357,10 @@ module Ci ...@@ -357,6 +357,10 @@ module Ci
success.group(:project_id).select('max(id) as id') success.group(:project_id).select('max(id) as id')
end end
def self.last_finished_for_ref_id(ci_ref_id)
where(ci_ref_id: ci_ref_id).ci_sources.finished.order(id: :desc).select(:id).take
end
def self.truncate_sha(sha) def self.truncate_sha(sha)
sha[0...8] sha[0...8]
end end
......
...@@ -45,7 +45,8 @@ module Ci ...@@ -45,7 +45,8 @@ module Ci
webide_source: 3, webide_source: 3,
remote_source: 4, remote_source: 4,
external_project_source: 5, external_project_source: 5,
bridge_source: 6 bridge_source: 6,
parameter_source: 7
} }
end end
......
...@@ -43,7 +43,7 @@ module Ci ...@@ -43,7 +43,7 @@ module Ci
end end
def last_finished_pipeline_id def last_finished_pipeline_id
Ci::Pipeline.where(ci_ref_id: self.id).finished.order(id: :desc).select(:id).take&.id Ci::Pipeline.last_finished_for_ref_id(self.id)&.id
end end
def update_status_by!(pipeline) def update_status_by!(pipeline)
......
...@@ -23,6 +23,24 @@ module Ci ...@@ -23,6 +23,24 @@ module Ci
Gitlab::Ci::Pipeline::Chain::Limit::Activity, Gitlab::Ci::Pipeline::Chain::Limit::Activity,
Gitlab::Ci::Pipeline::Chain::Limit::JobActivity].freeze Gitlab::Ci::Pipeline::Chain::Limit::JobActivity].freeze
# Create a new pipeline in the specified project.
#
# @param [Symbol] source What event (Ci::Pipeline.sources) triggers the pipeline
# creation.
# @param [Boolean] ignore_skip_ci Whether skipping a pipeline creation when `[skip ci]` comment
# is present in the commit body
# @param [Boolean] save_on_errors Whether persisting an invalid pipeline when it encounters an
# error during creation (e.g. invalid yaml)
# @param [Ci::TriggerRequest] trigger_request The pipeline trigger triggers the pipeline creation.
# @param [Ci::PipelineSchedule] schedule The pipeline schedule triggers the pipeline creation.
# @param [MergeRequest] merge_request The merge request triggers the pipeline creation.
# @param [ExternalPullRequest] external_pull_request The external pull request triggers the pipeline creation.
# @param [Ci::Bridge] bridge The bridge job that triggers the downstream pipeline creation.
# @param [String] content The content of .gitlab-ci.yml to override the default config
# contents (e.g. .gitlab-ci.yml in repostiry). Mainly used for
# generating a dangling pipeline.
#
# @return [Ci::Pipeline] The created Ci::Pipeline object.
# rubocop: disable Metrics/ParameterLists # rubocop: disable Metrics/ParameterLists
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, merge_request: nil, external_pull_request: nil, bridge: nil, **options, &block) def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, merge_request: nil, external_pull_request: nil, bridge: nil, **options, &block)
@pipeline = Ci::Pipeline.new @pipeline = Ci::Pipeline.new
...@@ -122,13 +140,8 @@ module Ci ...@@ -122,13 +140,8 @@ module Ci
end end
end end
def extra_options(options = {}) def extra_options(content: nil)
# In Ruby 2.4, even when options is empty, f(**options) doesn't work when f { content: content }
# doesn't have any parameters. We reproduce the Ruby 2.5 behavior by
# checking explicitly that no arguments are given.
raise ArgumentError if options.any?
{} # overridden in EE
end end
end end
end end
......
---
title: Ability to use an arbitrary YAML blob to create CI pipelines
merge_request: 34706
author:
type: added
...@@ -6,10 +6,8 @@ module EE ...@@ -6,10 +6,8 @@ module EE
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
override :extra_options override :extra_options
def extra_options(mirror_update: false) def extra_options(mirror_update: false, **options)
{ options.merge(allow_mirror_update: mirror_update)
allow_mirror_update: mirror_update
}
end end
end end
end end
......
...@@ -10,7 +10,7 @@ module Gitlab ...@@ -10,7 +10,7 @@ module Gitlab
:trigger_request, :schedule, :merge_request, :external_pull_request, :trigger_request, :schedule, :merge_request, :external_pull_request,
:ignore_skip_ci, :save_incompleted, :ignore_skip_ci, :save_incompleted,
:seeds_block, :variables_attributes, :push_options, :seeds_block, :variables_attributes, :push_options,
:chat_data, :allow_mirror_update, :bridge, :chat_data, :allow_mirror_update, :bridge, :content,
# These attributes are set by Chains during processing: # These attributes are set by Chains during processing:
:config_content, :config_processor, :stage_seeds :config_content, :config_processor, :stage_seeds
) do ) do
......
...@@ -9,6 +9,7 @@ module Gitlab ...@@ -9,6 +9,7 @@ module Gitlab
include Chain::Helpers include Chain::Helpers
SOURCES = [ SOURCES = [
Gitlab::Ci::Pipeline::Chain::Config::Content::Parameter,
Gitlab::Ci::Pipeline::Chain::Config::Content::Bridge, Gitlab::Ci::Pipeline::Chain::Config::Content::Bridge,
Gitlab::Ci::Pipeline::Chain::Config::Content::Repository, Gitlab::Ci::Pipeline::Chain::Config::Content::Repository,
Gitlab::Ci::Pipeline::Chain::Config::Content::ExternalProject, Gitlab::Ci::Pipeline::Chain::Config::Content::ExternalProject,
......
# frozen_string_literal: true
module Gitlab
module Ci
module Pipeline
module Chain
module Config
class Content
class Parameter < Source
def content
strong_memoize(:content) do
next unless command.content.present?
command.content
end
end
def source
:parameter_source
end
end
end
end
end
end
end
end
...@@ -11,6 +11,8 @@ module Gitlab ...@@ -11,6 +11,8 @@ module Gitlab
DEFAULT_YAML_FILE = '.gitlab-ci.yml' DEFAULT_YAML_FILE = '.gitlab-ci.yml'
attr_reader :command
def initialize(pipeline, command) def initialize(pipeline, command)
@pipeline = pipeline @pipeline = pipeline
@command = command @command = command
......
...@@ -5,7 +5,8 @@ require 'spec_helper' ...@@ -5,7 +5,8 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Chain::Config::Content do RSpec.describe Gitlab::Ci::Pipeline::Chain::Config::Content do
let(:project) { create(:project, ci_config_path: ci_config_path) } let(:project) { create(:project, ci_config_path: ci_config_path) }
let(:pipeline) { build(:ci_pipeline, project: project) } let(:pipeline) { build(:ci_pipeline, project: project) }
let(:command) { Gitlab::Ci::Pipeline::Chain::Command.new(project: project) } let(:content) { nil }
let(:command) { Gitlab::Ci::Pipeline::Chain::Command.new(project: project, content: content) }
subject { described_class.new(pipeline, command) } subject { described_class.new(pipeline, command) }
...@@ -141,6 +142,25 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Config::Content do ...@@ -141,6 +142,25 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Config::Content do
end end
end end
context 'when config is passed as a parameter' do
let(:ci_config_path) { nil }
let(:content) do
<<~EOY
---
stages:
- dast
EOY
end
it 'uses the parameter content' do
subject.perform!
expect(pipeline.config_source).to eq 'parameter_source'
expect(pipeline.pipeline_config.content).to eq(content)
expect(command.config_content).to eq(content)
end
end
context 'when config is not defined anywhere' do context 'when config is not defined anywhere' do
let(:ci_config_path) { nil } let(:ci_config_path) { nil }
......
...@@ -1978,6 +1978,23 @@ RSpec.describe Ci::Pipeline, :mailer do ...@@ -1978,6 +1978,23 @@ RSpec.describe Ci::Pipeline, :mailer do
end end
end end
describe '.last_finished_for_ref_id' do
let(:project) { create(:project, :repository) }
let(:branch) { project.default_branch }
let(:ref) { project.ci_refs.take }
let(:config_source) { Ci::PipelineEnums.config_sources[:parameter_source] }
let!(:pipeline1) { create(:ci_pipeline, :success, project: project, ref: branch) }
let!(:pipeline2) { create(:ci_pipeline, :success, project: project, ref: branch) }
let!(:pipeline3) { create(:ci_pipeline, :failed, project: project, ref: branch) }
let!(:pipeline4) { create(:ci_pipeline, :success, project: project, ref: branch) }
let!(:pipeline5) { create(:ci_pipeline, :success, project: project, ref: branch, config_source: config_source) }
it 'returns the expected pipeline' do
result = described_class.last_finished_for_ref_id(ref.id)
expect(result).to eq(pipeline4)
end
end
describe '.internal_sources' do describe '.internal_sources' do
subject { described_class.internal_sources } subject { described_class.internal_sources }
......
...@@ -62,6 +62,35 @@ RSpec.describe Ci::Ref do ...@@ -62,6 +62,35 @@ RSpec.describe Ci::Ref do
end end
end end
describe '#last_finished_pipeline_id' do
let(:pipeline_status) { :running }
let(:config_source) { Ci::PipelineEnums.config_sources[:repository_source] }
let(:pipeline) { create(:ci_pipeline, pipeline_status, config_source: config_source) }
let(:ci_ref) { pipeline.ci_ref }
context 'when there are no finished pipelines' do
it 'returns nil' do
expect(ci_ref.last_finished_pipeline_id).to be_nil
end
end
context 'when there are finished pipelines' do
let(:pipeline_status) { :success }
it 'returns the pipeline id' do
expect(ci_ref.last_finished_pipeline_id).to eq(pipeline.id)
end
context 'when the pipeline is not a ci_source' do
let(:config_source) { Ci::PipelineEnums.config_sources[:parameter_source] }
it 'returns nil' do
expect(ci_ref.last_finished_pipeline_id).to be_nil
end
end
end
end
describe '#update_status_by!' do describe '#update_status_by!' do
subject { ci_ref.update_status_by!(pipeline) } subject { ci_ref.update_status_by!(pipeline) }
......
# frozen_string_literal: true
require 'spec_helper'
describe Ci::CreatePipelineService do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:admin) }
let(:service) { described_class.new(project, user, { ref: 'refs/heads/master' }) }
let(:content) do
<<~EOY
---
stages:
- dast
variables:
DAST_VERSION: 1
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
dast:
stage: dast
image:
name: "$SECURE_ANALYZERS_PREFIX/dast:$DAST_VERSION"
variables:
GIT_STRATEGY: none
script:
- /analyze
EOY
end
describe '#execute' do
subject { service.execute(:web, content: content) }
context 'parameter config content' do
it 'creates a pipeline' do
expect(subject).to be_persisted
end
it 'creates builds with the correct names' do
expect(subject.builds.pluck(:name)).to match_array %w[dast]
end
it 'creates stages with the correct names' do
expect(subject.stages.pluck(:name)).to match_array %w[dast]
end
it 'sets the correct config source' do
expect(subject.config_source).to eq 'parameter_source'
end
end
end
end
Markdown is supported
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