diff --git a/app/assets/javascripts/pages/projects/pipelines/new/index.js b/app/assets/javascripts/pages/projects/pipelines/new/index.js index 9aa8945e268854e4831962e7e0a6be30f2dee82f..b0b077a5e4c09d1652668ce34b5435ff3db644ed 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 1dda557657c30c0c334aba2fe385ee957f1ab627..3852a5b89f71c756a56f19c6d84c3e7b57cff533 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -159,7 +159,7 @@ def render_show 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 9ec5bfef7c65bc7ce610571c514ac7b7238fef95..9c7b0d1e0a26f793ad0810a3a1e19f4b1f61b7c3 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -44,6 +44,8 @@ class Pipeline < ActiveRecord::Base 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 de5aae17a150c36992c24dfb18320bf748e61857..38e14ffbc0c441060fc1d8d4ed2fe517cdb74a91 100644 --- a/app/models/ci/pipeline_variable.rb +++ b/app/models/ci/pipeline_variable.rb @@ -5,6 +5,8 @@ class PipelineVariable < ActiveRecord::Base 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 e4e149000f701e171752b989a2749ad0933c2f30..3a393bf74f53327f555e41338f3bb569aa5512c8 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -27,6 +27,7 @@ def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request 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 8f2142af2cef8cd575b35ca388fc1644408904fb..81984ee94b0156dc4466ffef5741fcb71276d0d0 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 0000000000000000000000000000000000000000..8854eeb5fba97baacb97d18cc0aab08a3d9007e0 --- /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 70732d26bbd573c10e905f7516247ba2c3645709..b5eb0cfa2f051705bb823b4a470af69305b88fdc 100644 --- a/lib/gitlab/ci/pipeline/chain/build.rb +++ b/lib/gitlab/ci/pipeline/chain/build.rb @@ -14,7 +14,8 @@ def perform! 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 c152cd4b5546455c85616b4a5c92a62c04a4c418..e1693274e875fe89ec91f1e896a2f7b96dc6c878 100644 --- a/lib/gitlab/ci/pipeline/chain/command.rb +++ b/lib/gitlab/ci/pipeline/chain/command.rb @@ -7,7 +7,7 @@ module Chain :origin_ref, :checkout_sha, :after_sha, :before_sha, :trigger_request, :schedule, :ignore_skip_ci, :save_incompleted, - :seeds_block, + :seeds_block, :variables_attributes, # EE specific :allow_mirror_update, diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 705ba78a0b78e10f4f8941361785d3799fe45dfe..d404bc66ba8ca9b533d54cec17efc3bd7973a73d 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -517,16 +517,31 @@ def create_build(stage, stage_idx, name, status) 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 @@ def create_build(stage, stage_idx, name, status) 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 @@ def create_build(stage, stage_idx, name, status) 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 3ae7053a995ed22c3801ed743c349a4d991c4bdf..85d73e5c3826b0be377242dd674b364d422b5617 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 @@ 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 @@ 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 @@ 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 267258b33a80ac1164417f7bc25001b6364c4006..9a0b6efd8a907faf1492e11cec0bff9d60d4f0b2 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -17,11 +17,13 @@ def execute_service( 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 @@ def previous_commit_sha_from_ref(ref) 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