diff --git a/app/models/ci/job_input.rb b/app/models/ci/job_input.rb index 5015e8a39e6b39e3a9681612b5036255e3fded51..394b81c9300e7d1bd8b9c0ec9e62f087af3c56f7 100644 --- a/app/models/ci/job_input.rb +++ b/app/models/ci/job_input.rb @@ -1,8 +1,18 @@ # frozen_string_literal: true module Ci + # Stores input values for CI jobs. + # + # Records are only persisted when a job is retried with user-submitted input values. + # On first run, jobs use default values from the input spec stored in options[:inputs], + # and no Ci::JobInput records are created. This avoids storing unnecessary data since + # most jobs use default values. + # + # The fallback logic to default values is implemented in: + # - BuildRunnerPresenter#runner_inputs (for job execution) class JobInput < Ci::ApplicationRecord include Ci::Partitionable + include BulkInsertSafe MAX_VALUE_SIZE = ::Gitlab::Ci::Config::Interpolation::Access::MAX_ACCESS_BYTESIZE diff --git a/app/presenters/ci/build_runner_presenter.rb b/app/presenters/ci/build_runner_presenter.rb index de50abf3da07299fd9f4ac356729ab19d4d91e31..b69934e3db012a377ef573a694ffe791f227ad61 100644 --- a/app/presenters/ci/build_runner_presenter.rb +++ b/app/presenters/ci/build_runner_presenter.rb @@ -51,7 +51,7 @@ def runner_inputs key: name, value: { content: input_value, - type: spec[:input_type] + type: spec[:type] } } end diff --git a/lib/gitlab/ci/pipeline/seed/build.rb b/lib/gitlab/ci/pipeline/seed/build.rb index 37c73699249b5eed7cac4b8775ee54187de68581..cc1f465e93ba0c5b5aab63717adfda5668b252eb 100644 --- a/lib/gitlab/ci/pipeline/seed/build.rb +++ b/lib/gitlab/ci/pipeline/seed/build.rb @@ -19,6 +19,7 @@ def initialize(context, attributes, stages_for_needs_lookup) @job_variables = @seed_attributes.delete(:job_variables) @execution_config_attribute = @seed_attributes.delete(:execution_config) @root_variables_inheritance = @seed_attributes.delete(:root_variables_inheritance) { true } + @inputs = @seed_attributes.delete(:inputs) @using_rules = attributes.key?(:rules) @using_only = attributes.key?(:only) @@ -71,6 +72,7 @@ def attributes .deep_merge(rules_attributes) .deep_merge(allow_failure_criteria_attributes) .deep_merge(@cache.cache_attributes) + .deep_merge(inputs_attributes) .deep_merge(runner_tags) .deep_merge(build_execution_config_attribute) .deep_merge(scoped_user_id_attribute) @@ -238,6 +240,14 @@ def allow_failure_criteria_attributes { options: { allow_failure_criteria: nil } } end + def inputs_attributes + return {} unless @inputs + + { + options: { inputs: @inputs } + } + end + def calculate_yaml_variables! @seed_attributes[:yaml_variables] = Gitlab::Ci::Variables::Helpers.inherit_yaml_variables( from: @context.root_variables, to: @job_variables, inheritance: @root_variables_inheritance diff --git a/spec/lib/api/entities/ci/job_request/response_spec.rb b/spec/lib/api/entities/ci/job_request/response_spec.rb index 2346f78abec2fbc6bf8071e1095153cfee3f614c..4514733407b44ff77480b79eb6aa3c812ac6fb47 100644 --- a/spec/lib/api/entities/ci/job_request/response_spec.rb +++ b/spec/lib/api/entities/ci/job_request/response_spec.rb @@ -7,7 +7,7 @@ let_it_be(:job) do create( :ci_build, runner: runner, - options: { inputs: { test_input: { input_type: 'string', default: 'test' } } } + options: { inputs: { test_input: { type: 'string', default: 'test' } } } ) end diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb index 4e6e53efd0032b1c17dee1b4cdf195888a5ffd6d..6a3d5dce981412426add3b1ce32bb933d1b7b1f0 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb @@ -475,6 +475,70 @@ end end + context 'with job:inputs' do + let(:attributes) do + { + name: 'rspec', + ref: 'master', + inputs: { + string_input: { + type: 'string', + default: 'default value' + }, + number_input: { + type: 'number', + default: 666 + } + } + } + end + + # Remove when the FF `stop_writing_builds_metadata` is removed. + it { is_expected.not_to have_key(:options) } + + it 'assigns inputs to job definition options' do + expect(seed_attributes[:temp_job_definition]).to have_attributes( + config: a_hash_including( + options: a_hash_including( + inputs: { + string_input: { + type: 'string', + default: 'default value' + }, + number_input: { + type: 'number', + default: 666 + } + } + ) + ) + ) + end + + context 'when the FF stop_writing_builds_metadata is disabled' do + before do + stub_feature_flags(stop_writing_builds_metadata: false) + end + + it 'includes inputs in the options hash' do + is_expected.to include( + options: a_hash_including( + inputs: { + string_input: { + type: 'string', + default: 'default value' + }, + number_input: { + type: 'number', + default: 666 + } + } + ) + ) + end + end + end + context 'with cache:key' do let(:attributes) do { diff --git a/spec/models/ci/job_input_spec.rb b/spec/models/ci/job_input_spec.rb index 22352950d4de074246166cab7dac873fcda26bd5..92d0a4e56726de12675013924815bf263076ae3e 100644 --- a/spec/models/ci/job_input_spec.rb +++ b/spec/models/ci/job_input_spec.rb @@ -14,6 +14,14 @@ let!(:parent) { model.project } end + it_behaves_like 'a BulkInsertSafe model', described_class do + let(:valid_items_for_bulk_insertion) do + build_list(:ci_job_input, 10, job: job, project: project) + end + + let(:invalid_items_for_bulk_insertion) { [] } # name and partition_id are NOT NULL at database level + end + describe 'associations' do it { is_expected.to belong_to(:job) } it { is_expected.to belong_to(:project) } diff --git a/spec/presenters/ci/build_runner_presenter_spec.rb b/spec/presenters/ci/build_runner_presenter_spec.rb index 951c367dcf25a0d98d40a8afa65bb3824d293f1f..4275257973d08dcd15fc9cb6cf0d1627caea2c34 100644 --- a/spec/presenters/ci/build_runner_presenter_spec.rb +++ b/spec/presenters/ci/build_runner_presenter_spec.rb @@ -374,19 +374,19 @@ let(:inputs_spec) do { string_input: { - input_type: 'string', + type: 'string', default: 'default value one' }, array_input: { - input_type: 'array', + type: 'array', default: ['default array'] }, boolean_input: { - input_type: 'boolean', + type: 'boolean', default: false }, number_input: { - input_type: 'number', + type: 'number', default: 666 } }