From 0f12ccf7b1b39625d42f29b1e6fcc7e6a8041708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20C=CC=8Cavoj?= Date: Tue, 7 Oct 2025 13:05:24 +0200 Subject: [PATCH 1/3] Persist and surface prefilled variables from PEP Variables defined as a hash with `description` can be prefilled when running pipeline manually. This change collects and persists these variables from pipeline execution policy configs and surfaces them together with the project variables. Changelog: added EE: true --- .../ci/list_config_variables_service.rb | 9 ++- .../policies/pipeline_execution_policies.md | 47 ++++++++++++ ee/app/models/security/policy.rb | 8 ++ .../ee/ci/list_config_variables_service.rb | 25 +++++++ ...ipeline_execution_policy_config_service.rb | 20 ++++- ...eline_execution_policy_metadata_service.rb | 6 +- ...peline_execution_policy_metadata_worker.rb | 6 +- ee/spec/models/security/policy_spec.rb | 47 ++++++++++++ .../ci/list_config_variables_service_spec.rb | 73 +++++++++++++++++++ ...ne_execution_policy_config_service_spec.rb | 70 ++++++++++++++++-- ..._execution_policy_metadata_service_spec.rb | 10 ++- ...e_execution_policy_metadata_worker_spec.rb | 22 ++++-- 12 files changed, 321 insertions(+), 22 deletions(-) create mode 100644 ee/app/services/ee/ci/list_config_variables_service.rb create mode 100644 ee/spec/services/ee/ci/list_config_variables_service_spec.rb diff --git a/app/services/ci/list_config_variables_service.rb b/app/services/ci/list_config_variables_service.rb index 1e40241f2aecf8..3e1c0ce3ff328e 100644 --- a/app/services/ci/list_config_variables_service.rb +++ b/app/services/ci/list_config_variables_service.rb @@ -31,7 +31,7 @@ def calculate_reactive_cache(sha, ref) result = execute_yaml_processor(sha, ref, config) - result.valid? ? result.root_variables_with_prefill_data : {} + result.valid? ? valid_config_result(result) : {} end # Required for ReactiveCaching, it is also used in `reactive_cache_worker_finder` @@ -56,5 +56,12 @@ def execute_yaml_processor(sha, ref, config) verify_project_sha: sha_passed_as_ref_parameter ).execute end + + # Overridden in EE + def valid_config_result(result) + result.root_variables_with_prefill_data + end end end + +Ci::ListConfigVariablesService.prepend_mod diff --git a/doc/user/application_security/policies/pipeline_execution_policies.md b/doc/user/application_security/policies/pipeline_execution_policies.md index 583dbd17dc653f..6119a1ade915b6 100644 --- a/doc/user/application_security/policies/pipeline_execution_policies.md +++ b/doc/user/application_security/policies/pipeline_execution_policies.md @@ -803,6 +803,53 @@ project-job: In this case, the job variable value `Project job variable value` takes precedence. +### Prefill variables in manual pipelines + +{{< history >}} + +- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/527021) in GitLab 18.5. + +{{< /history >}} + +{{< alert type="warning" >}} + +This feature does not work with pipeline execution policies created before GitLab 18.5. +To use this feature with older pipeline execution policies, copy, delete, and recreate the policies. +For more information, see [Recreate pipeline execution policies](#recreate-pipeline-execution-policies). + +{{< /alert >}} + +You can use the [`description`, `value` and `options`](../../../ci/yaml/_index.md#variablesdescription) keywords to [define pipeline-level (global) variables](../../../ci/variables/_index.md#define-a-cicd-variable-in-the-gitlab-ciyml-file) +that are prefilled when running a pipeline manually. Use the description to explain information such as what the variable is used for, and what the acceptable values are. + +Job-level variables cannot be prefilled. + +In manually-triggered pipelines, the **New pipeline** page displays pipeline-level variables from all applicable policies that have a `description` defined in the CI/CD configuration file. + +You must configure the prefilled variables as allowed using [`variables_override`](pipeline_execution_policies.md#variables_override-type), +otherwise the values used when manually triggering the pipelines are ignored. + +For more information about prefilled variables, see [Prefill variables in manual pipelines](../../../ci/pipelines/_index.md#prefill-variables-in-manual-pipelines) + +#### Recreate pipeline execution policies + +To recreate a pipeline execution policy: + + + +1. On the left sidebar, select **Search or go to** and find your group. +1. Select **Secure** > **Policies**. +1. Select the pipeline execution policy you want to recreate. +1. On the right sidebar, select the **YAML** tab and copy the contents of the entire policy file. +1. Next to the policies table, select the vertical ellipsis ({{< icon name="ellipsis_v" >}}), and select **Delete**. +1. Merge the generated merge request. +1. Go back to **Secure** > **Policies** and select **New policy**. +1. In the **Pipeline execution policy** section, select **Select policy**. +1. In the **.yaml mode**, paste the contents of the old policy. +1. Select **Update via merge request** and merge the generated merge request. + + + ## Behavior with `[skip ci]` By default, to prevent a regular pipeline from triggering, users can push a commit to a protected branch with `[skip ci]` in the commit message. However, jobs defined with a pipeline execution policy are always triggered, as the policy ignores the `[skip ci]` directive. This prevents developers from skipping the execution of jobs defined in the policy, which ensures that critical security and compliance checks are always performed. diff --git a/ee/app/models/security/policy.rb b/ee/app/models/security/policy.rb index ead1bdc6ce7899..a9efc0766c2d97 100644 --- a/ee/app/models/security/policy.rb +++ b/ee/app/models/security/policy.rb @@ -343,6 +343,14 @@ def enforced_scans=(scans) metadata['enforced_scans'] = scans end + def prefill_variables + metadata.fetch('prefill_variables', {}) + end + + def prefill_variables=(variables) + metadata['prefill_variables'] = variables + end + def bypass_settings Security::ScanResultPolicies::BypassSettings.new(policy_content[:bypass_settings]) end diff --git a/ee/app/services/ee/ci/list_config_variables_service.rb b/ee/app/services/ee/ci/list_config_variables_service.rb new file mode 100644 index 00000000000000..ea7483fce4c78b --- /dev/null +++ b/ee/app/services/ee/ci/list_config_variables_service.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module EE + module Ci + module ListConfigVariablesService + extend ::Gitlab::Utils::Override + + private + + override :valid_config_result + def valid_config_result(result) + merge_policy_variables(super) + end + + def merge_policy_variables(config_variables) + policy_prefill_variables = project.security_policies.type_pipeline_execution_policy + .each_with_object({}) do |policy, result| + result.merge!(policy.prefill_variables.transform_values(&:symbolize_keys)) + end + + config_variables.merge(policy_prefill_variables) + end + end + end +end diff --git a/ee/app/services/security/security_orchestration_policies/analyze_pipeline_execution_policy_config_service.rb b/ee/app/services/security/security_orchestration_policies/analyze_pipeline_execution_policy_config_service.rb index 4246e7ff8019d5..9916f8b96c30eb 100644 --- a/ee/app/services/security/security_orchestration_policies/analyze_pipeline_execution_policy_config_service.rb +++ b/ee/app/services/security/security_orchestration_policies/analyze_pipeline_execution_policy_config_service.rb @@ -10,19 +10,26 @@ def execute unless config.valid? return ServiceResponse.error( message: "Error occurred while parsing the CI configuration: #{config.errors}", - payload: [] + payload: build_payload ) end - analyzers_config = extract_analyzers_from_config(config.to_hash) + config_hash = config.to_hash + analyzers_config = extract_analyzers_from_config(config_hash) + scans = analyzers_config.keys & Security::MergeRequestSecurityReportGenerationService::ALLOWED_REPORT_TYPES - ServiceResponse.success(payload: scans) + variables = parse_prefill_variables(config_hash) + ServiceResponse.success(payload: build_payload(scans: scans, variables: variables)) rescue StandardError => e - ServiceResponse.error(message: e.message, payload: []) + ServiceResponse.error(message: e.message, payload: build_payload) end private + def build_payload(scans: [], variables: {}) + { enforced_scans: scans, prefill_variables: variables } + end + def extract_analyzers_from_config(config) artifact_reports = config.select { |_key, entry| entry.is_a?(Hash) && entry[:artifacts].present? } .map { |_key, entry| entry.dig(:artifacts, :reports) } @@ -34,6 +41,11 @@ def extract_analyzers_from_config(config) end end.with_indifferent_access end + + def parse_prefill_variables(config) + variables = config.fetch(:variables, {}).with_indifferent_access + variables.select { |_key, value| value.is_a?(Hash) && value[:description] } + end end end end diff --git a/ee/app/services/security/security_orchestration_policies/update_pipeline_execution_policy_metadata_service.rb b/ee/app/services/security/security_orchestration_policies/update_pipeline_execution_policy_metadata_service.rb index 0ee94a994d9d82..2c492fd2ebc5b0 100644 --- a/ee/app/services/security/security_orchestration_policies/update_pipeline_execution_policy_metadata_service.rb +++ b/ee/app/services/security/security_orchestration_policies/update_pipeline_execution_policy_metadata_service.rb @@ -3,15 +3,17 @@ module Security module SecurityOrchestrationPolicies class UpdatePipelineExecutionPolicyMetadataService - def initialize(security_policy:, enforced_scans:) + def initialize(security_policy:, enforced_scans:, prefill_variables:) @security_policy = security_policy @enforced_scans = enforced_scans + @prefill_variables = prefill_variables end def execute return ServiceResponse.success(payload: security_policy) unless security_policy.type_pipeline_execution_policy? security_policy.enforced_scans = enforced_scans + security_policy.prefill_variables = prefill_variables security_policy.save! ServiceResponse.success(payload: security_policy) rescue StandardError => e @@ -20,7 +22,7 @@ def execute private - attr_accessor :security_policy, :enforced_scans + attr_accessor :security_policy, :enforced_scans, :prefill_variables end end end diff --git a/ee/app/workers/security/sync_pipeline_execution_policy_metadata_worker.rb b/ee/app/workers/security/sync_pipeline_execution_policy_metadata_worker.rb index 028427d23ae03d..e1ae4c3df43bbf 100644 --- a/ee/app/workers/security/sync_pipeline_execution_policy_metadata_worker.rb +++ b/ee/app/workers/security/sync_pipeline_execution_policy_metadata_worker.rb @@ -29,7 +29,11 @@ def perform(config_project_id, user_id, content, security_policy_ids) Security::Policy.id_in(security_policy_ids).find_each do |policy| Security::SecurityOrchestrationPolicies::UpdatePipelineExecutionPolicyMetadataService - .new(security_policy: policy, enforced_scans: result.payload).execute + .new( + security_policy: policy, + enforced_scans: result.payload[:enforced_scans], + prefill_variables: result.payload[:prefill_variables] + ).execute end end end diff --git a/ee/spec/models/security/policy_spec.rb b/ee/spec/models/security/policy_spec.rb index 77582a81625a31..63bd5aab4a3fe0 100644 --- a/ee/spec/models/security/policy_spec.rb +++ b/ee/spec/models/security/policy_spec.rb @@ -1177,6 +1177,53 @@ end end + describe '#prefill_variables' do + subject(:prefill_variables) { policy.prefill_variables } + + let(:policy) { build(:security_policy, :pipeline_execution_policy, metadata: metadata) } + let(:metadata) { { prefill_variables: { 'VAR' => { value: 'value', description: 'description' } } } } + + it { is_expected.to eq('VAR' => { 'value' => 'value', 'description' => 'description' }) } + + context 'when metadata is empty' do + let(:metadata) { {} } + + it { is_expected.to eq({}) } + end + + context 'when metadata does not contain prefill_variables' do + let(:metadata) { { other: 'property' } } + + it { is_expected.to eq({}) } + end + end + + describe '#prefill_variables=' do + let(:policy) { build(:security_policy, :pipeline_execution_policy, metadata: metadata) } + let(:metadata) { {} } + + it 'updates metadata' do + policy.prefill_variables = { 'VAR' => { value: 'value', description: 'description' } } + + expect(policy.metadata).to eq( + 'prefill_variables' => { 'VAR' => { value: 'value', description: 'description' } } + ) + end + + context 'when metadata contains other properties' do + let(:metadata) { { other: 'property' } } + + it 'updates extends metadata and keeps the other property' do + policy.prefill_variables = { 'VAR' => { value: 'value', description: 'description' } } + + expect(policy.metadata).to eq( + 'prefill_variables' => { 'VAR' => { value: 'value', description: 'description' } }, + 'other' => 'property' + ) + end + end + end + describe '#framework_ids_from_scope' do let_it_be(:policy) { build(:security_policy) } diff --git a/ee/spec/services/ee/ci/list_config_variables_service_spec.rb b/ee/spec/services/ee/ci/list_config_variables_service_spec.rb new file mode 100644 index 00000000000000..56fefddce4db21 --- /dev/null +++ b/ee/spec/services/ee/ci/list_config_variables_service_spec.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::ListConfigVariablesService, + :use_clean_rails_memory_store_caching, feature_category: :pipeline_composition do + include ReactiveCachingHelpers + + let_it_be(:ci_config) do + { + variables: { + KEY1: { value: 'val 1', description: 'description 1' }, + KEY2: { value: 'val 2', description: '' }, + KEY3: { value: 'val 3' }, + KEY4: 'val 4' + }, + test: { + stage: 'test', + script: 'echo' + } + } + end + + let_it_be(:files) { { '.gitlab-ci.yml' => YAML.dump(ci_config) } } + let_it_be(:project) { create(:project, :custom_repo, :auto_devops_disabled, files: files) } + let(:user) { project.creator } + let(:ref) { project.default_branch } + let(:service) { described_class.new(project, user) } + + subject(:result) { service.execute(ref) } + + before do + synchronous_reactive_cache(service) + end + + describe 'variables from pipeline execution policies' do + let(:expected_result) do + { + 'KEY1' => { value: 'val 1', description: 'description 1' }, + 'KEY2' => { value: 'val 2', description: '' }, + 'KEY3' => { value: 'val 3' }, + 'KEY4' => { value: 'val 4' }, + 'PEP_KEY1' => { value: 'pep val 1', description: 'pep description 1' }, + 'PEP_KEY2' => { value: 'pep val 2', description: 'pep description 2' } + } + end + + let(:policy1_variables) do + { + 'PEP_KEY1' => { value: 'pep val 1', description: 'pep description 1' } + } + end + + let(:policy2_variables) do + { + 'PEP_KEY2' => { value: 'pep val 2', description: 'pep description 2' } + } + end + + before do + create(:security_policy, :pipeline_execution_policy, + linked_projects: [project], + metadata: { prefill_variables: policy1_variables }) + create(:security_policy, :pipeline_execution_policy, + linked_projects: [project], + metadata: { prefill_variables: policy2_variables }) + end + + it 'returns a combined variables list' do + expect(result).to eq(expected_result) + end + end +end diff --git a/ee/spec/services/security/security_orchestration_policies/analyze_pipeline_execution_policy_config_service_spec.rb b/ee/spec/services/security/security_orchestration_policies/analyze_pipeline_execution_policy_config_service_spec.rb index 581bb91a9f316e..b01a353b10fb9c 100644 --- a/ee/spec/services/security/security_orchestration_policies/analyze_pipeline_execution_policy_config_service_spec.rb +++ b/ee/spec/services/security/security_orchestration_policies/analyze_pipeline_execution_policy_config_service_spec.rb @@ -8,6 +8,7 @@ let_it_be(:user) { create(:user) } let(:content) do { + variables: content_variables, include: [ { template: 'Jobs/Secret-Detection.gitlab-ci.yml' }, { template: 'Jobs/Dependency-Scanning.gitlab-ci.yml' } @@ -15,12 +16,69 @@ } end + let(:content_variables) do + { + 'ENVIRONMENT' => { value: 'staging', description: 'Value for environment.' } + } + end + describe '#execute' do subject(:execute) { service.execute } it 'extracts scanners by the declared artifacts:reports' do expect(execute).to be_success - expect(execute.payload).to eq(%w[secret_detection dependency_scanning]) + expect(execute.payload).to eq( + enforced_scans: %w[secret_detection dependency_scanning], + prefill_variables: content[:variables].deep_stringify_keys + ) + end + + describe 'prefill variables' do + context 'when variables have mixed formats' do + let(:content_variables) do + { + 'VAR1' => { value: 'value1', description: 'var 1' }, + 'VAR2' => { description: 'var 2' }, + 'VAR3' => { value: 'value3', options: %w[value2 value3], description: 'var 3' }, + 'VAR4' => { value: 'value4' }, + 'VAR5' => 'value5' + } + end + + it 'only extracts variables with description' do + expect(execute.payload).to match a_hash_including(prefill_variables: { + 'VAR1' => { value: 'value1', description: 'var 1' }, + 'VAR2' => { description: 'var 2' }, + 'VAR3' => { value: 'value3', options: %w[value2 value3], description: 'var 3' } + }) + end + end + + context 'when variables are not prefill variables' do + context 'when variables do not declare description' do + let(:content_variables) do + { + 'ENVIRONMENT' => { value: 'value' } + } + end + + it 'does not extract the variables' do + expect(execute.payload).to match a_hash_including(prefill_variables: {}) + end + end + + context 'when variables are not a hash' do + let(:content_variables) do + { + 'ENVIRONMENT' => 'value' + } + end + + it 'does not extract the variables' do + expect(execute.payload).to match a_hash_including(prefill_variables: {}) + end + end + end end context 'when a job with artifacts is declared manually' do @@ -40,7 +98,7 @@ it 'extracts scanners by the declared artifacts:reports' do expect(execute).to be_success - expect(execute.payload).to eq(%w[secret_detection]) + expect(execute.payload).to match(a_hash_including(enforced_scans: %w[secret_detection])) end end @@ -63,7 +121,7 @@ it 'extracts scanners without duplicates' do expect(execute).to be_success - expect(execute.payload).to eq(%w[secret_detection]) + expect(execute.payload).to match(a_hash_including(enforced_scans: %w[secret_detection])) end end @@ -83,7 +141,7 @@ it 'returns an empty array' do expect(execute).to be_success - expect(execute.payload).to be_empty + expect(execute.payload).to eq(enforced_scans: [], prefill_variables: {}) end end @@ -99,7 +157,7 @@ 'Error occurred while parsing the CI configuration', 'jobs config should contain at least one visible job' ) - expect(execute.payload).to be_empty + expect(execute.payload).to eq(enforced_scans: [], prefill_variables: {}) end end @@ -113,7 +171,7 @@ it 'returns an error' do expect(execute).to be_error expect(execute.message).to include('Project `invalid` not found or access denied!') - expect(execute.payload).to be_empty + expect(execute.payload).to eq(enforced_scans: [], prefill_variables: {}) end end end diff --git a/ee/spec/services/security/security_orchestration_policies/update_pipeline_execution_policy_metadata_service_spec.rb b/ee/spec/services/security/security_orchestration_policies/update_pipeline_execution_policy_metadata_service_spec.rb index 22635a799e92ac..c38171332a99e5 100644 --- a/ee/spec/services/security/security_orchestration_policies/update_pipeline_execution_policy_metadata_service_spec.rb +++ b/ee/spec/services/security/security_orchestration_policies/update_pipeline_execution_policy_metadata_service_spec.rb @@ -3,7 +3,10 @@ require 'spec_helper' RSpec.describe Security::SecurityOrchestrationPolicies::UpdatePipelineExecutionPolicyMetadataService, feature_category: :security_policy_management do - let(:service) { described_class.new(security_policy: policy, enforced_scans: enforced_scans) } + let(:service) do + described_class.new(security_policy: policy, enforced_scans: enforced_scans, prefill_variables: prefill_variables) + end + let_it_be(:project) { create(:project) } let_it_be(:configuration) { create(:security_orchestration_policy_configuration, project: project) } let(:policy) do @@ -14,7 +17,10 @@ subject(:execute) { service.execute } let(:enforced_scans) { %w[sast] } - let(:expected_metadata) { { 'enforced_scans' => enforced_scans } } + let(:prefill_variables) { { 'ENVIRONMENT' => { description: 'Policy environment' } } } + let(:expected_metadata) do + { 'enforced_scans' => enforced_scans, 'prefill_variables' => prefill_variables.deep_stringify_keys } + end it 'updates the metadata' do expect(execute).to be_success diff --git a/ee/spec/workers/security/sync_pipeline_execution_policy_metadata_worker_spec.rb b/ee/spec/workers/security/sync_pipeline_execution_policy_metadata_worker_spec.rb index 7b5c06008dfaa7..8bd3ec401c69e3 100644 --- a/ee/spec/workers/security/sync_pipeline_execution_policy_metadata_worker_spec.rb +++ b/ee/spec/workers/security/sync_pipeline_execution_policy_metadata_worker_spec.rb @@ -65,14 +65,17 @@ expect(policy.reload.metadata).to match("enforced_scans" => an_array_matching( %w[secret_detection dependency_scanning container_scanning sast] - )) + ), "prefill_variables" => {}) end it 'calls AnalyzePipelineExecutionPolicyConfigService and UpdatePipelineExecutionPolicyMetadataService' do expect_next_instance_of( Security::SecurityOrchestrationPolicies::AnalyzePipelineExecutionPolicyConfigService ) do |service| - expect(service).to receive(:execute).and_return(ServiceResponse.success(payload: %w[sast])) + expect(service).to receive(:execute).and_return( + ServiceResponse.success(payload: { enforced_scans: %w[sast], + prefill_variables: { 'VAR' => { description: 'desc' } } }) + ) end update_metadata_service = instance_double( @@ -80,7 +83,11 @@ execute: ServiceResponse.success ) expect(Security::SecurityOrchestrationPolicies::UpdatePipelineExecutionPolicyMetadataService) - .to receive(:new).with(security_policy: policy, enforced_scans: %w[sast]).and_return(update_metadata_service) + .to receive(:new).with( + security_policy: policy, + enforced_scans: %w[sast], + prefill_variables: { 'VAR' => { description: 'desc' } } + ).and_return(update_metadata_service) expect(update_metadata_service).to receive(:execute) perform @@ -91,7 +98,10 @@ allow_next_instance_of( Security::SecurityOrchestrationPolicies::AnalyzePipelineExecutionPolicyConfigService ) do |service| - allow(service).to receive(:execute).and_return(ServiceResponse.error(message: 'error', payload: [])) + allow(service) + .to receive(:execute).and_return( + ServiceResponse.error(message: 'error', payload: { enforced_scans: [], prefill_variables: {} }) + ) end end @@ -108,7 +118,7 @@ it 'persists empty array as metadata' do perform - expect(policy.reload.metadata).to eq("enforced_scans" => []) + expect(policy.reload.metadata).to eq("enforced_scans" => [], "prefill_variables" => {}) end end end @@ -117,7 +127,7 @@ it 'persists empty array as metadata' do perform - expect(policy.reload.metadata).to eq("enforced_scans" => []) + expect(policy.reload.metadata).to eq("enforced_scans" => [], "prefill_variables" => {}) end it 'logs the error' do -- GitLab From d61506dd49ef45f66ba54090a1201da8dd82e9cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20=C4=8Cavoj?= Date: Thu, 9 Oct 2025 09:56:03 +0200 Subject: [PATCH 2/3] Apply 7 suggestion(s) to 1 file(s) Co-authored-by: Ryan Lehmann --- .../policies/pipeline_execution_policies.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/user/application_security/policies/pipeline_execution_policies.md b/doc/user/application_security/policies/pipeline_execution_policies.md index 6119a1ade915b6..eb7a9fada9fa09 100644 --- a/doc/user/application_security/policies/pipeline_execution_policies.md +++ b/doc/user/application_security/policies/pipeline_execution_policies.md @@ -803,7 +803,7 @@ project-job: In this case, the job variable value `Project job variable value` takes precedence. -### Prefill variables in manual pipelines +### Prefill variables in manually run pipelines {{< history >}} @@ -815,21 +815,21 @@ In this case, the job variable value `Project job variable value` takes preceden This feature does not work with pipeline execution policies created before GitLab 18.5. To use this feature with older pipeline execution policies, copy, delete, and recreate the policies. -For more information, see [Recreate pipeline execution policies](#recreate-pipeline-execution-policies). +For more information, see [recreate pipeline execution policies](#recreate-pipeline-execution-policies). {{< /alert >}} -You can use the [`description`, `value` and `options`](../../../ci/yaml/_index.md#variablesdescription) keywords to [define pipeline-level (global) variables](../../../ci/variables/_index.md#define-a-cicd-variable-in-the-gitlab-ciyml-file) -that are prefilled when running a pipeline manually. Use the description to explain information such as what the variable is used for, and what the acceptable values are. +You can use the [`description`, `value` and `options`](../../../ci/yaml/_index.md#variablesdescription) keywords to [define global pipeline CI/CD variables](../../../ci/variables/_index.md#define-a-cicd-variable-in-the-gitlab-ciyml-file) +that are prefilled when a user runs a pipeline manually. Use the description to provide relevant information, such as what the variable is used for and what the acceptable values are. -Job-level variables cannot be prefilled. +You cannot prefill job-specific variables. -In manually-triggered pipelines, the **New pipeline** page displays pipeline-level variables from all applicable policies that have a `description` defined in the CI/CD configuration file. +In manually-triggered pipelines, the **New pipeline** page displays pipeline variables from all applicable policies that have a `description` defined in the CI/CD configuration file. You must configure the prefilled variables as allowed using [`variables_override`](pipeline_execution_policies.md#variables_override-type), otherwise the values used when manually triggering the pipelines are ignored. -For more information about prefilled variables, see [Prefill variables in manual pipelines](../../../ci/pipelines/_index.md#prefill-variables-in-manual-pipelines) +For more information about prefilled variables, see [prefill variables in manual pipelines](../../../ci/pipelines/_index.md#prefill-variables-in-manual-pipelines) #### Recreate pipeline execution policies -- GitLab From 3e5147e50940da50ffd244fdc0d53b6a8992b98c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20=C4=8Cavoj?= Date: Thu, 9 Oct 2025 19:35:09 +0200 Subject: [PATCH 3/3] Apply 4 suggestion(s) to 1 file(s) Co-authored-by: Marcel Amirault --- .../policies/pipeline_execution_policies.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/doc/user/application_security/policies/pipeline_execution_policies.md b/doc/user/application_security/policies/pipeline_execution_policies.md index eb7a9fada9fa09..f80fdd91d29a86 100644 --- a/doc/user/application_security/policies/pipeline_execution_policies.md +++ b/doc/user/application_security/policies/pipeline_execution_policies.md @@ -803,7 +803,7 @@ project-job: In this case, the job variable value `Project job variable value` takes precedence. -### Prefill variables in manually run pipelines +### Prefill variables in manually-run pipelines {{< history >}} @@ -819,18 +819,17 @@ For more information, see [recreate pipeline execution policies](#recreate-pipel {{< /alert >}} -You can use the [`description`, `value` and `options`](../../../ci/yaml/_index.md#variablesdescription) keywords to [define global pipeline CI/CD variables](../../../ci/variables/_index.md#define-a-cicd-variable-in-the-gitlab-ciyml-file) -that are prefilled when a user runs a pipeline manually. Use the description to provide relevant information, such as what the variable is used for and what the acceptable values are. +You can use the `description`, `value` and `options` keywords to define CI/CD variables +that are [prefilled when a user runs a pipeline manually](../../../ci/pipelines/_index.md#prefill-variables-in-manual-pipelines). +Use the description to provide relevant information, such as what the variable is used for and what the acceptable values are. You cannot prefill job-specific variables. -In manually-triggered pipelines, the **New pipeline** page displays pipeline variables from all applicable policies that have a `description` defined in the CI/CD configuration file. +In manually-triggered pipelines, the **New pipeline** page displays all pipeline variables that have a `description` defined in the CI/CD configuration, from all applicable policies. You must configure the prefilled variables as allowed using [`variables_override`](pipeline_execution_policies.md#variables_override-type), otherwise the values used when manually triggering the pipelines are ignored. -For more information about prefilled variables, see [prefill variables in manual pipelines](../../../ci/pipelines/_index.md#prefill-variables-in-manual-pipelines) - #### Recreate pipeline execution policies To recreate a pipeline execution policy: -- GitLab