Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3034c7e6aa
commit
19044caf66
|
|
@ -4,7 +4,7 @@ include:
|
|||
- local: .gitlab/ci/global.gitlab-ci.yml
|
||||
- local: .gitlab/ci/package-and-test/rules.gitlab-ci.yml
|
||||
- project: gitlab-org/quality/pipeline-common
|
||||
ref: 1.2.0
|
||||
ref: 1.2.1
|
||||
file:
|
||||
- /ci/base.gitlab-ci.yml
|
||||
- /ci/allure-report.yml
|
||||
|
|
@ -448,23 +448,6 @@ allure-report:
|
|||
ALLURE_MERGE_REQUEST_IID: $CI_MERGE_REQUEST_IID
|
||||
ALLURE_JOB_NAME: e2e-package-and-test
|
||||
|
||||
notify-slack:
|
||||
extends:
|
||||
- .notify-slack-qa
|
||||
- .ruby-image
|
||||
- .bundle-install
|
||||
- .rules:report:process-results
|
||||
stage: .post
|
||||
variables:
|
||||
ALLURE_JOB_NAME: package-and-e2e
|
||||
SLACK_ICON_EMOJI: ci_failing
|
||||
STATUS_SYM: ☠️
|
||||
STATUS: failed
|
||||
when: on_failure
|
||||
script:
|
||||
- bundle exec gitlab-qa-report --prepare-stage-reports "$CI_PROJECT_DIR/gitlab-qa-run-*/**/rspec-*.xml" # generate summary
|
||||
- !reference [.notify-slack-qa, script]
|
||||
|
||||
upload-knapsack-report:
|
||||
extends:
|
||||
- .generate-knapsack-report-base
|
||||
|
|
@ -488,7 +471,7 @@ relate-test-failures:
|
|||
script:
|
||||
- |
|
||||
bundle exec gitlab-qa-report \
|
||||
--relate-failure-issue "gitlab-qa-run-*/**/rspec-*.json" \
|
||||
--relate-failure-issue "$CI_PROJECT_DIR/gitlab-qa-run-*/**/rspec-*.json" \
|
||||
--project "$QA_FAILURES_REPORTING_PROJECT" \
|
||||
--max-diff-ratio "$QA_FAILURES_MAX_DIFF_RATIO"
|
||||
|
||||
|
|
@ -505,5 +488,29 @@ generate-test-session:
|
|||
script:
|
||||
- |
|
||||
bundle exec gitlab-qa-report \
|
||||
--generate-test-session "gitlab-qa-run-*/**/rspec-*.json" \
|
||||
--generate-test-session "$CI_PROJECT_DIR/gitlab-qa-run-*/**/rspec-*.json" \
|
||||
--project "$QA_TESTCASE_SESSIONS_PROJECT"
|
||||
artifacts:
|
||||
when: always
|
||||
expire_in: 1d
|
||||
paths:
|
||||
- qa/REPORT_ISSUE_URL
|
||||
|
||||
notify-slack:
|
||||
extends:
|
||||
- .notify-slack-qa
|
||||
- .ruby-image
|
||||
- .bundle-install
|
||||
- .rules:report:process-results
|
||||
stage: .post
|
||||
needs:
|
||||
- generate-test-session
|
||||
variables:
|
||||
ALLURE_JOB_NAME: e2e-package-and-test
|
||||
SLACK_ICON_EMOJI: ci_failing
|
||||
STATUS_SYM: ☠️
|
||||
STATUS: failed
|
||||
when: on_failure
|
||||
script:
|
||||
- bundle exec gitlab-qa-report --prepare-stage-reports "$CI_PROJECT_DIR/gitlab-qa-run-*/**/rspec-*.xml" # generate summary
|
||||
- !reference [.notify-slack-qa, script]
|
||||
|
|
|
|||
|
|
@ -968,6 +968,7 @@
|
|||
variables:
|
||||
SKIP_REPORT_IN_ISSUES: "false"
|
||||
PROCESS_TEST_RESULTS: "true"
|
||||
KNAPSACK_GENERATE_REPORT: "true"
|
||||
- <<: *if-force-ci
|
||||
when: manual
|
||||
allow_failure: true
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ Style/FormatString:
|
|||
- 'app/helpers/members_helper.rb'
|
||||
- 'app/helpers/merge_requests_helper.rb'
|
||||
- 'app/helpers/mirror_helper.rb'
|
||||
- 'app/helpers/notify_helper.rb'
|
||||
- 'app/helpers/preferences_helper.rb'
|
||||
- 'app/helpers/profiles_helper.rb'
|
||||
- 'app/helpers/projects/project_members_helper.rb'
|
||||
|
|
|
|||
|
|
@ -82,8 +82,11 @@ export default {
|
|||
|
||||
this.autosave.reset();
|
||||
|
||||
if (window.mrTabs && this.note) {
|
||||
window.location.hash = `note_${this.getCurrentUserLastNote.id}`;
|
||||
if (window.mrTabs && (this.noteData.note || this.noteData.approve)) {
|
||||
if (this.noteData.note) {
|
||||
window.location.hash = `note_${this.getCurrentUserLastNote.id}`;
|
||||
}
|
||||
|
||||
window.mrTabs.tabShown('show');
|
||||
|
||||
setTimeout(() =>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,14 @@ module NotifyHelper
|
|||
end
|
||||
|
||||
def merge_request_approved_description(merge_request, approved_by)
|
||||
format(s_('Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}').html_safe, mr_highlight: '<span style="font-weight: 600;color:#333333;">'.html_safe, highlight_end: '</span>'.html_safe, mr_link: link_to(merge_request.to_reference, merge_request_url(merge_request), style: "font-weight: 600;color:#3777b0;text-decoration:none").html_safe, approved_highlight: '<span>'.html_safe, approver_avatar: content_tag(:img, nil, height: "24", src: avatar_icon_for_user(approved_by, 24, only_path: false),
|
||||
style: "border-radius:12px;margin:-7px 0 -7px 3px;", width: "24", alt: "Avatar", class: "avatar").html_safe, approver_link: link_to(approved_by.name, user_url(approved_by), style: "color:#333333;text-decoration:none;", class: "muted").html_safe)
|
||||
s_('Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}')
|
||||
.html_safe % {
|
||||
mr_highlight: '<span style="font-weight: 600;color:#333333;">'.html_safe,
|
||||
highlight_end: '</span>'.html_safe,
|
||||
mr_link: link_to(merge_request.to_reference, merge_request_url(merge_request), style: "font-weight: 600;color:#3777b0;text-decoration:none").html_safe,
|
||||
approved_highlight: '<span>'.html_safe,
|
||||
approver_avatar: content_tag(:img, nil, height: "24", src: avatar_icon_for_user(approved_by, 24, only_path: false), style: "border-radius:12px;margin:-7px 0 -7px 3px;", width: "24", alt: "Avatar", class: "avatar").html_safe,
|
||||
approver_link: link_to(approved_by.name, user_url(approved_by), style: "color:#333333;text-decoration:none;", class: "muted").html_safe
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,8 +10,17 @@ module Ci
|
|||
STORE_COLUMN = :file_store
|
||||
NotSupportedAdapterError = Class.new(StandardError)
|
||||
FILE_FORMAT_ADAPTERS = {
|
||||
# While zip is a streamable file format, performing streaming
|
||||
# reads requires that each entry in the zip has certain headers
|
||||
# present at the front of the entry. These headers are OPTIONAL
|
||||
# according to the file format specification. GitLab Runner uses
|
||||
# Go's `archive/zip` to create zip archives, which does not include
|
||||
# these headers. Go maintainers have expressed that they don't intend
|
||||
# to support them: https://github.com/golang/go/issues/23301#issuecomment-363240781
|
||||
#
|
||||
# If you need GitLab to be able to read Artifactables, store them in
|
||||
# raw or gzip format instead of zip.
|
||||
gzip: Gitlab::Ci::Build::Artifacts::Adapters::GzipStream,
|
||||
zip: Gitlab::Ci::Build::Artifacts::Adapters::ZipStream,
|
||||
raw: Gitlab::Ci::Build::Artifacts::Adapters::RawStream
|
||||
}.freeze
|
||||
|
||||
|
|
|
|||
|
|
@ -35,12 +35,14 @@ module Users
|
|||
return user
|
||||
end
|
||||
|
||||
# Calling all before/after_destroy hooks for the user because
|
||||
# there is no dependent: destroy in the relationship. And the removal
|
||||
# is done by a foreign_key. Otherwise they won't be called
|
||||
user.members.find_each { |member| member.run_callbacks(:destroy) }
|
||||
user.block
|
||||
|
||||
user.solo_owned_groups.each do |group|
|
||||
# Load the records. Groups are unavailable after membership is destroyed.
|
||||
solo_owned_groups = user.solo_owned_groups.load
|
||||
|
||||
user.members.each_batch { |batch| batch.destroy_all } # rubocop:disable Style/SymbolProc, Cop/DestroyAll
|
||||
|
||||
solo_owned_groups.each do |group|
|
||||
Groups::DestroyService.new(group, current_user).execute
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
%p
|
||||
You have been mentioned in an issue.
|
||||
= s_('Notify|You have been mentioned in an issue.')
|
||||
|
||||
= render template: 'notify/new_issue_email'
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
- project_buttons = local_assigns.fetch(:project_buttons, false)
|
||||
|
||||
- return unless anchors.any?
|
||||
%ul.nav.gl-gap-3
|
||||
%ul.nav{ class: (project_buttons ? 'gl-gap-3' : 'gl-gap-5') }
|
||||
- anchors.each do |anchor|
|
||||
%li.nav-item
|
||||
= link_to_if(anchor.link, anchor.label, anchor.link, stat_anchor_attrs(anchor)) do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
config_files.add_suggestion_for_missing_introduced_by_url
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../tooling/danger/config_files'
|
||||
|
||||
module Danger
|
||||
class ConfigFiles < ::Danger::Plugin
|
||||
# Put the helper code somewhere it can be tested
|
||||
include Tooling::Danger::ConfigFiles
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DropTemporaryJobTraceIndex < Gitlab::Database::Migration[2.0]
|
||||
INDEX_NAME = 'tmp_index_ci_job_artifacts_on_id_where_trace_and_expire_at'
|
||||
|
||||
def up
|
||||
prepare_async_index_removal :ci_job_artifacts, :id, name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
unprepare_async_index_by_name :ci_job_artifacts, INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
75cb9d7b4a0bc8ad26b3bf6bf41a4414bcc4307607de058fc35fe4ece7009423
|
||||
|
|
@ -48,13 +48,14 @@ Example response:
|
|||
"name":"production",
|
||||
"deploy_access_levels":[
|
||||
{
|
||||
"id": 12,
|
||||
"access_level": 40,
|
||||
"access_level_description": "Maintainers",
|
||||
"user_id": null,
|
||||
"group_id": null
|
||||
}
|
||||
],
|
||||
"required_approval_count": 0
|
||||
"required_approval_count": 0
|
||||
}
|
||||
]
|
||||
```
|
||||
|
|
@ -83,6 +84,7 @@ Example response:
|
|||
"name":"production",
|
||||
"deploy_access_levels":[
|
||||
{
|
||||
"id": 12,
|
||||
"access_level":40,
|
||||
"access_level_description":"Maintainers",
|
||||
"user_id":null,
|
||||
|
|
@ -123,13 +125,182 @@ Example response:
|
|||
"name":"production",
|
||||
"deploy_access_levels":[
|
||||
{
|
||||
"id": 12,
|
||||
"access_level": 40,
|
||||
"access_level_description": "protected-access-group",
|
||||
"user_id": null,
|
||||
"group_id": 9899826
|
||||
}
|
||||
],
|
||||
"required_approval_count": 0
|
||||
"required_approval_count": 0
|
||||
}
|
||||
```
|
||||
|
||||
## Update an environment
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351854) in GitLab 15.4.
|
||||
|
||||
Updates a single environment.
|
||||
|
||||
```plaintext
|
||||
PUT /groups/:id/protected_environments/:name
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) maintained by the authenticated user. |
|
||||
| `name` | string | yes | The deployment tier of the protected environment. One of `production`, `staging`, `testing`, `development`, or `other`. Read more about [deployment tiers](../ci/environments/index.md#deployment-tier-of-environments).|
|
||||
| `deploy_access_levels` | array | no | Array of access levels allowed to deploy, with each described by a hash. One of `user_id`, `group_id` or `access_level`. They take the form of `{user_id: integer}`, `{group_id: integer}` or `{access_level: integer}` respectively. |
|
||||
| `required_approval_count` | integer | no | The number of approvals required to deploy to this environment. |
|
||||
| `approval_rules` | array | no | Array of access levels allowed to approve, with each described by a hash. One of `user_id`, `group_id` or `access_level`. They take the form of `{user_id: integer}`, `{group_id: integer}` or `{access_level: integer}` respectively. You can also specify the number of required approvals from the specified entity with `required_approvals` field. See [Multiple approval rules](../ci/environments/deployment_approvals.md#multiple-approval-rules) for more information. |
|
||||
|
||||
To update:
|
||||
|
||||
- **`user_id`**: Ensure the updated user belongs to the given group with the Maintainer role (or above). You must also pass the `id` of either a `deploy_access_level` or `approval_rule` in the respective hash.
|
||||
- **`group_id`**: Ensure the updated group is a sub-group of the group this protected environment belongs to. You must also pass the `id` of either a `deploy_access_level` or `approval_rule` in the respective hash.
|
||||
|
||||
To delete:
|
||||
|
||||
- You must pass `_destroy` set to `true`. See the following examples.
|
||||
|
||||
### Example: Create a `deploy_access_level` record
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request PUT \
|
||||
--data '{"deploy_access_levels": [{"group_id": 9899829, access_level: 40}], "required_approval_count": 1}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
"https://gitlab.example.com/api/v4/groups/22034114/protected_environments/production"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "production",
|
||||
"deploy_access_levels": [
|
||||
{
|
||||
"id": 12,
|
||||
"access_level": 40,
|
||||
"access_level_description": "protected-access-group",
|
||||
"user_id": null,
|
||||
"group_id": 9899829,
|
||||
"group_inheritance_type": 1
|
||||
}
|
||||
],
|
||||
"required_approval_count": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Update a `deploy_access_level` record
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request PUT \
|
||||
--data '{"deploy_access_levels": [{"id": 12, "group_id": 22034120}], "required_approval_count": 2}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22034114/protected_environments/production"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "production",
|
||||
"deploy_access_levels": [
|
||||
{
|
||||
"id": 12,
|
||||
"access_level": 40,
|
||||
"access_level_description": "protected-access-group",
|
||||
"user_id": null,
|
||||
"group_id": 22034120,
|
||||
"group_inheritance_type": 0
|
||||
}
|
||||
],
|
||||
"required_approval_count": 2
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Delete a `deploy_access_level` record
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request PUT \
|
||||
--data '{"deploy_access_levels": [{"id": 12, "_destroy": true}], "required_approval_count": 0}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22034114/protected_environments/production"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "production",
|
||||
"deploy_access_levels": [],
|
||||
"required_approval_count": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Create an `approval_rule` record
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request PUT \
|
||||
--data '{"approval_rules": [{"group_id": 134, "required_approvals": 1}]}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
"https://gitlab.example.com/api/v4/groups/22034114/protected_environments/production"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "production",
|
||||
"approval_rules": [
|
||||
{
|
||||
"id": 38,
|
||||
"user_id": null,
|
||||
"group_id": 134,
|
||||
"access_level": null,
|
||||
"access_level_description": "qa-group",
|
||||
"required_approvals": 1,
|
||||
"group_inheritance_type": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Update an `approval_rule` record
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request PUT \
|
||||
--data '{"approval_rules": [{"id": 38, "group_id": 135, "required_approvals": 2}]}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22034114/protected_environments/production"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "production",
|
||||
"approval_rules": [
|
||||
{
|
||||
"id": 38,
|
||||
"user_id": null,
|
||||
"group_id": 135,
|
||||
"access_level": null,
|
||||
"access_level_description": "security-group",
|
||||
"required_approvals": 2,
|
||||
"group_inheritance_type": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Delete an `approval_rule` record
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request PUT \
|
||||
--data '{"approval_rules": [{"id": 38, "_destroy": true}]}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22034114/protected_environments/production"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "production",
|
||||
"approval_rules": []
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ Example response:
|
|||
"name":"production",
|
||||
"deploy_access_levels":[
|
||||
{
|
||||
"id": 12,
|
||||
"access_level":40,
|
||||
"access_level_description":"Maintainers",
|
||||
"user_id":null,
|
||||
|
|
@ -61,7 +62,7 @@ Example response:
|
|||
"group_inheritance_type": 0
|
||||
}
|
||||
],
|
||||
"required_approval_count": 0
|
||||
"required_approval_count": 0
|
||||
}
|
||||
]
|
||||
```
|
||||
|
|
@ -90,6 +91,7 @@ Example response:
|
|||
"name":"production",
|
||||
"deploy_access_levels":[
|
||||
{
|
||||
"id": 12,
|
||||
"access_level": 40,
|
||||
"access_level_description": "Maintainers",
|
||||
"user_id": null,
|
||||
|
|
@ -97,7 +99,7 @@ Example response:
|
|||
"group_inheritance_type": 0
|
||||
}
|
||||
],
|
||||
"required_approval_count": 0
|
||||
"required_approval_count": 0
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -109,13 +111,6 @@ Protects a single environment:
|
|||
POST /projects/:id/protected_environments
|
||||
```
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request POST \
|
||||
--data '{"name": "production", "deploy_access_levels": [{"group_id": 9899826}], "approval_rules": [{"group_id": 134}, {"group_id": 135, "required_approvals": 2}]}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
"https://gitlab.example.com/api/v4/projects/22034114/protected_environments"
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
|
|
@ -130,6 +125,13 @@ Elements in the `deploy_access_levels` and `approval_rules` array should be one
|
|||
|
||||
Each user must have access to the project and each group must [have this project shared](../user/project/members/share_project_with_groups.md).
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request POST \
|
||||
--data '{"name": "production", "deploy_access_levels": [{"group_id": 9899826}], "approval_rules": [{"group_id": 134}, {"group_id": 135, "required_approvals": 2}]}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
"https://gitlab.example.com/api/v4/projects/22034114/protected_environments"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
|
|
@ -137,6 +139,7 @@ Example response:
|
|||
"name": "production",
|
||||
"deploy_access_levels": [
|
||||
{
|
||||
"id": 12,
|
||||
"access_level": 40,
|
||||
"access_level_description": "protected-access-group",
|
||||
"user_id": null,
|
||||
|
|
@ -144,25 +147,199 @@ Example response:
|
|||
"group_inheritance_type": 0
|
||||
}
|
||||
],
|
||||
"required_approval_count": 0,
|
||||
"approval_rules": [
|
||||
{
|
||||
"user_id": null,
|
||||
"group_id": 134,
|
||||
"access_level": null,
|
||||
"access_level_description": "qa-group",
|
||||
"required_approvals": 1,
|
||||
"group_inheritance_type": 0
|
||||
},
|
||||
{
|
||||
"user_id": null,
|
||||
"group_id": 135,
|
||||
"access_level": null,
|
||||
"access_level_description": "security-group",
|
||||
"required_approvals": 2,
|
||||
"group_inheritance_type": 0
|
||||
}
|
||||
]
|
||||
"required_approval_count": 0,
|
||||
"approval_rules": [
|
||||
{
|
||||
"id": 38,
|
||||
"user_id": null,
|
||||
"group_id": 134,
|
||||
"access_level": null,
|
||||
"access_level_description": "qa-group",
|
||||
"required_approvals": 1,
|
||||
"group_inheritance_type": 0
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
"user_id": null,
|
||||
"group_id": 135,
|
||||
"access_level": null,
|
||||
"access_level_description": "security-group",
|
||||
"required_approvals": 2,
|
||||
"group_inheritance_type": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Update a protected environment
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351854) in GitLab 15.4.
|
||||
|
||||
Updates a single environment.
|
||||
|
||||
```plaintext
|
||||
PUT /projects/:id/protected_environments/:name
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `name` | string | yes | The name of the environment. |
|
||||
| `deploy_access_levels` | array | no | Array of access levels allowed to deploy, with each described by a hash. |
|
||||
| `required_approval_count` | integer | no | The number of approvals required to deploy to this environment. |
|
||||
| `approval_rules` | array | no | Array of access levels allowed to approve, with each described by a hash. See [Multiple approval rules](../ci/environments/deployment_approvals.md#multiple-approval-rules) for more information. |
|
||||
|
||||
Elements in the `deploy_access_levels` and `approval_rules` array should be one of `user_id`, `group_id` or
|
||||
`access_level`, and take the form `{user_id: integer}`, `{group_id: integer}` or
|
||||
`{access_level: integer}`. Optionally you can specify the `group_inheritance_type` on each as one of the [valid group inheritance types](#group-inheritance-types).
|
||||
|
||||
To update:
|
||||
|
||||
- **`user_id`**: Ensure the updated user has access to the project. You must also pass the `id` of either a `deploy_access_level` or `approval_rule` in the respective hash.
|
||||
- **`group_id`**: Ensure the updated group [have this project shared](../user/project/members/share_project_with_groups.md). You must also pass the `id` of either a `deploy_access_level` or `approval_rule` in the respective hash.
|
||||
|
||||
To delete:
|
||||
|
||||
- You must pass `_destroy` set to `true`. See the following examples.
|
||||
|
||||
### Example: Create a `deploy_access_level` record
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request PUT \
|
||||
--data '{"deploy_access_levels": [{"group_id": 9899829, access_level: 40}], "required_approval_count": 1}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
"https://gitlab.example.com/api/v4/projects/22034114/protected_environments/production"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "production",
|
||||
"deploy_access_levels": [
|
||||
{
|
||||
"id": 12,
|
||||
"access_level": 40,
|
||||
"access_level_description": "protected-access-group",
|
||||
"user_id": null,
|
||||
"group_id": 9899829,
|
||||
"group_inheritance_type": 1
|
||||
}
|
||||
],
|
||||
"required_approval_count": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Update a `deploy_access_level` record
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request PUT \
|
||||
--data '{"deploy_access_levels": [{"id": 12, "group_id": 22034120}], "required_approval_count": 2}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/22034114/protected_environments/production"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "production",
|
||||
"deploy_access_levels": [
|
||||
{
|
||||
"id": 12,
|
||||
"access_level": 40,
|
||||
"access_level_description": "protected-access-group",
|
||||
"user_id": null,
|
||||
"group_id": 22034120,
|
||||
"group_inheritance_type": 0
|
||||
}
|
||||
],
|
||||
"required_approval_count": 2
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Delete a `deploy_access_level` record
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request PUT \
|
||||
--data '{"deploy_access_levels": [{"id": 12, "_destroy": true}], "required_approval_count": 0}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/22034114/protected_environments/production"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "production",
|
||||
"deploy_access_levels": [],
|
||||
"required_approval_count": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Create an `approval_rule` record
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request PUT \
|
||||
--data '{"approval_rules": [{"group_id": 134, "required_approvals": 1}]}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
"https://gitlab.example.com/api/v4/projects/22034114/protected_environments/production"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "production",
|
||||
"approval_rules": [
|
||||
{
|
||||
"id": 38,
|
||||
"user_id": null,
|
||||
"group_id": 134,
|
||||
"access_level": null,
|
||||
"access_level_description": "qa-group",
|
||||
"required_approvals": 1,
|
||||
"group_inheritance_type": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Update an `approval_rule` record
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request PUT \
|
||||
--data '{"approval_rules": [{"id": 38, "group_id": 135, "required_approvals": 2}]}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/22034114/protected_environments/production"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "production",
|
||||
"approval_rules": [
|
||||
{
|
||||
"id": 38,
|
||||
"user_id": null,
|
||||
"group_id": 135,
|
||||
"access_level": null,
|
||||
"access_level_description": "security-group",
|
||||
"required_approvals": 2,
|
||||
"group_inheritance_type": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Delete an `approval_rule` record
|
||||
|
||||
```shell
|
||||
curl --header 'Content-Type: application/json' --request PUT \
|
||||
--data '{"approval_rules": [{"id": 38, "_destroy": true}]}' \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/22034114/protected_environments/production"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "production",
|
||||
"approval_rules": []
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -174,11 +351,11 @@ Unprotects the given protected environment:
|
|||
DELETE /projects/:id/protected_environments/:name
|
||||
```
|
||||
|
||||
```shell
|
||||
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/protected_environments/staging"
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `name` | string | yes | The name of the protected environment. |
|
||||
|
||||
```shell
|
||||
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/protected_environments/staging"
|
||||
```
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
type: index, concepts, howto
|
||||
---
|
||||
|
||||
# Sec Section development documentation **(FREE)**
|
||||
# Sec section development **(FREE)**
|
||||
|
||||
Development guides that are specific to Sec Section are listed here.
|
||||
The Sec section is responsible for GitLab application security features, the "Sec" part of
|
||||
DevSecOps. Development guides that are specific to the Sec section are listed here.
|
||||
|
||||
See [Terminology](../../user/application_security/terminology) for an overview of our shared terminology.
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ After sharing 'Project Acme' with 'Engineering':
|
|||
|
||||
- The group is listed in the **Groups** tab.
|
||||
- The project is listed on the group dashboard.
|
||||
- All members, including members from the ancestors of the 'Engineering' group, gain access to 'Project Acme' with an access level based on the outcome of [maximum access level](#maximum-access-level).
|
||||
|
||||
When you share a project, be aware of the following restrictions and outcomes:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Ci
|
||||
module Build
|
||||
module Artifacts
|
||||
module Adapters
|
||||
class ZipStream
|
||||
MAX_DECOMPRESSED_SIZE = 100.megabytes
|
||||
MAX_FILES_PROCESSED = 50
|
||||
|
||||
attr_reader :stream
|
||||
|
||||
InvalidStreamError = Class.new(StandardError)
|
||||
|
||||
def initialize(stream)
|
||||
raise InvalidStreamError, "Stream is required" unless stream
|
||||
|
||||
@stream = stream
|
||||
@files_processed = 0
|
||||
end
|
||||
|
||||
def each_blob
|
||||
Zip::InputStream.open(stream) do |zio|
|
||||
while entry = zio.get_next_entry
|
||||
break if at_files_processed_limit?
|
||||
next unless should_process?(entry)
|
||||
|
||||
@files_processed += 1
|
||||
|
||||
yield entry.get_input_stream.read
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def should_process?(entry)
|
||||
file?(entry) && !too_large?(entry)
|
||||
end
|
||||
|
||||
def file?(entry)
|
||||
# Check the file name as a workaround for incorrect
|
||||
# file type detection when using InputStream
|
||||
# https://github.com/rubyzip/rubyzip/issues/533
|
||||
entry.file? && !entry.name.end_with?('/')
|
||||
end
|
||||
|
||||
def too_large?(entry)
|
||||
entry.size > MAX_DECOMPRESSED_SIZE
|
||||
end
|
||||
|
||||
def at_files_processed_limit?
|
||||
@files_processed >= MAX_FILES_PROCESSED
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -46,6 +46,13 @@ module Gitlab
|
|||
url_builder.build(__subject__, only_path: true)
|
||||
end
|
||||
|
||||
def path_with_line_numbers(path, start_line, end_line)
|
||||
path.tap do |complete_path|
|
||||
complete_path << "#L#{start_line}"
|
||||
complete_path << "-#{end_line}" if end_line && end_line != start_line
|
||||
end
|
||||
end
|
||||
|
||||
class_methods do
|
||||
def presenter?
|
||||
true
|
||||
|
|
|
|||
|
|
@ -26950,6 +26950,9 @@ msgstr ""
|
|||
msgid "Notify|You don't have access to the project."
|
||||
msgstr ""
|
||||
|
||||
msgid "Notify|You have been mentioned in an issue."
|
||||
msgstr ""
|
||||
|
||||
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -40076,6 +40079,9 @@ msgstr ""
|
|||
msgid "This board's scope is reduced"
|
||||
msgstr ""
|
||||
|
||||
msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
|
||||
msgstr ""
|
||||
|
||||
msgid "This chart could not be displayed"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,14 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
def go_to_repository_contributors
|
||||
hover_repository do
|
||||
within_submenu do
|
||||
click_element(:sidebar_menu_item_link, menu_item: 'Contributors')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def hover_repository
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ variables=$(cat <<YML
|
|||
variables:
|
||||
RELEASE: "${CI_REGISTRY}/gitlab-org/build/omnibus-gitlab-mirror/gitlab-ee:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA:-$CI_COMMIT_SHA}"
|
||||
SKIP_REPORT_IN_ISSUES: "${SKIP_REPORT_IN_ISSUES:-true}"
|
||||
OMNIBUS_GITLAB_CACHE_UPDATE: "${OMNIBUS_GITLAB_CACHE_UPDATE:-false}"
|
||||
COLORIZED_LOGS: "true"
|
||||
QA_LOG_LEVEL: "info"
|
||||
QA_TESTS: "$QA_TESTS"
|
||||
|
|
|
|||
|
|
@ -102,28 +102,6 @@ FactoryBot.define do
|
|||
end
|
||||
end
|
||||
|
||||
trait :zip_with_single_file do
|
||||
file_type { :archive }
|
||||
file_format { :zip }
|
||||
|
||||
after(:build) do |artifact, evaluator|
|
||||
artifact.file = fixture_file_upload(
|
||||
Rails.root.join('spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zip'),
|
||||
'application/zip')
|
||||
end
|
||||
end
|
||||
|
||||
trait :zip_with_multiple_files do
|
||||
file_type { :archive }
|
||||
file_format { :zip }
|
||||
|
||||
after(:build) do |artifact, evaluator|
|
||||
artifact.file = fixture_file_upload(
|
||||
Rails.root.join('spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zip'),
|
||||
'application/zip')
|
||||
end
|
||||
end
|
||||
|
||||
trait :junit do
|
||||
file_type { :junit }
|
||||
file_format { :gzip }
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -23,6 +23,7 @@ function factory({ canApprove = true } = {}) {
|
|||
current_user: { can_approve: canApprove },
|
||||
}),
|
||||
noteableType: () => 'merge_request',
|
||||
getCurrentUserLastNote: () => ({ id: 1 }),
|
||||
},
|
||||
modules: {
|
||||
batchComments: {
|
||||
|
|
@ -45,6 +46,7 @@ const findForm = () => wrapper.findByTestId('submit-gl-form');
|
|||
describe('Batch comments submit dropdown', () => {
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
window.mrTabs = null;
|
||||
});
|
||||
|
||||
it('calls publishReview with note data', async () => {
|
||||
|
|
@ -63,6 +65,19 @@ describe('Batch comments submit dropdown', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('switches to the overview tab after submit', async () => {
|
||||
window.mrTabs = { tabShown: jest.fn() };
|
||||
|
||||
factory();
|
||||
|
||||
findCommentTextarea().setValue('Hello world');
|
||||
|
||||
await findForm().vm.$emit('submit', { preventDefault: jest.fn() });
|
||||
await Vue.nextTick();
|
||||
|
||||
expect(window.mrTabs.tabShown).toHaveBeenCalledWith('show');
|
||||
});
|
||||
|
||||
it('sets submit dropdown to loading', async () => {
|
||||
factory();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::Build::Artifacts::Adapters::ZipStream do
|
||||
let(:file_name) { 'single_file.zip' }
|
||||
let(:fixture_path) { "lib/gitlab/ci/build/artifacts/adapters/zip_stream/#{file_name}" }
|
||||
let(:stream) { File.open(expand_fixture_path(fixture_path), 'rb') }
|
||||
|
||||
describe '#initialize' do
|
||||
it 'initializes when stream is passed' do
|
||||
expect { described_class.new(stream) }.not_to raise_error
|
||||
end
|
||||
|
||||
context 'when stream is not passed' do
|
||||
let(:stream) { nil }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { described_class.new(stream) }.to raise_error(described_class::InvalidStreamError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#each_blob' do
|
||||
let(:adapter) { described_class.new(stream) }
|
||||
|
||||
context 'when stream is a zip file' do
|
||||
it 'iterates file content when zip file contains one file' do
|
||||
expect { |b| adapter.each_blob(&b) }
|
||||
.to yield_with_args("file 1 content\n")
|
||||
end
|
||||
|
||||
context 'when zip file contains multiple files' do
|
||||
let(:file_name) { 'multiple_files.zip' }
|
||||
|
||||
it 'iterates content of all files' do
|
||||
expect { |b| adapter.each_blob(&b) }
|
||||
.to yield_successive_args("file 1 content\n", "file 2 content\n")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when zip file includes files in a directory' do
|
||||
let(:file_name) { 'with_directory.zip' }
|
||||
|
||||
it 'iterates contents from files only' do
|
||||
expect { |b| adapter.each_blob(&b) }
|
||||
.to yield_successive_args("file 1 content\n", "file 2 content\n")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when zip contains a file which decompresses beyond the size limit' do
|
||||
let(:file_name) { '200_mb_decompressed.zip' }
|
||||
|
||||
it 'does not read the file' do
|
||||
expect { |b| adapter.each_blob(&b) }.not_to yield_control
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the zip contains too many files' do
|
||||
let(:file_name) { '100_files.zip' }
|
||||
|
||||
it 'stops processing when the limit is reached' do
|
||||
expect { |b| adapter.each_blob(&b) }
|
||||
.to yield_control.exactly(described_class::MAX_FILES_PROCESSED).times
|
||||
end
|
||||
end
|
||||
|
||||
context 'when stream is a zipbomb' do
|
||||
let(:file_name) { 'zipbomb.zip' }
|
||||
|
||||
it 'does not read the file' do
|
||||
expect { |b| adapter.each_blob(&b) }.not_to yield_control
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when stream is not a zip file' do
|
||||
let(:stream) { File.open(expand_fixture_path('junit/junit.xml.gz'), 'rb') }
|
||||
|
||||
it 'does not yield any data' do
|
||||
expect { |b| adapter.each_blob(&b) }.not_to yield_control
|
||||
expect { adapter.each_blob { |b| b } }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,12 +5,14 @@ require 'spec_helper'
|
|||
RSpec.describe CommitSignatures::GpgSignature do
|
||||
# This commit is seeded from https://gitlab.com/gitlab-org/gitlab-test
|
||||
# For instructions on how to add more seed data, see the project README
|
||||
let(:commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' }
|
||||
let!(:project) { create(:project, :repository, path: 'sample-project') }
|
||||
let!(:commit) { create(:commit, project: project, sha: commit_sha) }
|
||||
let(:signature) { create(:gpg_signature, commit_sha: commit_sha) }
|
||||
let(:gpg_key) { create(:gpg_key) }
|
||||
let(:gpg_key_subkey) { create(:gpg_key_subkey) }
|
||||
let_it_be(:commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' }
|
||||
let_it_be(:project) { create(:project, :repository, path: 'sample-project') }
|
||||
let_it_be(:commit) { create(:commit, project: project, sha: commit_sha) }
|
||||
let_it_be(:gpg_key) { create(:gpg_key) }
|
||||
let_it_be(:gpg_key_subkey) { create(:gpg_key_subkey, gpg_key: gpg_key) }
|
||||
|
||||
let(:signature) { create(:gpg_signature, commit_sha: commit_sha, gpg_key: gpg_key) }
|
||||
|
||||
let(:attributes) do
|
||||
{
|
||||
commit_sha: commit_sha,
|
||||
|
|
@ -35,8 +37,7 @@ RSpec.describe CommitSignatures::GpgSignature do
|
|||
end
|
||||
|
||||
describe '.by_commit_sha scope' do
|
||||
let(:gpg_key) { create(:gpg_key, key: GpgHelpers::User2.public_key) }
|
||||
let!(:another_gpg_signature) { create(:gpg_signature, gpg_key: gpg_key) }
|
||||
let_it_be(:another_gpg_signature) { create(:gpg_signature, gpg_key: gpg_key) }
|
||||
|
||||
it 'returns all gpg signatures by sha' do
|
||||
expect(described_class.by_commit_sha(commit_sha)).to match_array([signature])
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ require 'spec_helper'
|
|||
RSpec.describe CommitSignatures::SshSignature do
|
||||
# This commit is seeded from https://gitlab.com/gitlab-org/gitlab-test
|
||||
# For instructions on how to add more seed data, see the project README
|
||||
let(:commit_sha) { '7b5160f9bb23a3d58a0accdbe89da13b96b1ece9' }
|
||||
let!(:project) { create(:project, :repository, path: 'sample-project') }
|
||||
let!(:commit) { create(:commit, project: project, sha: commit_sha) }
|
||||
let(:signature) { create(:ssh_signature, commit_sha: commit_sha) }
|
||||
let(:ssh_key) { create(:ed25519_key_256) }
|
||||
let_it_be(:commit_sha) { '7b5160f9bb23a3d58a0accdbe89da13b96b1ece9' }
|
||||
let_it_be(:project) { create(:project, :repository, path: 'sample-project') }
|
||||
let_it_be(:commit) { create(:commit, project: project, sha: commit_sha) }
|
||||
let_it_be(:ssh_key) { create(:ed25519_key_256) }
|
||||
|
||||
let(:attributes) do
|
||||
{
|
||||
commit_sha: commit_sha,
|
||||
|
|
@ -18,6 +18,8 @@ RSpec.describe CommitSignatures::SshSignature do
|
|||
}
|
||||
end
|
||||
|
||||
let(:signature) { create(:ssh_signature, commit_sha: commit_sha, key: ssh_key) }
|
||||
|
||||
it_behaves_like 'having unique enum values'
|
||||
it_behaves_like 'commit signature'
|
||||
|
||||
|
|
|
|||
|
|
@ -5,11 +5,10 @@ require 'spec_helper'
|
|||
RSpec.describe CommitSignatures::X509CommitSignature do
|
||||
# This commit is seeded from https://gitlab.com/gitlab-org/gitlab-test
|
||||
# For instructions on how to add more seed data, see the project README
|
||||
let(:commit_sha) { '189a6c924013fc3fe40d6f1ec1dc20214183bc97' }
|
||||
let(:project) { create(:project, :public, :repository) }
|
||||
let!(:commit) { create(:commit, project: project, sha: commit_sha) }
|
||||
let(:x509_certificate) { create(:x509_certificate) }
|
||||
let(:signature) { create(:x509_commit_signature, commit_sha: commit_sha) }
|
||||
let_it_be(:commit_sha) { '189a6c924013fc3fe40d6f1ec1dc20214183bc97' }
|
||||
let_it_be(:project) { create(:project, :public, :repository) }
|
||||
let_it_be(:commit) { create(:commit, project: project, sha: commit_sha) }
|
||||
let_it_be(:x509_certificate) { create(:x509_certificate) }
|
||||
|
||||
let(:attributes) do
|
||||
{
|
||||
|
|
@ -20,6 +19,8 @@ RSpec.describe CommitSignatures::X509CommitSignature do
|
|||
}
|
||||
end
|
||||
|
||||
let(:signature) { create(:x509_commit_signature, commit_sha: commit_sha, x509_certificate: x509_certificate) }
|
||||
|
||||
it_behaves_like 'having unique enum values'
|
||||
it_behaves_like 'commit signature'
|
||||
|
||||
|
|
|
|||
|
|
@ -46,30 +46,8 @@ RSpec.describe Ci::Artifactable do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when file format is zip' do
|
||||
context 'when artifact contains one file' do
|
||||
let(:artifact) { build(:ci_job_artifact, :zip_with_single_file) }
|
||||
|
||||
it 'iterates blob once' do
|
||||
expect { |b| artifact.each_blob(&b) }.to yield_control.once
|
||||
end
|
||||
end
|
||||
|
||||
context 'when artifact contains two files' do
|
||||
let(:artifact) { build(:ci_job_artifact, :zip_with_multiple_files) }
|
||||
|
||||
it 'iterates blob two times' do
|
||||
expect { |b| artifact.each_blob(&b) }.to yield_control.exactly(2).times
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are no adapters for the file format' do
|
||||
let(:artifact) { build(:ci_job_artifact, :junit) }
|
||||
|
||||
before do
|
||||
allow(artifact).to receive(:file_format).and_return(:unknown)
|
||||
end
|
||||
let(:artifact) { build(:ci_job_artifact, :junit, file_format: :zip) }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { |b| artifact.each_blob(&b) }.to raise_error(described_class::NotSupportedAdapterError)
|
||||
|
|
|
|||
|
|
@ -190,9 +190,20 @@ RSpec.describe Users::DestroyService do
|
|||
])
|
||||
end
|
||||
|
||||
it 'does not delete the user' do
|
||||
it 'does not delete the user, nor the group' do
|
||||
service.execute(user)
|
||||
|
||||
expect(User.find(user.id)).to eq user
|
||||
expect(Group.find(solo_owned.id)).to eq solo_owned
|
||||
end
|
||||
|
||||
context 'when delete solo owned groups option is passed' do
|
||||
it 'deletes the user and the group' do
|
||||
service.execute(user, delete_solo_owned_groups: true)
|
||||
|
||||
expect(User.where(id: user.id)).not_to exist
|
||||
expect(Group.where(id: solo_owned.id)).not_to exist
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'gitlab-dangerfiles'
|
||||
require 'danger'
|
||||
require 'danger/plugins/internal/helper'
|
||||
require 'gitlab/dangerfiles/spec_helper'
|
||||
|
||||
require_relative '../../../tooling/danger/config_files'
|
||||
require_relative '../../../tooling/danger/project_helper'
|
||||
|
||||
RSpec.describe Tooling::Danger::ConfigFiles do
|
||||
include_context "with dangerfile"
|
||||
|
||||
let(:fake_danger) { DangerSpecHelper.fake_danger.include(described_class) }
|
||||
let(:fake_project_helper) { instance_double(Tooling::Danger::ProjectHelper) }
|
||||
let(:matching_line) { "+ introduced_by_url:" }
|
||||
|
||||
subject(:config_file) { fake_danger.new(helper: fake_helper) }
|
||||
|
||||
before do
|
||||
allow(config_file).to receive(:project_helper).and_return(fake_project_helper)
|
||||
end
|
||||
|
||||
describe '#add_suggestion_for_missing_introduced_by_url' do
|
||||
let(:file_lines) do
|
||||
[
|
||||
"---",
|
||||
"name: about_your_company_registration_flow",
|
||||
"introduced_by_url: #{url}",
|
||||
"rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/355909",
|
||||
"milestone: '14.10'"
|
||||
]
|
||||
end
|
||||
|
||||
let(:filename) { 'config/feature_flags/new_ff.yml' }
|
||||
|
||||
before do
|
||||
allow(config_file.project_helper).to receive(:file_lines).and_return(file_lines)
|
||||
allow(config_file.helper).to receive(:added_files).and_return([filename])
|
||||
allow(config_file.helper).to receive(:mr_web_url).and_return(url)
|
||||
end
|
||||
|
||||
context 'when config file has an empty introduced_by_url line' do
|
||||
let(:url) { '' }
|
||||
|
||||
it 'adds suggestions at the correct line' do
|
||||
expected_format = format(described_class::SUGGEST_INTRODUCED_BY_COMMENT, url: url)
|
||||
expect(config_file).to receive(:markdown).with(expected_format, file: filename, line: 3)
|
||||
|
||||
config_file.add_suggestion_for_missing_introduced_by_url
|
||||
end
|
||||
end
|
||||
|
||||
context 'when config file has an introduced_by_url line with value' do
|
||||
let(:url) { 'https://gitlab.com/gitlab-org/gitlab/-/issues/1' }
|
||||
|
||||
it 'does not add suggestion' do
|
||||
expect(config_file).not_to receive(:markdown)
|
||||
|
||||
config_file.add_suggestion_for_missing_introduced_by_url
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#new_config_files' do
|
||||
let(:expected_files) do
|
||||
%w[
|
||||
config/feature_flags/first.yml
|
||||
config/events/1234_new_event.yml
|
||||
config/metrics/count_7d/new_metric.yml
|
||||
]
|
||||
end
|
||||
|
||||
before do
|
||||
all_new_files = %w[
|
||||
app/workers/a.rb
|
||||
doc/events/new_event.md
|
||||
config/feature_flags/first.yml
|
||||
config/events/1234_new_event.yml
|
||||
config/metrics/count_7d/new_metric.yml
|
||||
app/assets/index.js
|
||||
]
|
||||
|
||||
allow(config_file.helper).to receive(:added_files).and_return(all_new_files)
|
||||
end
|
||||
|
||||
it 'returns added, modified, and renamed_after files by default' do
|
||||
expect(config_file.new_config_files).to match_array(expected_files)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'yaml'
|
||||
|
||||
module Tooling
|
||||
module Danger
|
||||
module ConfigFiles
|
||||
SUGGEST_INTRODUCED_BY_COMMENT = <<~SUGGEST_COMMENT
|
||||
```suggestion
|
||||
introduced_by_url: "%<url>s"
|
||||
```
|
||||
SUGGEST_COMMENT
|
||||
|
||||
CONFIG_DIRS = %w[
|
||||
config/feature_flags
|
||||
config/metrics
|
||||
config/events
|
||||
].freeze
|
||||
|
||||
def add_suggestion_for_missing_introduced_by_url
|
||||
new_config_files.each do |file_name|
|
||||
config_file_lines = project_helper.file_lines(file_name)
|
||||
|
||||
config_file_lines.each_with_index do |added_line, i|
|
||||
next unless added_line =~ /^introduced_by_url:\s?$/
|
||||
|
||||
markdown(format(SUGGEST_INTRODUCED_BY_COMMENT, url: helper.mr_web_url), file: file_name, line: i + 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def new_config_files
|
||||
helper.added_files.select { |f| in_config_dir?(f) && f.end_with?('yml') }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def in_config_dir?(path)
|
||||
CONFIG_DIRS.any? { |d| path.start_with?(d) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue