Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-08-31 18:12:39 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-08-31 18:12:39 +0300
commit19044caf6695065ff64e26355e830dbdc6719e54 (patch)
tree5b9bdf081ab28d873cee38a5036f143a300ba733
parent3034c7e6aa99d21c3d9fa1df01f60fdd3f32d914 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/ci/package-and-test/main.gitlab-ci.yml47
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml1
-rw-r--r--.rubocop_todo/style/format_string.yml1
-rw-r--r--app/assets/javascripts/batch_comments/components/submit_dropdown.vue7
-rw-r--r--app/helpers/notify_helper.rb11
-rw-r--r--app/models/concerns/ci/artifactable.rb11
-rw-r--r--app/services/users/destroy_service.rb12
-rw-r--r--app/views/notify/new_mention_in_issue_email.html.haml2
-rw-r--r--app/views/projects/_stat_anchor_list.html.haml2
-rw-r--r--danger/config_files/Dangerfile3
-rw-r--r--danger/plugins/config_files.rb10
-rw-r--r--db/post_migrate/20220826165048_drop_temporary_job_trace_index.rb13
-rw-r--r--db/schema_migrations/202208261650481
-rw-r--r--doc/api/group_protected_environments.md175
-rw-r--r--doc/api/protected_environments.md235
-rw-r--r--doc/development/sec/index.md5
-rw-r--r--doc/user/project/members/share_project_with_groups.md1
-rw-r--r--lib/gitlab/ci/build/artifacts/adapters/zip_stream.rb61
-rw-r--r--lib/gitlab/view/presenter/base.rb7
-rw-r--r--locale/gitlab.pot6
-rw-r--r--qa/qa/page/project/sub_menus/repository.rb8
-rwxr-xr-xscripts/generate-e2e-pipeline1
-rw-r--r--spec/factories/ci/job_artifacts.rb22
-rw-r--r--spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/100_files.zipbin15902 -> 0 bytes
-rw-r--r--spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/200_mb_decompressed.zipbin203718 -> 0 bytes
-rw-r--r--spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zipbin332 -> 0 bytes
-rw-r--r--spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zipbin177 -> 0 bytes
-rw-r--r--spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/with_directory.zipbin520 -> 0 bytes
-rw-r--r--spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/zipbomb.zipbin1042247 -> 0 bytes
-rw-r--r--spec/frontend/batch_comments/components/submit_dropdown_spec.js15
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb86
-rw-r--r--spec/models/commit_signatures/gpg_signature_spec.rb17
-rw-r--r--spec/models/commit_signatures/ssh_signature_spec.rb12
-rw-r--r--spec/models/commit_signatures/x509_commit_signature_spec.rb11
-rw-r--r--spec/models/concerns/ci/artifactable_spec.rb24
-rw-r--r--spec/services/users/destroy_service_spec.rb13
-rw-r--r--spec/tooling/danger/config_files_spec.rb91
-rw-r--r--tooling/danger/config_files.rb43
38 files changed, 678 insertions, 276 deletions
diff --git a/.gitlab/ci/package-and-test/main.gitlab-ci.yml b/.gitlab/ci/package-and-test/main.gitlab-ci.yml
index 8ba690533ca..117a78a7325 100644
--- a/.gitlab/ci/package-and-test/main.gitlab-ci.yml
+++ b/.gitlab/ci/package-and-test/main.gitlab-ci.yml
@@ -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]
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index 1f37e7f7cd3..6cc958e192d 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -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
diff --git a/.rubocop_todo/style/format_string.yml b/.rubocop_todo/style/format_string.yml
index bb95d8f7fe9..66c368a7a52 100644
--- a/.rubocop_todo/style/format_string.yml
+++ b/.rubocop_todo/style/format_string.yml
@@ -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'
diff --git a/app/assets/javascripts/batch_comments/components/submit_dropdown.vue b/app/assets/javascripts/batch_comments/components/submit_dropdown.vue
index c5b713b5447..acc3cbe10a0 100644
--- a/app/assets/javascripts/batch_comments/components/submit_dropdown.vue
+++ b/app/assets/javascripts/batch_comments/components/submit_dropdown.vue
@@ -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(() =>
diff --git a/app/helpers/notify_helper.rb b/app/helpers/notify_helper.rb
index 455b56e92fc..e15b1b21fe1 100644
--- a/app/helpers/notify_helper.rb
+++ b/app/helpers/notify_helper.rb
@@ -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
diff --git a/app/models/concerns/ci/artifactable.rb b/app/models/concerns/ci/artifactable.rb
index ee8e98ec1bf..3fdbd6a8789 100644
--- a/app/models/concerns/ci/artifactable.rb
+++ b/app/models/concerns/ci/artifactable.rb
@@ -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
diff --git a/app/services/users/destroy_service.rb b/app/services/users/destroy_service.rb
index dfa9316889e..5cd390141f0 100644
--- a/app/services/users/destroy_service.rb
+++ b/app/services/users/destroy_service.rb
@@ -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
diff --git a/app/views/notify/new_mention_in_issue_email.html.haml b/app/views/notify/new_mention_in_issue_email.html.haml
index 6b45ac265f7..3b2e36d118b 100644
--- a/app/views/notify/new_mention_in_issue_email.html.haml
+++ b/app/views/notify/new_mention_in_issue_email.html.haml
@@ -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'
diff --git a/app/views/projects/_stat_anchor_list.html.haml b/app/views/projects/_stat_anchor_list.html.haml
index 4a21cb32c20..1409b28e735 100644
--- a/app/views/projects/_stat_anchor_list.html.haml
+++ b/app/views/projects/_stat_anchor_list.html.haml
@@ -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
diff --git a/danger/config_files/Dangerfile b/danger/config_files/Dangerfile
new file mode 100644
index 00000000000..dcd2e44df07
--- /dev/null
+++ b/danger/config_files/Dangerfile
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+config_files.add_suggestion_for_missing_introduced_by_url
diff --git a/danger/plugins/config_files.rb b/danger/plugins/config_files.rb
new file mode 100644
index 00000000000..2604a491d03
--- /dev/null
+++ b/danger/plugins/config_files.rb
@@ -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
diff --git a/db/post_migrate/20220826165048_drop_temporary_job_trace_index.rb b/db/post_migrate/20220826165048_drop_temporary_job_trace_index.rb
new file mode 100644
index 00000000000..0cad7cd1968
--- /dev/null
+++ b/db/post_migrate/20220826165048_drop_temporary_job_trace_index.rb
@@ -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
diff --git a/db/schema_migrations/20220826165048 b/db/schema_migrations/20220826165048
new file mode 100644
index 00000000000..0539118356d
--- /dev/null
+++ b/db/schema_migrations/20220826165048
@@ -0,0 +1 @@
+75cb9d7b4a0bc8ad26b3bf6bf41a4414bcc4307607de058fc35fe4ece7009423 \ No newline at end of file
diff --git a/doc/api/group_protected_environments.md b/doc/api/group_protected_environments.md
index 0f1527f8968..8a7a3ecd3bc 100644
--- a/doc/api/group_protected_environments.md
+++ b/doc/api/group_protected_environments.md
@@ -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": []
}
```
diff --git a/doc/api/protected_environments.md b/doc/api/protected_environments.md
index d210a6dfccf..4c6f509a752 100644
--- a/doc/api/protected_environments.md
+++ b/doc/api/protected_environments.md
@@ -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,6 +111,20 @@ Protects a single environment:
POST /projects/:id/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. |
+| `name` | string | yes | The name of the environment. |
+| `deploy_access_levels` | array | yes | 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).
+
+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}]}' \
@@ -116,19 +132,84 @@ curl --header 'Content-Type: application/json' --request POST \
"https://gitlab.example.com/api/v4/projects/22034114/protected_environments"
```
+Example response:
+
+```json
+{
+ "name": "production",
+ "deploy_access_levels": [
+ {
+ "id": 12,
+ "access_level": 40,
+ "access_level_description": "protected-access-group",
+ "user_id": null,
+ "group_id": 9899826,
+ "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 | yes | 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. |
+| `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).
-Each user must have access to the project and each group must [have this project shared](../user/project/members/share_project_with_groups.md).
+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:
@@ -137,32 +218,128 @@ Example response:
"name": "production",
"deploy_access_levels": [
{
+ "id": 12,
"access_level": 40,
"access_level_description": "protected-access-group",
"user_id": null,
- "group_id": 9899826,
+ "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": 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": 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"
+```
diff --git a/doc/development/sec/index.md b/doc/development/sec/index.md
index 9200311f731..06c20cee0bb 100644
--- a/doc/development/sec/index.md
+++ b/doc/development/sec/index.md
@@ -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.
diff --git a/doc/user/project/members/share_project_with_groups.md b/doc/user/project/members/share_project_with_groups.md
index 1196fe3c524..ee161deaabb 100644
--- a/doc/user/project/members/share_project_with_groups.md
+++ b/doc/user/project/members/share_project_with_groups.md
@@ -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:
diff --git a/lib/gitlab/ci/build/artifacts/adapters/zip_stream.rb b/lib/gitlab/ci/build/artifacts/adapters/zip_stream.rb
deleted file mode 100644
index 690a47097c6..00000000000
--- a/lib/gitlab/ci/build/artifacts/adapters/zip_stream.rb
+++ /dev/null
@@ -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
diff --git a/lib/gitlab/view/presenter/base.rb b/lib/gitlab/view/presenter/base.rb
index a2d217fb42f..2a57ca9ae02 100644
--- a/lib/gitlab/view/presenter/base.rb
+++ b/lib/gitlab/view/presenter/base.rb
@@ -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
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 995284354fc..1192b788a06 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -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 ""
diff --git a/qa/qa/page/project/sub_menus/repository.rb b/qa/qa/page/project/sub_menus/repository.rb
index e35828ecd6a..f9d55c0009c 100644
--- a/qa/qa/page/project/sub_menus/repository.rb
+++ b/qa/qa/page/project/sub_menus/repository.rb
@@ -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
diff --git a/scripts/generate-e2e-pipeline b/scripts/generate-e2e-pipeline
index 0fa940d69af..0588b923b3b 100755
--- a/scripts/generate-e2e-pipeline
+++ b/scripts/generate-e2e-pipeline
@@ -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"
diff --git a/spec/factories/ci/job_artifacts.rb b/spec/factories/ci/job_artifacts.rb
index 07cc30486e0..f8b964cf8e0 100644
--- a/spec/factories/ci/job_artifacts.rb
+++ b/spec/factories/ci/job_artifacts.rb
@@ -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 }
diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/100_files.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/100_files.zip
deleted file mode 100644
index 31124abc0e5..00000000000
--- a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/100_files.zip
+++ /dev/null
Binary files differ
diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/200_mb_decompressed.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/200_mb_decompressed.zip
deleted file mode 100644
index 8c56cce641a..00000000000
--- a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/200_mb_decompressed.zip
+++ /dev/null
Binary files differ
diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zip
deleted file mode 100644
index 09ac4e5df51..00000000000
--- a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zip
+++ /dev/null
Binary files differ
diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zip
deleted file mode 100644
index 81768a9f2b3..00000000000
--- a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zip
+++ /dev/null
Binary files differ
diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/with_directory.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/with_directory.zip
deleted file mode 100644
index 6de321ea86a..00000000000
--- a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/with_directory.zip
+++ /dev/null
Binary files differ
diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/zipbomb.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/zipbomb.zip
deleted file mode 100644
index b8cfcef9739..00000000000
--- a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/zipbomb.zip
+++ /dev/null
Binary files differ
diff --git a/spec/frontend/batch_comments/components/submit_dropdown_spec.js b/spec/frontend/batch_comments/components/submit_dropdown_spec.js
index dc7ecb8e44d..b28e6d68e40 100644
--- a/spec/frontend/batch_comments/components/submit_dropdown_spec.js
+++ b/spec/frontend/batch_comments/components/submit_dropdown_spec.js
@@ -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();
diff --git a/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb b/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb
deleted file mode 100644
index 2c236ba3726..00000000000
--- a/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb
+++ /dev/null
@@ -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
diff --git a/spec/models/commit_signatures/gpg_signature_spec.rb b/spec/models/commit_signatures/gpg_signature_spec.rb
index 6ae2a202b72..605ad725dd7 100644
--- a/spec/models/commit_signatures/gpg_signature_spec.rb
+++ b/spec/models/commit_signatures/gpg_signature_spec.rb
@@ -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])
diff --git a/spec/models/commit_signatures/ssh_signature_spec.rb b/spec/models/commit_signatures/ssh_signature_spec.rb
index 64d95fe3a71..08530bf6964 100644
--- a/spec/models/commit_signatures/ssh_signature_spec.rb
+++ b/spec/models/commit_signatures/ssh_signature_spec.rb
@@ -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'
diff --git a/spec/models/commit_signatures/x509_commit_signature_spec.rb b/spec/models/commit_signatures/x509_commit_signature_spec.rb
index beb101cdd89..b971fd078e2 100644
--- a/spec/models/commit_signatures/x509_commit_signature_spec.rb
+++ b/spec/models/commit_signatures/x509_commit_signature_spec.rb
@@ -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'
diff --git a/spec/models/concerns/ci/artifactable_spec.rb b/spec/models/concerns/ci/artifactable_spec.rb
index 64691165e21..6af244a5a0f 100644
--- a/spec/models/concerns/ci/artifactable_spec.rb
+++ b/spec/models/concerns/ci/artifactable_spec.rb
@@ -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)
diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb
index 90c4f70d749..26e25cf6438 100644
--- a/spec/services/users/destroy_service_spec.rb
+++ b/spec/services/users/destroy_service_spec.rb
@@ -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
diff --git a/spec/tooling/danger/config_files_spec.rb b/spec/tooling/danger/config_files_spec.rb
new file mode 100644
index 00000000000..0e01908a1dd
--- /dev/null
+++ b/spec/tooling/danger/config_files_spec.rb
@@ -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
diff --git a/tooling/danger/config_files.rb b/tooling/danger/config_files.rb
new file mode 100644
index 00000000000..436335bfc06
--- /dev/null
+++ b/tooling/danger/config_files.rb
@@ -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