Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-08-31 15:12:39 +00:00
parent 3034c7e6aa
commit 19044caf66
38 changed files with 681 additions and 279 deletions

View File

@ -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]

View File

@ -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

View File

@ -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'

View File

@ -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(() =>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -0,0 +1,3 @@
# frozen_string_literal: true
config_files.add_suggestion_for_missing_introduced_by_url

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
75cb9d7b4a0bc8ad26b3bf6bf41a4414bcc4307607de058fc35fe4ece7009423

View File

@ -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": []
}
```

View File

@ -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"
```

View File

@ -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.

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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 ""

View File

@ -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

View File

@ -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"

View File

@ -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 }

View File

@ -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();

View File

@ -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

View File

@ -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])

View File

@ -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'

View File

@ -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'

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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