Enforce Custom Stages in Pipeline Execution Policies
<!--This issue template can be used as a great starting point for feature requests. Learn more about the process: https://handbook.gitlab.com/handbook/product/how-to-engage/#customer-feature-requests. The section "Release notes" can be used as a summary of the feature and is also required if you want to have your release post blog MR auto generated using the release post item generator: https://about.gitlab.com/handbook/marketing/blog/release-posts/#release-post-item-generator. The remaining sections are the backbone for every feature in GitLab. The goal of this template is brevity for quick/smaller iterations. For a more thorough list of considerations for larger features or feature sets, you can leverage the detailed [feature proposal](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20proposal%20-%20detailed.md).--> # Release notes We're excited to introduce a new capability for Pipeline Execution Policies (PEP) that allows you to enforce **custom stages** into your CI/CD pipelines. This feature provides greater flexibility and control over your pipeline structure while maintaining security and compliance requirements. #### Benefits * **Enhanced pipeline customization**: Define and inject custom stages at specific points in your pipeline, allowing for more granular control over job execution order. * **Improved security and compliance**: Ensure that security scans and compliance checks run at the most appropriate times in your pipeline, such as after build but before deployment. * **Flexible policy management**: Maintain centralized policy control while allowing development teams to customize their pipelines within defined guardrails. * **Seamless integration**: Custom stages work alongside existing project stages and other policy types, providing a non-disruptive way to enhance your CI/CD workflows. #### How it works The new `inject_policy_ci` strategy for Pipeline Execution Policies allows you to define custom stages in your policy configuration. These stages are then intelligently merged with your project's existing stages using a Directed Acyclic Graph (DAG) algorithm, ensuring proper ordering and preventing conflicts. For example, you can now easily inject a custom security scanning stage between your build and deploy stages: ```yaml stages: [build, policy-test, deploy] policy-security-scan-job: stage: policy-test script: - run_security_script ``` This feature provides a powerful way to standardize and enforce security practices across your projects while maintaining the flexibility needed for diverse development workflows. The `inject_ci` mode will be deprecated, allowing you to opt into the `inject_policy_ci` mode. The `inject_policy_ci` mode will become the default when configuring policies with `Inject` in the policy editor. # Problem to solve <!--What is the user problem you are trying to solve with this issue?--> Currently, Pipeline Execution Policies only allow jobs to be placed in the following two stages: - `.pipeline-policy-pre` which always run at the start of the pipeline. - `.pipeline-policy-post`, which always runs at the end. In the experimental feature [Pipeline execution policy action](https://docs.gitlab.com/17.0/ee/user/application_security/policies/scan-execution-policies.html#pipeline-execution-policy-action) (no longer available), there as an additional stage (`.pipeline-policy-test`) which always ran after the `test` stage, but this behavior was not carried over to `Pipeline Execution Policies`. This limitation can affect the needs of some users (for example, those that practice trunk-based development and can't make use of [MRAP's](https://docs.gitlab.com/ee/user/application_security/policies/scan-result-policies.html)) who require more granular control over when policy-enforced jobs run, especially in relation to critical stages like testing and deployment. ## Example use case Consider a workflow in a trunk-based development project in which a `deploy` job must run _only_ if the Pipeline Execution Policy jobs are successful and find no vulnerabilities: ```mermaid graph LR A[build] --> B[test] --> C[analyze]--> D[deploy] ``` The `deploy` job should only run if the `test` jobs run properly and generate security artifacts which are consumed consumed by the `analyze` job in order to parse any detected vulnerabilities. If any vulnerabilities are found, the `analyze` job fails the pipeline which prevents the vulnerabilities from being deployed. Ideally, the `analyze` job would be enforced via a Pipeline Execution Policy; however, since neither of the two available stages run after the `test` job, they cannot fail the pipeline and prevent the changes to being deployed. ```mermaid graph LR A[.pipeline-policy-pre] --> B[Build] --> C[Test] --> D[Deploy] --> E[.pipeline-policy-post] ``` Notice that previously, the `.pipeline-policy-pre` stage helped achieving just that, but the feature does no longer implement this stage: ```mermaid graph LR A[.pipeline-policy-pre] --> B[Build] --> C[Test] --> D[.pipeline-policy-test] --> E[Deploy] --> F[.pipeline-policy-post] ``` # Proposal Introduce support for "custom stages" that can be placed in the middle of the pipeline by defining stages sequentially. For example, `policy-test` can be placed between `test` and `deploy` in the project pipeline: ``` stages: [test, policy-test, deploy] policy-test-job: stage: policy-test script: - sleep 1 ``` It is possible to specify any possible stages that projects might be using. Only stages with jobs will be present in the resulting pipeline. For example: ``` stages: [build, compile, test, check, policy-test, deploy, publish] policy-test-job: stage: policy-test script: - sleep 1 ``` In this case, `policy-test` will be placed after `build, compile, test, check` and before `deploy, publish`. If project was using `stages: [compile, check, publish]`, the resulting pipeline would contain stages: `[compile, check, policy-test, publish]`. If policy stages only specify the custom stage, this will be placed at the end of the pipeline, just before `.pipeline-policy-post` (if used): ``` stages: [policy-test] policy-test-job: stage: policy-test script: - sleep 1 ``` With default project stages, this results in `[build, test, deploy, policy-test]`. When used in a project without `.gitlab-ci.yml`, the behavior is the same as injecting into default stages. #### Behavior with other policies - It is additive to other policies and the project - When used together with `override_project_ci` policies, it merges into the override stages. #### New strategy Users can specify a new `pipeline_config_strategy` called `inject_policy_ci` which enables custom stages from the policy pipelines to be injected. It is made as a separate strategy so that users can opt-in and avoid breaking changes. `inject_ci` will be deprecated and customers will be encouraged to use the new strategy. #### Possible pipeline errors Depending on the policy stages configuration, the pipeline could fail with this strategy - in case the stages are defined as cyclic. Example: Policy stages: `[test, deploy]` Project stages: `[build, deploy, test]` In this example, `deploy` depends on `test` in the policy, but `test` depends on `deploy` in the project. This configuration results in a "cyclic dependencies" error: ![CleanShot_2025-01-20_at_16.55.20_2x](/uploads/34c4b09bb1e6e074177507e8ba511e60/CleanShot_2025-01-20_at_16.55.20_2x.png){width="585" height="122"} <!--Who will use this feature? If known, include any of the following: types of users (e.g. Developer), personas, or specific company roles (e.g. Release Manager). It's okay to write "Unknown" and fill this field in later. Personas are described at https://handbook.gitlab.com/handbook/product/personas/ * [Parker (Product Manager)](https://handbook.gitlab.com/handbook/product/personas/#parker-product-manager) * [Delaney (Development Team Lead)](https://handbook.gitlab.com/handbook/product/personas/#delaney-development-team-lead) * [Presley (Product Designer)](https://handbook.gitlab.com/handbook/product/personas/#presley-product-designer) * [Sasha (Software Developer)](https://handbook.gitlab.com/handbook/product/personas/#sasha-software-developer) * [Priyanka (Platform Engineer)](https://handbook.gitlab.com/handbook/product/personas/#priyanka-platform-engineer) * [Sidney (Systems Administrator)](https://handbook.gitlab.com/handbook/product/personas/#sidney-systems-administrator) * [Rachel (Release Manager)](https://handbook.gitlab.com/handbook/product/personas/#rachel-release-manager) * [Simone (Software Engineer in Test)](https://handbook.gitlab.com/handbook/product/personas/#simone-software-engineer-in-test) * [Allison (Application Ops)](https://handbook.gitlab.com/handbook/product/personas/#allison-application-ops) * [Ingrid (Infrastructure Operator)](https://handbook.gitlab.com/handbook/product/personas/#ingrid-infrastructure-operator) * [Dakota (Application Development Director)](https://handbook.gitlab.com/handbook/product/personas/#dakota-application-development-director) * [Dana (Data Analyst)](https://handbook.gitlab.com/handbook/product/personas/#dana-data-analyst) * [Eddie (Content Editor)](https://handbook.gitlab.com/handbook/product/personas/#eddie-content-editor) * [Amy (Application Security Engineer)](https://handbook.gitlab.com/handbook/product/personas/#amy-application-security-engineer) * [Isaac (Infrastructure Engineer)](https://handbook.gitlab.com/handbook/product/personas/#isaac-infrastructure-security-engineer) * [Alex (Security Operations Engineer)](https://handbook.gitlab.com/handbook/product/personas/#alex-security-operations-engineer) * [Cameron (Compliance Manager)](https://handbook.gitlab.com/handbook/product/personas/#cameron-compliance-manager)--> ## Verification steps 1. Create a project 2. Create a policy with `pipeline_config_strategy: inject_policy` 3. Verify the various examples mentioned in the documentation: https://docs.gitlab.com/ee/user/application_security/policies/pipeline_execution_policies.html#stages-injection <!--How are you going to track usage of this feature? Think about user behavior and their interaction with the product. What indicates someone is getting value from it? Explore (../../doc/development/internal_analytics/internal_event_instrumentation/quick_start.md) for a guide.--> <!--- Checkout these docs to know more https://docs.gitlab.com/ee/development/audit_event_guide/#what-are-audit-events https://docs.gitlab.com/ee/administration/audit_events.html ---> <!--Label reminders Make sure to add the appropriate labels for the product stage and/or group (e.g ~"devops::plan") if known and add a comment tagging the appropriate Product Manager. Use the following resources to find the appropriate labels: - Use only one tier label choosing the lowest tier this is intended for - https://gitlab.com/gitlab-org/gitlab/-/labels - https://about.gitlab.com/handbook/product/categories/features/ Examples: /label ~group:: ~section:: ~Category: /label ~"GitLab Free" ~"GitLab Premium" ~"GitLab Ultimate"-->
issue