Commit e588a17d authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch 'ali/add-approval-count-to-protected-environment-ui' into 'master'

Add required approvals to Protected Environment settings

See merge request gitlab-org/gitlab!78931
parents 8ba1caeb 65ffaea1
...@@ -53,9 +53,16 @@ Example: ...@@ -53,9 +53,16 @@ Example:
### Require approvals for a protected environment ### Require approvals for a protected environment
NOTE: NOTE:
At this time, only API-based configuration is available. UI-based configuration is planned for the near future. See [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/344675). At this time, it is not possible to require approvals for an existing protected environment. The workaround is to unprotect the environment and configure approvals when re-protecting the environment.
Use the [Protected Environments API](../../api/protected_environments.md#protect-repository-environments) to create an environment with `required_approval_count` > 0. After this is set, all jobs deploying to this environment automatically go into a blocked state and wait for approvals before running. There are two ways to configure approvals for a protected environment:
1. Using the [UI](protected_environments.md#protecting-environments)
1. Set the **Required approvals** field to 1 or more.
1. Using the [REST API](../../api/protected_environments.md#protect-repository-environments)
2. Set the `required_approval_count` field to 1 or more.
After this is configured, all jobs deploying to this environment automatically go into a blocked state and wait for approvals before running. Ensure that the number of required approvals is less than the number of users allowed to deploy.
Example: Example:
...@@ -66,6 +73,7 @@ curl --header 'Content-Type: application/json' --request POST \ ...@@ -66,6 +73,7 @@ curl --header 'Content-Type: application/json' --request POST \
"https://gitlab.example.com/api/v4/projects/22034114/protected_environments" "https://gitlab.example.com/api/v4/projects/22034114/protected_environments"
``` ```
NOTE:
To protect, update, or unprotect an environment, you must have at least the To protect, update, or unprotect an environment, you must have at least the
Maintainer role. Maintainer role.
......
...@@ -38,6 +38,9 @@ To protect an environment: ...@@ -38,6 +38,9 @@ To protect an environment:
- You can select groups that are already associated with the project only. - You can select groups that are already associated with the project only.
- Users must have at least the Developer role to appear in - Users must have at least the Developer role to appear in
the **Allowed to deploy** list. the **Allowed to deploy** list.
1. In the **Required approvals** list, select the number of approvals required to deploy to this environment.
- Ensure that this number is less than the number of users allowed to deploy.
- See [Deployment Approvals](deployment_approvals.md) for more information about this feature.
1. Select **Protect**. 1. Select **Protect**.
The protected environment now appears in the list of protected environments. The protected environment now appears in the list of protected environments.
...@@ -94,7 +97,7 @@ Alternatively, you can use the API to protect an environment: ...@@ -94,7 +97,7 @@ Alternatively, you can use the API to protect an environment:
1. Use the API to add the group with protected environment access: 1. Use the API to add the group with protected environment access:
```shell ```shell
curl --header 'Content-Type: application/json' --request POST --data '{"name": "production", "deploy_access_levels": [{"group_id": 9899826}]}' \ curl --header 'Content-Type: application/json' --request POST --data '{"name": "production", "deploy_access_levels": [{"group_id": 9899826}], "required_approval_count": 0}' \
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.com/api/v4/projects/22034114/protected_environments" --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.com/api/v4/projects/22034114/protected_environments"
``` ```
......
...@@ -8,6 +8,8 @@ import { initAccessDropdown } from '~/projects/settings/init_access_dropdown'; ...@@ -8,6 +8,8 @@ import { initAccessDropdown } from '~/projects/settings/init_access_dropdown';
import { ACCESS_LEVELS } from './constants'; import { ACCESS_LEVELS } from './constants';
const PROTECTED_ENVIRONMENT_INPUT = 'input[name="protected_environment[name]"]'; const PROTECTED_ENVIRONMENT_INPUT = 'input[name="protected_environment[name]"]';
const REQUIRED_APPROVAL_COUNT_INPUT =
'select[name="protected_environment[required_approval_count]"]';
export default class ProtectedEnvironmentCreate { export default class ProtectedEnvironmentCreate {
constructor() { constructor() {
...@@ -82,6 +84,7 @@ export default class ProtectedEnvironmentCreate { ...@@ -82,6 +84,7 @@ export default class ProtectedEnvironmentCreate {
authenticity_token: this.$form.find('input[name="authenticity_token"]').val(), authenticity_token: this.$form.find('input[name="authenticity_token"]').val(),
protected_environment: { protected_environment: {
name: this.$form.find(PROTECTED_ENVIRONMENT_INPUT).val(), name: this.$form.find(PROTECTED_ENVIRONMENT_INPUT).val(),
required_approval_count: this.$form.find(REQUIRED_APPROVAL_COUNT_INPUT).val(),
}, },
}; };
formData.protected_environment[`${ACCESS_LEVELS.DEPLOY}_attributes`] = this.selected; formData.protected_environment[`${ACCESS_LEVELS.DEPLOY}_attributes`] = this.selected;
......
...@@ -8,12 +8,14 @@ ...@@ -8,12 +8,14 @@
%colgroup %colgroup
%col{ width: '30%' } %col{ width: '30%' }
%col %col
%col
- if can_admin_project - if can_admin_project
%col{ width: '10%' } %col{ width: '10%' }
%thead %thead
%tr %tr
%th= s_('ProtectedEnvironment|Protected Environment (%{protected_environments_count})') % { protected_environments_count: limited_counter_with_delimiter(@protected_environments) } %th= s_('ProtectedEnvironment|Protected Environment (%{protected_environments_count})') % { protected_environments_count: limited_counter_with_delimiter(@protected_environments) }
%th= s_('ProtectedEnvironment|Allowed to deploy') %th= s_('ProtectedEnvironment|Allowed to deploy')
%th= s_('ProtectedEnvironment|Required approvals')
- if can_admin_project - if can_admin_project
%th %th
%tbody %tbody
......
...@@ -15,5 +15,11 @@ ...@@ -15,5 +15,11 @@
= s_('ProtectedEnvironment|Allowed to deploy') = s_('ProtectedEnvironment|Allowed to deploy')
.js-allowed-to-deploy-dropdown .js-allowed-to-deploy-dropdown
.form-group
= f.label :required_approval_count, s_('ProtectedEnvironment|Required approvals'), class: 'label-bold gl-display-block'
.dropdown.b-dropdown.gl-new-dropdown.gl-min-w-20.btn-group
= f.select :required_approval_count, (0..5).map { |num| [num.to_s, num] }, {}, class: 'dropdown-menu-toggle select-control'
= sprite_icon('chevron-down', css_class: 'gl-absolute gl-top-3 gl-right-3 gl-text-gray-500')
.card-footer .card-footer
= f.submit s_('ProtectedEnvironment|Protect'), class: 'gl-button btn btn-confirm', disabled: true = f.submit s_('ProtectedEnvironment|Protect'), class: 'gl-button btn btn-confirm', disabled: true
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
%span.ref-name= protected_environment.name %span.ref-name= protected_environment.name
%td %td
= render partial: 'projects/protected_environments/update_deploy_access_level_dropdown', locals: { protected_environment: protected_environment, access_levels: protected_environment.deploy_access_levels, disabled: !can_admin_project } = render partial: 'projects/protected_environments/update_deploy_access_level_dropdown', locals: { protected_environment: protected_environment, access_levels: protected_environment.deploy_access_levels, disabled: !can_admin_project }
%td.gl-min-w-20
= protected_environment.required_approval_count
- if can_admin_project - if can_admin_project
%td %td
......
...@@ -52,10 +52,10 @@ RSpec.describe 'Protected Environments' do ...@@ -52,10 +52,10 @@ RSpec.describe 'Protected Environments' do
end end
it 'allows creating explicit protected environments', :js do it 'allows creating explicit protected environments', :js do
set_protected_environment('staging')
within('.js-new-protected-environment') do within('.js-new-protected-environment') do
set_protected_environment('staging')
set_allowed_to_deploy('Developers + Maintainers') set_allowed_to_deploy('Developers + Maintainers')
set_required_approvals(1)
click_on('Protect') click_on('Protect')
end end
...@@ -63,6 +63,11 @@ RSpec.describe 'Protected Environments' do ...@@ -63,6 +63,11 @@ RSpec.describe 'Protected Environments' do
within('.protected-branches-list') do within('.protected-branches-list') do
expect(page).to have_content('staging') expect(page).to have_content('staging')
within('tr', text: 'staging') do
expect(page).to have_content('Developers + Maintainers')
expect(page).to have_content('1')
end
end end
end end
...@@ -111,11 +116,9 @@ RSpec.describe 'Protected Environments' do ...@@ -111,11 +116,9 @@ RSpec.describe 'Protected Environments' do
end end
def set_protected_environment(environment_name) def set_protected_environment(environment_name)
within('.js-new-protected-environment') do find('.js-protected-environment-select').click
find('.js-protected-environment-select').click find('.dropdown-input-field').set(environment_name)
find('.dropdown-input-field').set(environment_name) find('.is-focused').click
find('.is-focused').click
end
end end
def set_allowed_to_deploy(option) def set_allowed_to_deploy(option)
...@@ -125,4 +128,8 @@ RSpec.describe 'Protected Environments' do ...@@ -125,4 +128,8 @@ RSpec.describe 'Protected Environments' do
Array(option).each { |opt| find('.gl-new-dropdown-item', text: opt).click } Array(option).each { |opt| find('.gl-new-dropdown-item', text: opt).click }
end end
end end
def set_required_approvals(number)
select(number.to_s, from: 'protected_environment_required_approval_count')
end
end end
...@@ -29340,6 +29340,9 @@ msgstr "" ...@@ -29340,6 +29340,9 @@ msgstr ""
msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})" msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
msgstr "" msgstr ""
msgid "ProtectedEnvironment|Required approvals"
msgstr ""
msgid "ProtectedEnvironment|Select an environment" msgid "ProtectedEnvironment|Select an environment"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment