diff --git a/app/assets/javascripts/pages/projects/pipelines/new/index.js b/app/assets/javascripts/pages/projects/pipelines/new/index.js index 9aa8945e268..b0b077a5e4c 100644 --- a/app/assets/javascripts/pages/projects/pipelines/new/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/new/index.js @@ -1,6 +1,12 @@ import $ from 'jquery'; import NewBranchForm from '~/new_branch_form'; +import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list'; document.addEventListener('DOMContentLoaded', () => { new NewBranchForm($('.js-new-pipeline-form')); // eslint-disable-line no-new + + setupNativeFormVariableList({ + container: $('.js-ci-variable-list-section'), + formField: 'variables_attributes', + }); }); diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 78d109cf33e..1ee273091d4 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -157,7 +157,7 @@ class Projects::PipelinesController < Projects::ApplicationController end def create_params - params.require(:pipeline).permit(:ref) + params.require(:pipeline).permit(:ref, variables_attributes: %i[key secret_value]) end def pipeline diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index e1b9bc76475..c184f398f6c 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -32,6 +32,8 @@ module Ci has_many :auto_canceled_pipelines, class_name: 'Ci::Pipeline', foreign_key: 'auto_canceled_by_id' has_many :auto_canceled_jobs, class_name: 'CommitStatus', foreign_key: 'auto_canceled_by_id' + accepts_nested_attributes_for :variables, reject_if: :persisted? + delegate :id, to: :project, prefix: true delegate :full_path, to: :project, prefix: true diff --git a/app/models/ci/pipeline_variable.rb b/app/models/ci/pipeline_variable.rb index de5aae17a15..38e14ffbc0c 100644 --- a/app/models/ci/pipeline_variable.rb +++ b/app/models/ci/pipeline_variable.rb @@ -5,6 +5,8 @@ module Ci belongs_to :pipeline + alias_attribute :secret_value, :value + validates :key, uniqueness: { scope: :pipeline_id } end end diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index 6ce86983287..17a53b6a8fd 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -24,6 +24,7 @@ module Ci ignore_skip_ci: ignore_skip_ci, save_incompleted: save_on_errors, seeds_block: block, + variables_attributes: params[:variables_attributes], project: project, current_user: current_user) diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml index 8f2142af2ce..81984ee94b0 100644 --- a/app/views/projects/pipelines/new.html.haml +++ b/app/views/projects/pipelines/new.html.haml @@ -1,5 +1,6 @@ - breadcrumb_title "Pipelines" - page_title = s_("Pipeline|Run Pipeline") +- settings_link = link_to _('CI/CD settings'), project_settings_ci_cd_path(@project) %h3.page-title = s_("Pipeline|Run Pipeline") @@ -8,17 +9,26 @@ = form_for @pipeline, as: :pipeline, url: project_pipelines_path(@project), html: { id: "new-pipeline-form", class: "form-horizontal js-new-pipeline-form js-requires-input" } do |f| = form_errors(@pipeline) .form-group - = f.label :ref, s_('Pipeline|Run on'), class: 'control-label' - .col-sm-10 + .col-sm-12 + = f.label :ref, s_('Pipeline|Create for') = hidden_field_tag 'pipeline[ref]', params[:ref] || @project.default_branch = dropdown_tag(params[:ref] || @project.default_branch, options: { toggle_class: 'js-branch-select wide git-revision-dropdown-toggle', filter: true, dropdown_class: "dropdown-menu-selectable git-revision-dropdown", placeholder: s_("Pipeline|Search branches"), data: { selected: params[:ref] || @project.default_branch, field_name: 'pipeline[ref]' } }) .help-block - = s_("Pipeline|Existing branch name, tag") + = s_("Pipeline|Existing branch name or tag") + + .col-sm-12.prepend-top-10.js-ci-variable-list-section + %label + = s_('Pipeline|Variables') + %ul.ci-variable-list + = render 'ci/variables/variable_row', form_field: 'pipeline', only_key_value: true + .help-block + = (s_("Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default.") % {settings_link: settings_link}).html_safe + .form-actions - = f.submit s_('Pipeline|Run pipeline'), class: 'btn btn-success', tabindex: 3 + = f.submit s_('Pipeline|Create pipeline'), class: 'btn btn-success js-variables-save-button', tabindex: 3 = link_to 'Cancel', project_pipelines_path(@project), class: 'btn btn-default pull-right' -# haml-lint:disable InlineJavaScript diff --git a/changelogs/unreleased/44059-specify-variables-when-executing-a-manual-pipeline-from-the-ui.yml b/changelogs/unreleased/44059-specify-variables-when-executing-a-manual-pipeline-from-the-ui.yml new file mode 100644 index 00000000000..8854eeb5fba --- /dev/null +++ b/changelogs/unreleased/44059-specify-variables-when-executing-a-manual-pipeline-from-the-ui.yml @@ -0,0 +1,5 @@ +--- +title: Enable specifying variables when executing a manual pipeline +merge_request: 18440 +author: +type: changed diff --git a/lib/gitlab/ci/pipeline/chain/build.rb b/lib/gitlab/ci/pipeline/chain/build.rb index 70732d26bbd..b5eb0cfa2f0 100644 --- a/lib/gitlab/ci/pipeline/chain/build.rb +++ b/lib/gitlab/ci/pipeline/chain/build.rb @@ -14,7 +14,8 @@ module Gitlab trigger_requests: Array(@command.trigger_request), user: @command.current_user, pipeline_schedule: @command.schedule, - protected: @command.protected_ref? + protected: @command.protected_ref?, + variables_attributes: Array(@command.variables_attributes) ) @pipeline.set_config_source diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb index a1849b01c5d..a53c80d34f7 100644 --- a/lib/gitlab/ci/pipeline/chain/command.rb +++ b/lib/gitlab/ci/pipeline/chain/command.rb @@ -7,7 +7,7 @@ module Gitlab # rubocop:disable Naming/FileName :origin_ref, :checkout_sha, :after_sha, :before_sha, :trigger_request, :schedule, :ignore_skip_ci, :save_incompleted, - :seeds_block + :seeds_block, :variables_attributes ) do include Gitlab::Utils::StrongMemoize diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 705ba78a0b7..d404bc66ba8 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -517,16 +517,31 @@ describe 'Pipelines', :js do end it 'creates a new pipeline' do - expect { click_on 'Run pipeline' } + expect { click_on 'Create pipeline' } .to change { Ci::Pipeline.count }.by(1) expect(Ci::Pipeline.last).to be_web end + + context 'when variables are specified' do + it 'creates a new pipeline with variables' do + page.within '.ci-variable-row-body' do + fill_in "Input variable key", with: "key_name" + fill_in "Input variable value", with: "value" + end + + expect { click_on 'Create pipeline' } + .to change { Ci::Pipeline.count }.by(1) + + expect(Ci::Pipeline.last.variables.map { |var| var.slice(:key, :secret_value) }) + .to eq [{ key: "key_name", secret_value: "value" }.with_indifferent_access] + end + end end context 'without gitlab-ci.yml' do before do - click_on 'Run pipeline' + click_on 'Create pipeline' end it { expect(page).to have_content('Missing .gitlab-ci.yml file') } @@ -539,7 +554,7 @@ describe 'Pipelines', :js do click_link 'master' end - expect { click_on 'Run pipeline' } + expect { click_on 'Create pipeline' } .to change { Ci::Pipeline.count }.by(1) end end @@ -557,7 +572,7 @@ describe 'Pipelines', :js do it 'has field to add a new pipeline' do expect(page).to have_selector('.js-branch-select') expect(find('.js-branch-select')).to have_content project.default_branch - expect(page).to have_content('Run on') + expect(page).to have_content('Create for') end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb index 3ae7053a995..85d73e5c382 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb @@ -5,6 +5,10 @@ describe Gitlab::Ci::Pipeline::Chain::Build do set(:user) { create(:user) } let(:pipeline) { Ci::Pipeline.new } + let(:variables_attributes) do + [{ key: 'first', secret_value: 'world' }, + { key: 'second', secret_value: 'second_world' }] + end let(:command) do Gitlab::Ci::Pipeline::Chain::Command.new( source: :push, @@ -15,7 +19,8 @@ describe Gitlab::Ci::Pipeline::Chain::Build do trigger_request: nil, schedule: nil, project: project, - current_user: user) + current_user: user, + variables_attributes: variables_attributes) end let(:step) { described_class.new(pipeline, command) } @@ -39,6 +44,8 @@ describe Gitlab::Ci::Pipeline::Chain::Build do expect(pipeline.tag).to be false expect(pipeline.user).to eq user expect(pipeline.project).to eq project + expect(pipeline.variables.map { |var| var.slice(:key, :secret_value) }) + .to eq variables_attributes.map(&:with_indifferent_access) end it 'sets a valid config source' do diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 267258b33a8..9a0b6efd8a9 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -17,11 +17,13 @@ describe Ci::CreatePipelineService do after: project.commit.id, message: 'Message', ref: ref_name, - trigger_request: nil) + trigger_request: nil, + variables_attributes: nil) params = { ref: ref, before: '00000000', after: after, - commits: [{ message: message }] } + commits: [{ message: message }], + variables_attributes: variables_attributes } described_class.new(project, user, params).execute( source, trigger_request: trigger_request) @@ -545,5 +547,19 @@ describe Ci::CreatePipelineService do expect(pipeline.tag?).to be true end end + + context 'when pipeline variables are specified' do + let(:variables_attributes) do + [{ key: 'first', secret_value: 'world' }, + { key: 'second', secret_value: 'second_world' }] + end + + subject { execute_service(variables_attributes: variables_attributes) } + + it 'creates a pipeline with specified variables' do + expect(subject.variables.map { |var| var.slice(:key, :secret_value) }) + .to eq variables_attributes.map(&:with_indifferent_access) + end + end end end