Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-02-15 09:17:01 +00:00
parent 66fc7ba6f3
commit e7e44c0e4c
51 changed files with 689 additions and 369 deletions

View File

@ -2,7 +2,7 @@
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'rails', '~> 6.1.4.4' gem 'rails', '~> 6.1.4.6'
gem 'bootsnap', '~> 1.9.1', require: false gem 'bootsnap', '~> 1.9.1', require: false

View File

@ -11,63 +11,63 @@ GEM
RedCloth (4.3.2) RedCloth (4.3.2)
acme-client (2.0.9) acme-client (2.0.9)
faraday (>= 0.17, < 2.0.0) faraday (>= 0.17, < 2.0.0)
actioncable (6.1.4.4) actioncable (6.1.4.6)
actionpack (= 6.1.4.4) actionpack (= 6.1.4.6)
activesupport (= 6.1.4.4) activesupport (= 6.1.4.6)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
actionmailbox (6.1.4.4) actionmailbox (6.1.4.6)
actionpack (= 6.1.4.4) actionpack (= 6.1.4.6)
activejob (= 6.1.4.4) activejob (= 6.1.4.6)
activerecord (= 6.1.4.4) activerecord (= 6.1.4.6)
activestorage (= 6.1.4.4) activestorage (= 6.1.4.6)
activesupport (= 6.1.4.4) activesupport (= 6.1.4.6)
mail (>= 2.7.1) mail (>= 2.7.1)
actionmailer (6.1.4.4) actionmailer (6.1.4.6)
actionpack (= 6.1.4.4) actionpack (= 6.1.4.6)
actionview (= 6.1.4.4) actionview (= 6.1.4.6)
activejob (= 6.1.4.4) activejob (= 6.1.4.6)
activesupport (= 6.1.4.4) activesupport (= 6.1.4.6)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (6.1.4.4) actionpack (6.1.4.6)
actionview (= 6.1.4.4) actionview (= 6.1.4.6)
activesupport (= 6.1.4.4) activesupport (= 6.1.4.6)
rack (~> 2.0, >= 2.0.9) rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (6.1.4.4) actiontext (6.1.4.6)
actionpack (= 6.1.4.4) actionpack (= 6.1.4.6)
activerecord (= 6.1.4.4) activerecord (= 6.1.4.6)
activestorage (= 6.1.4.4) activestorage (= 6.1.4.6)
activesupport (= 6.1.4.4) activesupport (= 6.1.4.6)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (6.1.4.4) actionview (6.1.4.6)
activesupport (= 6.1.4.4) activesupport (= 6.1.4.6)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (6.1.4.4) activejob (6.1.4.6)
activesupport (= 6.1.4.4) activesupport (= 6.1.4.6)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (6.1.4.4) activemodel (6.1.4.6)
activesupport (= 6.1.4.4) activesupport (= 6.1.4.6)
activerecord (6.1.4.4) activerecord (6.1.4.6)
activemodel (= 6.1.4.4) activemodel (= 6.1.4.6)
activesupport (= 6.1.4.4) activesupport (= 6.1.4.6)
activerecord-explain-analyze (0.1.0) activerecord-explain-analyze (0.1.0)
activerecord (>= 4) activerecord (>= 4)
pg pg
activestorage (6.1.4.4) activestorage (6.1.4.6)
actionpack (= 6.1.4.4) actionpack (= 6.1.4.6)
activejob (= 6.1.4.4) activejob (= 6.1.4.6)
activerecord (= 6.1.4.4) activerecord (= 6.1.4.6)
activesupport (= 6.1.4.4) activesupport (= 6.1.4.6)
marcel (~> 1.0.0) marcel (~> 1.0.0)
mini_mime (>= 1.1.0) mini_mime (>= 1.1.0)
activesupport (6.1.4.4) activesupport (6.1.4.6)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
@ -967,20 +967,20 @@ GEM
rack-test (1.1.0) rack-test (1.1.0)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
rack-timeout (0.5.2) rack-timeout (0.5.2)
rails (6.1.4.4) rails (6.1.4.6)
actioncable (= 6.1.4.4) actioncable (= 6.1.4.6)
actionmailbox (= 6.1.4.4) actionmailbox (= 6.1.4.6)
actionmailer (= 6.1.4.4) actionmailer (= 6.1.4.6)
actionpack (= 6.1.4.4) actionpack (= 6.1.4.6)
actiontext (= 6.1.4.4) actiontext (= 6.1.4.6)
actionview (= 6.1.4.4) actionview (= 6.1.4.6)
activejob (= 6.1.4.4) activejob (= 6.1.4.6)
activemodel (= 6.1.4.4) activemodel (= 6.1.4.6)
activerecord (= 6.1.4.4) activerecord (= 6.1.4.6)
activestorage (= 6.1.4.4) activestorage (= 6.1.4.6)
activesupport (= 6.1.4.4) activesupport (= 6.1.4.6)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 6.1.4.4) railties (= 6.1.4.6)
sprockets-rails (>= 2.0.0) sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.5) rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1) actionpack (>= 5.0.1.rc1)
@ -994,9 +994,9 @@ GEM
rails-i18n (6.0.0) rails-i18n (6.0.0)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 7) railties (>= 6.0.0, < 7)
railties (6.1.4.4) railties (6.1.4.6)
actionpack (= 6.1.4.4) actionpack (= 6.1.4.6)
activesupport (= 6.1.4.4) activesupport (= 6.1.4.6)
method_source method_source
rake (>= 0.13) rake (>= 0.13)
thor (~> 1.0) thor (~> 1.0)
@ -1580,7 +1580,7 @@ DEPENDENCIES
rack-oauth2 (~> 1.16.0) rack-oauth2 (~> 1.16.0)
rack-proxy (~> 0.6.0) rack-proxy (~> 0.6.0)
rack-timeout (~> 0.5.1) rack-timeout (~> 0.5.1)
rails (~> 6.1.4.4) rails (~> 6.1.4.6)
rails-controller-testing rails-controller-testing
rails-i18n (~> 6.0) rails-i18n (~> 6.0)
rainbow (~> 3.0) rainbow (~> 3.0)

View File

@ -266,7 +266,7 @@ export default {
> >
<span class="gl-font-lg">&middot;</span> <span class="gl-font-lg">&middot;</span>
<span data-testid="vsa-stage-event-date"> <span data-testid="vsa-stage-event-date">
{{ s__('OpenedNDaysAgo|Opened') }} {{ s__('OpenedNDaysAgo|Created') }}
<gl-link class="gl-text-black-normal" :href="item.url">{{ <gl-link class="gl-text-black-normal" :href="item.url">{{
item.createdAt item.createdAt
}}</gl-link> }}</gl-link>

View File

@ -102,7 +102,7 @@ export default {
</div> </div>
</div> </div>
<span> <span>
{{ __('Opened') }} {{ __('Created') }}
<time-ago-tooltip data-testid="startTimeItem" :time="createdAt" /> <time-ago-tooltip data-testid="startTimeItem" :time="createdAt" />
{{ __('by') }} {{ __('by') }}
</span> </span>

View File

@ -40,7 +40,7 @@ module Projects
avenues = [authorizable_project_members] avenues = [authorizable_project_members]
avenues << if project.personal? avenues << if project.personal?
project_owner_acting_as_maintainer project_owner
else else
authorizable_group_members authorizable_group_members
end end
@ -85,9 +85,15 @@ module Projects
Member.from_union(members) Member.from_union(members)
end end
def project_owner_acting_as_maintainer # workaround until we migrate Project#owners to have membership with
# OWNER access level
def project_owner
user_id = project.namespace.owner.id user_id = project.namespace.owner.id
access_level = Gitlab::Access::MAINTAINER access_level = if ::Feature.enabled?(:personal_project_owner_with_owner_access, default_enabled: :yaml)
Gitlab::Access::OWNER
else
Gitlab::Access::MAINTAINER
end
Member Member
.from(generate_from_statement([[user_id, access_level]])) # rubocop: disable CodeReuse/ActiveRecord .from(generate_from_statement([[user_id, access_level]])) # rubocop: disable CodeReuse/ActiveRecord

View File

@ -8,8 +8,14 @@ module SelectForProjectAuthorization
select("projects.id AS project_id", "members.access_level") select("projects.id AS project_id", "members.access_level")
end end
def select_as_maintainer_for_project_authorization # workaround until we migrate Project#owners to have membership with
select(["projects.id AS project_id", "#{Gitlab::Access::MAINTAINER} AS access_level"]) # OWNER access level
def select_project_owner_for_project_authorization
if ::Feature.enabled?(:personal_project_owner_with_owner_access, default_enabled: :yaml)
select(["projects.id AS project_id", "#{Gitlab::Access::OWNER} AS access_level"])
else
select(["projects.id AS project_id", "#{Gitlab::Access::MAINTAINER} AS access_level"])
end
end end
end end
end end

View File

@ -459,7 +459,7 @@ class Project < ApplicationRecord
delegate :name, to: :owner, allow_nil: true, prefix: true delegate :name, to: :owner, allow_nil: true, prefix: true
delegate :members, to: :team, prefix: true delegate :members, to: :team, prefix: true
delegate :add_user, :add_users, to: :team delegate :add_user, :add_users, to: :team
delegate :add_guest, :add_reporter, :add_developer, :add_maintainer, :add_role, to: :team delegate :add_guest, :add_reporter, :add_developer, :add_maintainer, :add_owner, :add_role, to: :team
delegate :group_runners_enabled, :group_runners_enabled=, to: :ci_cd_settings, allow_nil: true delegate :group_runners_enabled, :group_runners_enabled=, to: :ci_cd_settings, allow_nil: true
delegate :root_ancestor, to: :namespace, allow_nil: true delegate :root_ancestor, to: :namespace, allow_nil: true
delegate :last_pipeline, to: :commit, allow_nil: true delegate :last_pipeline, to: :commit, allow_nil: true

View File

@ -23,6 +23,10 @@ class ProjectTeam
add_user(user, :maintainer, current_user: current_user) add_user(user, :maintainer, current_user: current_user)
end end
def add_owner(user, current_user: nil)
add_user(user, :owner, current_user: current_user)
end
def add_role(user, role, current_user: nil) def add_role(user, role, current_user: nil)
public_send(:"add_#{role}", user, current_user: current_user) # rubocop:disable GitlabSecurity/PublicSend public_send(:"add_#{role}", user, current_user: current_user) # rubocop:disable GitlabSecurity/PublicSend
end end
@ -103,7 +107,9 @@ class ProjectTeam
if group if group
group.owners group.owners
else else
[project.owner] # workaround until we migrate Project#owners to have membership with
# OWNER access level
Array.wrap(fetch_members(Gitlab::Access::OWNER)) | Array.wrap(project.owner)
end end
end end

View File

@ -4,7 +4,7 @@ module Members
module Projects module Projects
class CreatorService < Members::CreatorService class CreatorService < Members::CreatorService
def self.access_levels def self.access_levels
Gitlab::Access.sym_options Gitlab::Access.sym_options_with_owner
end end
private private

View File

@ -14,6 +14,7 @@ module NotificationRecipients
return [] unless project return [] unless project
add_recipients(project.team.maintainers, :mention, nil) add_recipients(project.team.maintainers, :mention, nil)
add_recipients(project.team.owners, :mention, nil)
end end
def acting_user def acting_user

View File

@ -147,7 +147,11 @@ module Projects
priority: UserProjectAccessChangedService::LOW_PRIORITY priority: UserProjectAccessChangedService::LOW_PRIORITY
) )
else else
@project.add_maintainer(@project.namespace.owner, current_user: current_user) if ::Feature.enabled?(:personal_project_owner_with_owner_access, default_enabled: :yaml)
@project.add_owner(@project.namespace.owner, current_user: current_user)
else
@project.add_maintainer(@project.namespace.owner, current_user: current_user)
end
end end
end end

View File

@ -2,6 +2,21 @@
- page_title _('Monitor Settings') - page_title _('Monitor Settings')
- breadcrumb_title _('Monitor Settings') - breadcrumb_title _('Monitor Settings')
.gl-alert.gl-alert-danger.gl-mb-5
- removal_epic_link_url = 'https://gitlab.com/groups/gitlab-org/-/epics/7188'
- removal_epic_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" class="gl-link">'.html_safe % { url: removal_epic_link_url }
- opstrace_link_url = 'https://gitlab.com/groups/gitlab-org/-/epics/6976'
- opstrace_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" class="gl-link">'.html_safe % { url: opstrace_link_url }
- link_end = '</a>'.html_safe
.gl-alert-container
= sprite_icon('error', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
.gl-alert-content
.gl-alert-title
= s_('Deprecations|Feature deprecation and removal')
.gl-alert-body
%p
= html_escape(s_('Deprecations|The metrics, logs and tracing features were deprecated in GitLab 14.7, and are %{removal_link_start} scheduled for removal %{link_end} in GitLab 15.0. For information on a possible replacement, %{opstrace_link_start} learn more about Opstrace %{link_end}.')) % {removal_link_start: removal_epic_link_start, opstrace_link_start: opstrace_link_start, link_end: link_end }
= render 'projects/settings/operations/metrics_dashboard' = render 'projects/settings/operations/metrics_dashboard'
= render 'projects/settings/operations/tracing' = render 'projects/settings/operations/tracing'
= render 'projects/settings/operations/error_tracking' = render 'projects/settings/operations/error_tracking'

View File

@ -9,4 +9,5 @@ raise "METRICS_SERVER_TARGET cannot be blank" if target.blank?
metrics_dir = ENV["prometheus_multiproc_dir"] || File.absolute_path("tmp/prometheus_multiproc_dir/#{target}") metrics_dir = ENV["prometheus_multiproc_dir"] || File.absolute_path("tmp/prometheus_multiproc_dir/#{target}")
wipe_metrics_dir = Gitlab::Utils.to_boolean(ENV['WIPE_METRICS_DIR']) || false wipe_metrics_dir = Gitlab::Utils.to_boolean(ENV['WIPE_METRICS_DIR']) || false
Process.wait(MetricsServer.spawn(target, metrics_dir: metrics_dir, wipe_metrics_dir: wipe_metrics_dir)) server = MetricsServer.new(target, metrics_dir, wipe_metrics_dir)
server.start

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343917
milestone: '14.5' milestone: '14.5'
type: development type: development
group: group::container security group: group::container security
default_enabled: false default_enabled: true

View File

@ -0,0 +1,8 @@
---
name: personal_project_owner_with_owner_access
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78193
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351919
milestone: '14.8'
type: development
group: group::workspace
default_enabled: false

View File

@ -1,8 +0,0 @@
---
name: prohibit_hexadecimal_branch_names
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/28439ca4b1dd14f22a5a6ad14530f6bf1046f8bc
rollout_issue_url:
milestone: '12.10'
type: development
group: group::source code
default_enabled: true

View File

@ -185,17 +185,17 @@ This allows you to create a single file. For creating multiple files with a sing
POST /projects/:id/repository/files/:file_path POST /projects/:id/repository/files/:file_path
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
|------------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------| | ---------------- | -------------- | -------- | ----------- |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user | | `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
| `file_path` | string | yes | URL encoded full path to new file. Ex. `lib%2Fclass%2Erb`. | | `file_path` | string | yes | URL-encoded full path to new file. For example: `lib%2Fclass%2Erb`. |
| `branch` | string | yes | Name of the branch | | `branch` | string | yes | Name of the new branch to create. The commit is added to this branch. |
| `start_branch` | string | no | Name of the branch to start the new commit from | | `start_branch` | string | no | Name of the base branch to create the new branch from. |
| `encoding` | string | no | Change encoding to `base64`. Default is `text`. | | `encoding` | string | no | Change encoding to `base64`. Default is `text`. |
| `author_email` | string | no | Specify the commit author's email address | | `author_email` | string | no | The commit author's email address. |
| `author_name` | string | no | Specify the commit author's name | | `author_name` | string | no | The commit author's name. |
| `content` | string | yes | File content | | `content` | string | yes | The file's content. |
| `commit_message` | string | yes | Commit message | | `commit_message` | string | yes | The commit message. |
```shell ```shell
curl --request POST --header 'PRIVATE-TOKEN: <your_access_token>' \ curl --request POST --header 'PRIVATE-TOKEN: <your_access_token>' \
@ -222,18 +222,18 @@ This allows you to update a single file. For updating multiple files with a sing
PUT /projects/:id/repository/files/:file_path PUT /projects/:id/repository/files/:file_path
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
|------------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------| | ---------------- | -------------- | -------- | ----------- |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user | | `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `file_path` | string | yes | URL encoded full path to new file. Ex. `lib%2Fclass%2Erb`. | | `file_path` | string | yes | URL-encoded full path to new file. For example: `lib%2Fclass%2Erb`. |
| `branch` | string | yes | Name of the branch | | `branch` | string | yes | Name of the new branch to create. The commit is added to this branch. |
| `start_branch` | string | no | Name of the branch to start the new commit from | | `start_branch` | string | no | Name of the base branch to create the new branch from. |
| `encoding` | string | no | Change encoding to `base64`. Default is `text`. | | `encoding` | string | no | Change encoding to `base64`. Default is `text`. |
| `author_email` | string | no | Specify the commit author's email address | | `author_email` | string | no | The commit author's email address. |
| `author_name` | string | no | Specify the commit author's name | | `author_name` | string | no | The commit author's name. |
| `content` | string | yes | File content | | `content` | string | yes | The file's content. |
| `commit_message` | string | yes | Commit message | | `commit_message` | string | yes | The commit message. |
| `last_commit_id` | string | no | Last known file commit ID | | `last_commit_id` | string | no | Last known file commit ID. |
```shell ```shell
curl --request PUT --header 'PRIVATE-TOKEN: <your_access_token>' \ curl --request PUT --header 'PRIVATE-TOKEN: <your_access_token>' \
@ -270,16 +270,16 @@ This allows you to delete a single file. For deleting multiple files with a sing
DELETE /projects/:id/repository/files/:file_path DELETE /projects/:id/repository/files/:file_path
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
|------------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------| | ---------------- | -------------- | -------- | ----------- |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user | | `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
| `file_path` | string | yes | URL encoded full path to new file. Ex. `lib%2Fclass%2Erb`. | | `file_path` | string | yes | URL-encoded full path to new file. For example: `lib%2Fclass%2Erb`. |
| `branch` | string | yes | Name of the branch | | `branch` | string | yes | Name of the new branch to create. The commit is added to this branch. |
| `start_branch` | string | no | Name of the branch to start the new commit from | | `start_branch` | string | no | Name of the base branch to create the new branch from. |
| `author_email` | string | no | Specify the commit author's email address. | | `author_email` | string | no | The commit author's email address. |
| `author_name` | string | no | Specify the commit author's name. | | `author_name` | string | no | The commit author's name. |
| `commit_message` | string | yes | Commit message. | | `commit_message` | string | yes | The commit message. |
| `last_commit_id` | string | no | Last known file commit ID. | | `last_commit_id` | string | no | Last known file commit ID. |
```shell ```shell
curl --request DELETE --header 'PRIVATE-TOKEN: <your_access_token>' \ curl --request DELETE --header 'PRIVATE-TOKEN: <your_access_token>' \

View File

@ -398,3 +398,14 @@ end
1. Be sure to update the [GitLab CE/EE documentation](../administration/logs.md) and the [GitLab.com 1. Be sure to update the [GitLab CE/EE documentation](../administration/logs.md) and the [GitLab.com
runbooks](https://gitlab.com/gitlab-com/runbooks/blob/master/docs/logging/README.md). runbooks](https://gitlab.com/gitlab-com/runbooks/blob/master/docs/logging/README.md).
## Control logging visibility
An increase in the logs can cause a growing backlog of unacknowledged messages. When adding new log messages, make sure they don't increase the overall volume of logging by more than 10%.
### Deprecation notices
If the expected volume of deprecation notices is large:
- Only log them in the development environment.
- If needed, log them in the testing environment.

View File

@ -58,7 +58,7 @@ You can configure the following security controls:
- [API Fuzzing](../api_fuzzing/index.md) - [API Fuzzing](../api_fuzzing/index.md)
- Select **Enable API Fuzzing** to use API Fuzzing for the current project. For more details, read [API Fuzzing](../../../user/application_security/api_fuzzing/index.md#enable-web-api-fuzzing). - Select **Enable API Fuzzing** to use API Fuzzing for the current project. For more details, read [API Fuzzing](../../../user/application_security/api_fuzzing/index.md#enable-web-api-fuzzing).
- [Coverage Fuzzing](../coverage_fuzzing/index.md) - [Coverage Fuzzing](../coverage_fuzzing/index.md)
- Can be configured with `.gitlab-ci.yml`. For more details, read [Coverage Fuzzing](../../../user/application_security/coverage_fuzzing/index.md#configuration). - Can be configured with `.gitlab-ci.yml`. For more details, read [Coverage Fuzzing](../../../user/application_security/coverage_fuzzing/index.md#enable-coverage-guided-fuzz-testing).
## Compliance **(ULTIMATE)** ## Compliance **(ULTIMATE)**

View File

@ -22,8 +22,14 @@ The fuzz testing process:
1. Compiles the target application. 1. Compiles the target application.
1. Runs the instrumented application, using the `gitlab-cov-fuzz` tool. 1. Runs the instrumented application, using the `gitlab-cov-fuzz` tool.
1. Parses and analyzes the exception information output by the fuzzer. 1. Parses and analyzes the exception information output by the fuzzer.
1. Downloads the [corpus](../terminology/index.md#corpus) and crash events from previous pipelines. 1. Downloads the [corpus](../terminology/index.md#corpus) from either:
- The previous pipelines.
- If `COVFUZZ_USE_REGISTRY` is set to `true`, the [corpus registry](#corpus-registry).
1. Downloads crash events from previous pipeline.
1. Outputs the parsed crash events and data to the `gl-coverage-fuzzing-report.json` file. 1. Outputs the parsed crash events and data to the `gl-coverage-fuzzing-report.json` file.
1. Updates the corpus, either:
- In the job's pipeline.
- If `COVFUZZ_USE_REGISTRY` is set to `true`, in the corpus registry.
The results of the coverage-guided fuzz testing are available in the CI/CD pipeline. The results of the coverage-guided fuzz testing are available in the CI/CD pipeline.
@ -43,9 +49,20 @@ You can use the following fuzzing engines to test the specified languages.
| Python | [`pythonfuzz`](https://gitlab.com/gitlab-org/security-products/analyzers/fuzzers/pythonfuzz) | [pythonfuzz-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/pythonfuzz-fuzzing-example) | | Python | [`pythonfuzz`](https://gitlab.com/gitlab-org/security-products/analyzers/fuzzers/pythonfuzz) | [pythonfuzz-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/pythonfuzz-fuzzing-example) |
| AFL (any language that works on top of AFL) | [AFL](https://lcamtuf.coredump.cx/afl/) | [afl-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/afl-fuzzing-example) | | AFL (any language that works on top of AFL) | [AFL](https://lcamtuf.coredump.cx/afl/) | [afl-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/afl-fuzzing-example) |
## Configuration ## Confirm status of coverage-guided fuzz testing
To enable coverage-guided fuzz testing, edit the `.gitlab-ci.yml` file: To confirm the status of coverage-guided fuzz testing:
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Configuration**.
1. In the **Coverage Fuzzing** section the status is:
- **Not configured**
- **Enabled**
- A prompt to upgrade to GitLab Ultimate.
## Enable coverage-guided fuzz testing
To enable coverage-guided fuzz testing, edit `.gitlab-ci.yml`:
1. Add the `fuzz` stage to the list of stages. 1. Add the `fuzz` stage to the list of stages.
@ -98,10 +115,13 @@ Use the following variables to configure coverage-guided fuzz testing in your CI
| CI/CD variable | Description | | CI/CD variable | Description |
|---------------------------|---------------------------------------------------------------------------------| |---------------------------|---------------------------------------------------------------------------------|
| `COVFUZZ_ADDITIONAL_ARGS` | Arguments passed to `gitlab-cov-fuzz`. Used to customize the behavior of the underlying fuzzing engine. Read the fuzzing engine's documentation for a complete list of arguments. | | `COVFUZZ_ADDITIONAL_ARGS` | Arguments passed to `gitlab-cov-fuzz`. Used to customize the behavior of the underlying fuzzing engine. Read the fuzzing engine's documentation for a complete list of arguments. |
| `COVFUZZ_BRANCH` | The branch on which long-running fuzzing jobs are to be run. On all other branches, only fuzzing regression tests are run. Default: Repository's default branch. | | `COVFUZZ_BRANCH` | The branch on which long-running fuzzing jobs are to be run. On all other branches, only fuzzing regression tests are run. Default: Repository's default branch. |
| `COVFUZZ_SEED_CORPUS` | Path to a seed corpus directory. Default: empty. | | `COVFUZZ_SEED_CORPUS` | Path to a seed corpus directory. Default: empty. |
| `COVFUZZ_URL_PREFIX` | Path to the `gitlab-cov-fuzz` repository cloned for use with an offline environment. You should only change this value when using an offline environment. Default: `https://gitlab.com/gitlab-org/security-products/analyzers/gitlab-cov-fuzz/-/raw`. | | `COVFUZZ_URL_PREFIX` | Path to the `gitlab-cov-fuzz` repository cloned for use with an offline environment. You should only change this value when using an offline environment. Default: `https://gitlab.com/gitlab-org/security-products/analyzers/gitlab-cov-fuzz/-/raw`. |
| `COVFUZZ_USE_REGISTRY` | Set to `true` to have the corpus stored in the GitLab corpus registry. The variables `COVFUZZ_CORPUS_NAME` and `COVFUZZ_GITLAB_TOKEN` are required if this variable is set to `true`. Default: `false`. [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5017) in GitLab 14.8. |
| `COVFUZZ_CORPUS_NAME` | Name of the corpus to be used in the job. [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5017) in GitLab 14.8. |
| `COVFUZZ_GITLAB_TOKEN` | Environment variable configured with [Personal Access Token](../../../user/profile/personal_access_tokens.md#create-a-personal-access-token) with API read/write access. [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5017) in GitLab 14.8. |
#### Seed corpus #### Seed corpus
@ -122,7 +142,91 @@ Each fuzzing step outputs these artifacts:
You can download the JSON report file from the CI/CD pipelines page. For more information, see You can download the JSON report file from the CI/CD pipelines page. For more information, see
[Downloading artifacts](../../../ci/pipelines/job_artifacts.md#download-job-artifacts). [Downloading artifacts](../../../ci/pipelines/job_artifacts.md#download-job-artifacts).
### Coverage-guided fuzz testing report ## Corpus registry
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5017) in GitLab 14.8.
FLAG:
On self-managed GitLab, by default this feature is available. To hide the feature, ask an
administrator to [disable the feature flags](../../../administration/feature_flags.md) named
`corpus_management` and `corpus_management_ui`. On GitLab.com, this feature is available.
The corpus registry is a library of corpuses. Corpuses in a project's registry are available to
all jobs in that project. A project-wide registry is a more efficient way to manage corpuses than
the default option of one corpus per job.
The corpus registry uses the package registry to store the project's corpuses. Corpuses stored in
the registry are hidden to ensure data integrity.
In the GitLab UI, with corpus management you can:
- View details of the corpus registry.
- Download a corpus.
- Delete a corpus.
- Create a new corpus.
When you download a corpus, the file is named `artifacts.zip`, regardless of the filename used when
the corpus was initially uploaded. This file contains only the corpus, which is different to the
artifacts files you can download from the CI/CD pipeline.
### View details of the corpus registry
To view details of the corpus registry:
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Configuration**.
1. In the **Coverage Fuzzing** section, select **Manage corpus**.
### Create a corpus in the corpus registry
To create a corpus in the corpus registry, either:
- Create a corpus in a pipeline
- Upload an existing corpus file
#### Create a corpus in a pipeline
To create a corpus in a pipeline:
1. In the `.gitlab-ci.yml` file, edit the `my_fuzz_target` job.
1. Set the following variables:
- Set `COVFUZZ_USE_REGISTRY` to `true`.
- Set `COVFUZZ_CORPUS_NAME` to name the corpus.
- Set `COVFUZZ_GITLAB_TOKEN` to the value of the personal access token.
After the `my_fuzz_target` job runs, the corpus is stored in the corpus registry, with the name
provided by the `COVFUZZ_CORPUS_NAME` variable. The corpus is updated on every pipeline run.
#### Upload a corpus file
To upload an existing corpus file:
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Configuration**.
1. In the **Coverage Fuzzing** section, select **Manage corpus**.
1. Select **New corpus**.
1. Complete the fields.
1. Select **Upload file**.
1. Select **Add**.
You can now reference the corpus in the `.gitlab-ci.yml` file. Ensure the value used in the
`COVFUZZ_CORPUS_NAME` variable matches exactly the name given to the uploaded corpus file.
### Use a corpus stored in the corpus registry
To use a corpus stored in the corpus registry, you must reference it by its name. To confirm the
name of the relevant corpus, view details of the corpus registry.
Prerequisites:
- [Enable coverage-guide fuzz testing](#enable-coverage-guided-fuzz-testing) in the project.
1. Set the following variables in the `.gitlab-ci.yml` file:
- Set `COVFUZZ_USE_REGISTRY` to `true`.
- Set `COVFUZZ_CORPUS_NAME` to the name of the corpus.
- Set `COVFUZZ_GITLAB_TOKEN` to the value of the personal access token.
## Coverage-guided fuzz testing report
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220062) in GitLab 13.3 as an [Alpha feature](../../../policy/alpha-beta-support.md#alpha-features). > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220062) in GitLab 13.3 as an [Alpha feature](../../../policy/alpha-beta-support.md#alpha-features).
@ -255,3 +359,16 @@ vulnerability:
engines listed in [Supported fuzzing engines and languages](#supported-fuzzing-engines-and-languages). engines listed in [Supported fuzzing engines and languages](#supported-fuzzing-engines-and-languages).
<!-- vale gitlab.Acronyms = YES --> <!-- vale gitlab.Acronyms = YES -->
## Troubleshooting
### Error "Unable to extract corpus folder from artifacts zip file"
If you see this error message, and `COVFUZZ_USE_REGISTRY` is set to `true`, ensure that the uploaded
corpus file extracts into a folder named `corpus`.
### Error "400 Bad request - Duplicate package is not allowed"
If you see this error message when running the fuzzing job with `COVFUZZ_USE_REGISTRY` set to `true`,
ensure that duplicates are allowed. For more details, see
[duplicate Generic packages](../../packages/generic_packages/#do-not-allow-duplicate-generic-packages).

View File

@ -196,7 +196,7 @@ see the [related epic](https://gitlab.com/groups/gitlab-org/-/epics/4739).
### View vulnerabilities in cluster images **(ULTIMATE)** ### View vulnerabilities in cluster images **(ULTIMATE)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8 [with a flag](../../../../administration/feature_flags.md) named `cluster_vulnerabilities`. Disabled by default. > [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8 [with a flag](../../../../administration/feature_flags.md) named `cluster_vulnerabilities`. Enabled by default.
Users with at least the [Developer role](../../../permissions.md) Users with at least the [Developer role](../../../permissions.md)
can view cluster vulnerabilities. You can access them through the [vulnerability report](../../../application_security/vulnerabilities/index.md) can view cluster vulnerabilities. You can access them through the [vulnerability report](../../../application_security/vulnerabilities/index.md)

View File

@ -280,10 +280,15 @@ To view the activity feed in Atom format, select the
## Share a group with another group ## Share a group with another group
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18328) in GitLab 12.7. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18328) in GitLab 12.7.
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 13.11 from a form to a modal window [with a flag](../feature_flags.md). Disabled by default.
> - Modal window [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 14.8.
NOTE: FLAG:
In GitLab 13.11, you can [replace this form with a modal window](#share-a-group-modal-window). On self-managed GitLab, by default the modal window feature is available.
To hide the feature, ask an administrator to [disable the feature flag](../../administration/feature_flags.md)
named `invite_members_group_modal`.
On GitLab.com, this feature is available.
Similar to how you [share a project with a group](../project/members/share_project_with_groups.md), Similar to how you [share a project with a group](../project/members/share_project_with_groups.md),
you can share a group with another group. Members get direct access you can share a group with another group. Members get direct access
@ -293,35 +298,14 @@ To share a given group, for example, `Frontend` with another group, for example,
`Engineering`: `Engineering`:
1. Go to the `Frontend` group. 1. Go to the `Frontend` group.
1. From the left menu, select **Group information > Members**. 1. On the left sidebar, select **Group information > Members**.
1. Select the **Invite group** tab. 1. Select **Invite a group**.
1. In the **Select a group to invite** list, select `Engineering`. 1. In the **Select a group to invite** list, select `Engineering`.
1. For the **Max role**, select a [role](../permissions.md). 1. Select a [role](../permissions.md).
1. Select **Invite**. 1. Select **Invite**.
All the members of the `Engineering` group are added to the `Frontend` group. All the members of the `Engineering` group are added to the `Frontend` group.
### Share a group modal window
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 13.11.
> - [Deployed behind a feature flag](../feature_flags.md), disabled by default.
> - Enabled on GitLab.com.
> - Recommended for production use.
> - Replaces the existing form with buttons to open a modal window.
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](../project/members/index.md#enable-or-disable-modal-window).
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
In GitLab 13.11, you can optionally replace the sharing form with a modal window.
To share a group after enabling this feature:
1. Go to your group's page.
1. On the left sidebar, go to **Group information > Members**, and then select **Invite a group**.
1. Select a group, and select a **Max role**.
1. Optional. Select an **Access expiration date**.
1. Select **Invite**.
## Manage group memberships via LDAP **(PREMIUM SELF)** ## Manage group memberships via LDAP **(PREMIUM SELF)**
Group syncing allows LDAP groups to be mapped to GitLab groups. This provides more control over per-group user management. To configure group syncing, edit the `group_base` **DN** (`'OU=Global Groups,OU=GitLab INT,DC=GitLab,DC=org'`). This **OU** contains all groups that will be associated with GitLab groups. Group syncing allows LDAP groups to be mapped to GitLab groups. This provides more control over per-group user management. To configure group syncing, edit the `group_base` **DN** (`'OU=Global Groups,OU=GitLab INT,DC=GitLab,DC=org'`). This **OU** contains all groups that will be associated with GitLab groups.

View File

@ -33,14 +33,27 @@ usernames. A GitLab administrator can configure the GitLab instance to
## Project members permissions ## Project members permissions
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/219299) in GitLab 14.8, personal namespace owners appear with Owner role in new projects in their namespace. Introduced [with a flag](../administration/feature_flags.md) named `personal_project_owner_with_owner_access`. Disabled by default.
FLAG:
On self-managed GitLab, personal namespace owners appearing with the Owner role in new projects in their namespace is disabled. To make it available,
ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `personal_project_owner_with_owner_access`.
The feature is not ready for production use.
On GitLab.com, this feature is not available.
A user's role determines what permissions they have on a project. The Owner role provides all permissions but is A user's role determines what permissions they have on a project. The Owner role provides all permissions but is
available only: available only:
- For group owners. The role is inherited for a group's projects. - For group owners. The role is inherited for a group's projects.
- For Administrators. - For Administrators.
Personal namespace owners have the same permissions as an Owner, but are displayed with the Maintainer role on projects created in their personal namespace. Personal [namespace](group/index.md#namespaces) owners:
For more information, see [projects members documentation](project/members/index.md).
- Are displayed as having the Maintainer role on projects in the namespace, but have the same permissions as a user with the Owner role.
- (Disabled by default) In GitLab 14.8 and later, for new projects in the namespace, are displayed as having the Owner role.
For more information about how to manage project members, see
[members of a project](project/members/index.md).
The following table lists project permissions available for each role: The following table lists project permissions available for each role:

View File

@ -12,6 +12,15 @@ Each member gets a role, which determines what they can do in the project.
## Add users to a project ## Add users to a project
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 13.11 from a form to a modal window [with a flag](../../feature_flags.md). Disabled by default.
> - Modal window [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 14.8.
FLAG:
On self-managed GitLab, by default the modal window feature is available.
To hide the feature, ask an administrator to [disable the feature flag](../../../administration/feature_flags.md)
named `invite_members_group_modal`.
On GitLab.com, this feature is available.
Add users to a project so they become members and have permission Add users to a project so they become members and have permission
to perform actions. to perform actions.
@ -21,11 +30,12 @@ Prerequisite:
To add a user to a project: To add a user to a project:
1. Go to your project and select **Project information > Members**. 1. On the top bar, select **Menu > Projects** and find your project.
1. On the **Invite member** tab, under **GitLab member or Email address**, type the username or email address. 1. On the left sidebar, select **Project information > Members**.
In GitLab 13.11 and later, you can [replace this form with a modal window](#add-a-member-modal-window). 1. Select **Invite members**.
1. Select a [role](../../permissions.md). 1. Enter an email address and select a [role](../../permissions.md).
1. Optional. Choose an expiration date. On that date, the user can no longer access the project. 1. Optional. Select an **Access expiration date**.
On that date, the user can no longer access the project.
1. Select **Invite**. 1. Select **Invite**.
If the user has a GitLab account, they are added to the members list. If the user has a GitLab account, they are added to the members list.
@ -40,6 +50,15 @@ using the email address the invitation was sent to.
## Add groups to a project ## Add groups to a project
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 13.11 from a form to a modal window [with a flag](../../feature_flags.md). Disabled by default.
> - Modal window [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 14.8.
FLAG:
On self-managed GitLab, by default the modal window feature is available.
To hide the feature, ask an administrator to [disable the feature flag](../../../administration/feature_flags.md)
named `invite_members_group_modal`.
On GitLab.com, this feature is available.
When you add a group to a project, each user in the group gets access to the project. When you add a group to a project, each user in the group gets access to the project.
Each user's access is based on: Each user's access is based on:
@ -54,9 +73,10 @@ To add groups to a project:
1. On the top bar, select **Menu > Projects** and find your project. 1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**. 1. On the left sidebar, select **Project information > Members**.
1. On the **Invite group** tab, under **Select a group to invite**, choose a group. 1. Select **Invite a group**.
1. Select the highest max [role](../../permissions.md) for users in the group. 1. Select a group.
1. Optional. Choose an expiration date. On that date, the user can no longer access the project. 1. Select the highest [role](../../permissions.md) for users in the group.
1. Optional. Select an **Access expiration date**. On that date, the group can no longer access the project.
1. Select **Invite**. 1. Select **Invite**.
The members of the group are not displayed on the **Members** tab. The members of the group are not displayed on the **Members** tab.
@ -203,40 +223,3 @@ Prerequisite:
## Share a project with a group ## Share a project with a group
Instead of adding users one by one, you can [share a project with an entire group](share_project_with_groups.md). Instead of adding users one by one, you can [share a project with an entire group](share_project_with_groups.md).
### Add a member modal window
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 13.11 [with a flag](../../feature_flags.md). Disabled by default.
> - Replaces the existing form with buttons to open a modal window.
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/247208) in GitLab 14.8.
FLAG:
On self-managed GitLab, by default this feature is available.
To hide the feature, ask an administrator to [disable the feature flag](#enable-or-disable-modal-window).
On GitLab.com, this feature is available.
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**.
1. Select **Invite members**.
1. Enter an email address and select a role.
1. Optional. Select an **Access expiration date**.
1. Select **Invite**.
### Enable or disable modal window **(FREE SELF)**
The modal window for adding a member is under development and is ready for production use. It is
deployed behind a feature flag that is **enabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can enable it.
To enable it:
```ruby
Feature.enable(:invite_members_group_modal)
```
To disable it:
```ruby
Feature.disable(:invite_members_group_modal)
```

View File

@ -33,7 +33,13 @@ module Gitlab
MAINTAINER_SUBGROUP_ACCESS = 1 MAINTAINER_SUBGROUP_ACCESS = 1
class << self class << self
delegate :values, to: :options def values
if ::Feature.enabled?(:personal_project_owner_with_owner_access, default_enabled: :yaml)
options_with_owner.values
else
options.values
end
end
def all_values def all_values
options_with_owner.values options_with_owner.values

View File

@ -41,7 +41,6 @@ module Gitlab
def prohibited_branch_checks def prohibited_branch_checks
return if deletion? return if deletion?
return unless Feature.enabled?(:prohibit_hexadecimal_branch_names, project, default_enabled: true)
if branch_name =~ /\A\h{40}\z/ if branch_name =~ /\A\h{40}\z/
raise GitAccess::ForbiddenError, ERROR_MESSAGES[:prohibited_hex_branch_name] raise GitAccess::ForbiddenError, ERROR_MESSAGES[:prohibited_hex_branch_name]

View File

@ -22,7 +22,7 @@ module Gitlab
user.projects_with_active_memberships.select_for_project_authorization, user.projects_with_active_memberships.select_for_project_authorization,
# The personal projects of the user. # The personal projects of the user.
user.personal_projects.select_as_maintainer_for_project_authorization, user.personal_projects.select_project_owner_for_project_authorization,
# Projects that belong directly to any of the groups the user has # Projects that belong directly to any of the groups the user has
# access to. # access to.

View File

@ -12510,10 +12510,10 @@ msgstr ""
msgid "DevopsAdoption|At least one deploy" msgid "DevopsAdoption|At least one deploy"
msgstr "" msgstr ""
msgid "DevopsAdoption|At least one issue opened" msgid "DevopsAdoption|At least one issue created"
msgstr "" msgstr ""
msgid "DevopsAdoption|At least one merge request opened" msgid "DevopsAdoption|At least one merge request created"
msgstr "" msgstr ""
msgid "DevopsAdoption|At least one pipeline successfully run" msgid "DevopsAdoption|At least one pipeline successfully run"
@ -17143,7 +17143,7 @@ msgstr ""
msgid "Group: %{name}" msgid "Group: %{name}"
msgstr "" msgstr ""
msgid "GroupActivityMetrics|Issues opened" msgid "GroupActivityMetrics|Issues created"
msgstr "" msgstr ""
msgid "GroupActivityMetrics|Last 90 days" msgid "GroupActivityMetrics|Last 90 days"
@ -17152,7 +17152,7 @@ msgstr ""
msgid "GroupActivityMetrics|Members added" msgid "GroupActivityMetrics|Members added"
msgstr "" msgstr ""
msgid "GroupActivityMetrics|Merge Requests opened" msgid "GroupActivityMetrics|Merge Requests created"
msgstr "" msgstr ""
msgid "GroupActivityMetrics|Recent activity" msgid "GroupActivityMetrics|Recent activity"
@ -20164,6 +20164,9 @@ msgstr ""
msgid "IssueAnalytics|Assignees" msgid "IssueAnalytics|Assignees"
msgstr "" msgstr ""
msgid "IssueAnalytics|Created by"
msgstr ""
msgid "IssueAnalytics|Due date" msgid "IssueAnalytics|Due date"
msgstr "" msgstr ""
@ -20176,9 +20179,6 @@ msgstr ""
msgid "IssueAnalytics|Milestone" msgid "IssueAnalytics|Milestone"
msgstr "" msgstr ""
msgid "IssueAnalytics|Opened by"
msgstr ""
msgid "IssueAnalytics|Status" msgid "IssueAnalytics|Status"
msgstr "" msgstr ""
@ -20302,10 +20302,10 @@ msgstr ""
msgid "IssuesAnalytics|Avg/Month:" msgid "IssuesAnalytics|Avg/Month:"
msgstr "" msgstr ""
msgid "IssuesAnalytics|Issues opened" msgid "IssuesAnalytics|Issues created"
msgstr "" msgstr ""
msgid "IssuesAnalytics|Issues opened per month" msgid "IssuesAnalytics|Issues created per month"
msgstr "" msgstr ""
msgid "IssuesAnalytics|Last 12 months" msgid "IssuesAnalytics|Last 12 months"
@ -25429,7 +25429,7 @@ msgstr ""
msgid "Opened issues" msgid "Opened issues"
msgstr "" msgstr ""
msgid "OpenedNDaysAgo|Opened" msgid "OpenedNDaysAgo|Created"
msgstr "" msgstr ""
msgid "Opens in a new window" msgid "Opens in a new window"
@ -43756,9 +43756,6 @@ msgstr ""
msgid "open issue" msgid "open issue"
msgstr "" msgstr ""
msgid "opened %{timeAgo}"
msgstr ""
msgid "or" msgid "or"
msgstr "" msgstr ""

View File

@ -6,8 +6,23 @@ require_relative 'dependencies'
class MetricsServer # rubocop:disable Gitlab/NamespacedClass class MetricsServer # rubocop:disable Gitlab/NamespacedClass
class << self class << self
def spawn(target, metrics_dir:, wipe_metrics_dir: false, trapped_signals: []) def spawn(target, metrics_dir:, gitlab_config: nil, wipe_metrics_dir: false)
raise "Target must be one of [puma,sidekiq]" unless %w(puma sidekiq).include?(target) ensure_valid_target!(target)
cmd = "#{Rails.root}/bin/metrics-server"
env = {
'METRICS_SERVER_TARGET' => target,
'WIPE_METRICS_DIR' => wipe_metrics_dir ? '1' : '0'
}
env['GITLAB_CONFIG'] = gitlab_config if gitlab_config
Process.spawn(env, cmd, err: $stderr, out: $stdout, pgroup: true).tap do |pid|
Process.detach(pid)
end
end
def fork(target, metrics_dir:, wipe_metrics_dir: false, reset_signals: [])
ensure_valid_target!(target)
pid = Process.fork pid = Process.fork
@ -15,7 +30,7 @@ class MetricsServer # rubocop:disable Gitlab/NamespacedClass
# Remove any custom signal handlers the parent process had registered, since we do # Remove any custom signal handlers the parent process had registered, since we do
# not want to inherit them, and Ruby forks with a `clone` that has the `CLONE_SIGHAND` # not want to inherit them, and Ruby forks with a `clone` that has the `CLONE_SIGHAND`
# flag set. # flag set.
Gitlab::ProcessManagement.modify_signals(trapped_signals, 'DEFAULT') Gitlab::ProcessManagement.modify_signals(reset_signals, 'DEFAULT')
server = MetricsServer.new(target, metrics_dir, wipe_metrics_dir) server = MetricsServer.new(target, metrics_dir, wipe_metrics_dir)
# This rewrites /proc/cmdline, since otherwise tools like `top` will show the # This rewrites /proc/cmdline, since otherwise tools like `top` will show the
@ -29,6 +44,12 @@ class MetricsServer # rubocop:disable Gitlab/NamespacedClass
pid pid
end end
private
def ensure_valid_target!(target)
raise "Target must be one of [puma,sidekiq]" unless %w(puma sidekiq).include?(target)
end
end end
def initialize(target, metrics_dir, wipe_metrics_dir) def initialize(target, metrics_dir, wipe_metrics_dir)
@ -40,7 +61,7 @@ class MetricsServer # rubocop:disable Gitlab/NamespacedClass
def start def start
::Prometheus::Client.configure do |config| ::Prometheus::Client.configure do |config|
config.multiprocess_files_dir = @metrics_dir config.multiprocess_files_dir = @metrics_dir
config.pid_provider = proc { "#{@target}_exporter" } config.pid_provider = proc { name }
end end
FileUtils.mkdir_p(@metrics_dir, mode: 0700) FileUtils.mkdir_p(@metrics_dir, mode: 0700)
@ -57,16 +78,18 @@ class MetricsServer # rubocop:disable Gitlab/NamespacedClass
case @target case @target
when 'puma' when 'puma'
Gitlab::Metrics::Exporter::WebExporter.instance(**default_opts) Gitlab::Metrics::Exporter::WebExporter.instance(**default_opts)
else when 'sidekiq'
exporter_class = "Gitlab::Metrics::Exporter::#{@target.camelize}Exporter".constantize
settings = Settings.new(Settings.monitoring[name]) settings = Settings.new(Settings.monitoring[name])
exporter_class.instance(settings, **default_opts) Gitlab::Metrics::Exporter::SidekiqExporter.instance(settings, **default_opts)
end end
exporter.start exporter.start
end end
def name def name
"#{@target}_exporter" case @target
when 'puma' then 'web_exporter'
when 'sidekiq' then 'sidekiq_exporter'
end
end end
end end

View File

@ -59,8 +59,8 @@
"@gitlab/svgs": "2.5.0", "@gitlab/svgs": "2.5.0",
"@gitlab/ui": "36.1.0", "@gitlab/ui": "36.1.0",
"@gitlab/visual-review-tools": "1.6.1", "@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "6.1.4-1", "@rails/actioncable": "6.1.4-6",
"@rails/ujs": "6.1.4-1", "@rails/ujs": "6.1.4-6",
"@sentry/browser": "5.30.0", "@sentry/browser": "5.30.0",
"@sourcegraph/code-host-integration": "0.0.60", "@sourcegraph/code-host-integration": "0.0.60",
"@tiptap/core": "^2.0.0-beta.171", "@tiptap/core": "^2.0.0-beta.171",

View File

@ -3,7 +3,7 @@
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'gitlab-qa', require: 'gitlab/qa' gem 'gitlab-qa', require: 'gitlab/qa'
gem 'activesupport', '~> 6.1.4.1' # This should stay in sync with the root's Gemfile gem 'activesupport', '~> 6.1.4.6' # This should stay in sync with the root's Gemfile
gem 'allure-rspec', '~> 2.15.0' gem 'allure-rspec', '~> 2.15.0'
gem 'capybara', '~> 3.35.0' gem 'capybara', '~> 3.35.0'
gem 'capybara-screenshot', '~> 1.0.23' gem 'capybara-screenshot', '~> 1.0.23'

View File

@ -2,7 +2,7 @@ GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
abstract_type (0.0.7) abstract_type (0.0.7)
activesupport (6.1.4.1) activesupport (6.1.4.6)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
@ -226,6 +226,7 @@ GEM
rack (2.2.3) rack (2.2.3)
rack-test (1.1.0) rack-test (1.1.0)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
rainbow (3.0.0)
rake (13.0.6) rake (13.0.6)
regexp_parser (2.1.1) regexp_parser (2.1.1)
representable (3.1.1) representable (3.1.1)
@ -321,7 +322,7 @@ PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
activesupport (~> 6.1.4.1) activesupport (~> 6.1.4.6)
airborne (~> 0.3.4) airborne (~> 0.3.4)
allure-rspec (~> 2.15.0) allure-rspec (~> 2.15.0)
capybara (~> 3.35.0) capybara (~> 3.35.0)
@ -339,6 +340,7 @@ DEPENDENCIES
parallel (~> 1.19) parallel (~> 1.19)
parallel_tests (~> 2.29) parallel_tests (~> 2.29)
pry-byebug (~> 3.5.1) pry-byebug (~> 3.5.1)
rainbow (~> 3.0.0)
rake (~> 13) rake (~> 13)
rest-client (~> 2.1.0) rest-client (~> 2.1.0)
rotp (~> 3.1.0) rotp (~> 3.1.0)

View File

@ -191,11 +191,11 @@ module Gitlab
return unless metrics_server_enabled? return unless metrics_server_enabled?
@logger.info("Starting metrics server on port #{sidekiq_exporter_port}") @logger.info("Starting metrics server on port #{sidekiq_exporter_port}")
@metrics_server_pid = MetricsServer.spawn( @metrics_server_pid = MetricsServer.fork(
'sidekiq', 'sidekiq',
metrics_dir: @metrics_dir, metrics_dir: @metrics_dir,
wipe_metrics_dir: wipe_metrics_dir, wipe_metrics_dir: wipe_metrics_dir,
trapped_signals: TERMINATE_SIGNALS + FORWARD_SIGNALS reset_signals: TERMINATE_SIGNALS + FORWARD_SIGNALS
) )
end end

View File

@ -38,13 +38,7 @@ RSpec.describe 'bin/metrics-server', :aggregate_failures do
config_file.write(YAML.dump(config)) config_file.write(YAML.dump(config))
config_file.close config_file.close
env = { @pid = MetricsServer.spawn(target, metrics_dir: metrics_dir, gitlab_config: config_file.path, wipe_metrics_dir: true)
'GITLAB_CONFIG' => config_file.path,
'METRICS_SERVER_TARGET' => target,
'WIPE_METRICS_DIR' => '1',
'prometheus_multiproc_dir' => metrics_dir
}
@pid = Process.spawn(env, 'bin/metrics-server', pgroup: true)
end end
after do after do

View File

@ -303,7 +303,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
end end
it 'does not start a sidekiq metrics server' do it 'does not start a sidekiq metrics server' do
expect(MetricsServer).not_to receive(:spawn) expect(MetricsServer).not_to receive(:fork)
cli.run(%w(foo)) cli.run(%w(foo))
end end
@ -320,7 +320,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
end end
it 'does not start a sidekiq metrics server' do it 'does not start a sidekiq metrics server' do
expect(MetricsServer).not_to receive(:spawn) expect(MetricsServer).not_to receive(:fork)
cli.run(%w(foo)) cli.run(%w(foo))
end end
@ -350,7 +350,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
end end
it 'does not start a sidekiq metrics server' do it 'does not start a sidekiq metrics server' do
expect(MetricsServer).not_to receive(:spawn) expect(MetricsServer).not_to receive(:fork)
cli.run(%w(foo)) cli.run(%w(foo))
end end
@ -376,7 +376,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
end end
it 'does not start a sidekiq metrics server' do it 'does not start a sidekiq metrics server' do
expect(MetricsServer).not_to receive(:spawn) expect(MetricsServer).not_to receive(:fork)
cli.run(%w(foo)) cli.run(%w(foo))
end end
@ -406,9 +406,9 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
specify do specify do
if start_metrics_server if start_metrics_server
expect(MetricsServer).to receive(:spawn).with('sidekiq', metrics_dir: metrics_dir, wipe_metrics_dir: true, trapped_signals: trapped_signals) expect(MetricsServer).to receive(:fork).with('sidekiq', metrics_dir: metrics_dir, wipe_metrics_dir: true, reset_signals: trapped_signals)
else else
expect(MetricsServer).not_to receive(:spawn) expect(MetricsServer).not_to receive(:fork)
end end
cli.run(%w(foo)) cli.run(%w(foo))
@ -421,7 +421,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
let(:sidekiq_exporter_enabled) { true } let(:sidekiq_exporter_enabled) { true }
it 'does not start the server' do it 'does not start the server' do
expect(MetricsServer).not_to receive(:spawn) expect(MetricsServer).not_to receive(:fork)
cli.run(%w(foo --dryrun)) cli.run(%w(foo --dryrun))
end end
@ -434,7 +434,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
before do before do
allow(cli).to receive(:sleep).with(a_kind_of(Numeric)) allow(cli).to receive(:sleep).with(a_kind_of(Numeric))
allow(MetricsServer).to receive(:spawn).and_return(99) allow(MetricsServer).to receive(:fork).and_return(99)
cli.start_metrics_server cli.start_metrics_server
end end
@ -453,8 +453,8 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo
allow(Gitlab::ProcessManagement).to receive(:all_alive?).with(an_instance_of(Array)).and_return(false) allow(Gitlab::ProcessManagement).to receive(:all_alive?).with(an_instance_of(Array)).and_return(false)
allow(cli).to receive(:stop_metrics_server) allow(cli).to receive(:stop_metrics_server)
expect(MetricsServer).to receive(:spawn).with( expect(MetricsServer).to receive(:fork).with(
'sidekiq', metrics_dir: metrics_dir, wipe_metrics_dir: false, trapped_signals: trapped_signals 'sidekiq', metrics_dir: metrics_dir, wipe_metrics_dir: false, reset_signals: trapped_signals
) )
cli.start_loop cli.start_loop

View File

@ -665,7 +665,7 @@ RSpec.describe Projects::ProjectMembersController do
sign_in(user) sign_in(user)
end end
it 'does not create a member' do it 'creates a member' do
expect do expect do
post :create, params: { post :create, params: {
user_ids: stranger.id, user_ids: stranger.id,
@ -673,7 +673,9 @@ RSpec.describe Projects::ProjectMembersController do
access_level: Member::OWNER, access_level: Member::OWNER,
project_id: project project_id: project
} }
end.to change { project.members.count }.by(0) end.to change { project.members.count }.by(1)
expect(project.team_members).to include(user)
end end
end end

View File

@ -11,21 +11,40 @@ RSpec.describe Projects::Members::EffectiveAccessLevelFinder, '#execute' do
context 'for a personal project' do context 'for a personal project' do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
shared_examples_for 'includes access level of the owner of the project as Maintainer' do shared_examples_for 'includes access level of the owner of the project' do
it 'includes access level of the owner of the project as Maintainer' do context 'when personal_project_owner_with_owner_access feature flag is enabled' do
expect(subject).to( it 'includes access level of the owner of the project as Owner' do
contain_exactly( expect(subject).to(
hash_including( contain_exactly(
'user_id' => project.namespace.owner.id, hash_including(
'access_level' => Gitlab::Access::MAINTAINER 'user_id' => project.namespace.owner.id,
'access_level' => Gitlab::Access::OWNER
)
) )
) )
) end
end
context 'when personal_project_owner_with_owner_access feature flag is disabled' do
before do
stub_feature_flags(personal_project_owner_with_owner_access: false)
end
it 'includes access level of the owner of the project as Maintainer' do
expect(subject).to(
contain_exactly(
hash_including(
'user_id' => project.namespace.owner.id,
'access_level' => Gitlab::Access::MAINTAINER
)
)
)
end
end end
end end
context 'when the project owner is a member of the project' do context 'when the project owner is a member of the project' do
it_behaves_like 'includes access level of the owner of the project as Maintainer' it_behaves_like 'includes access level of the owner of the project'
end end
context 'when the project owner is not explicitly a member of the project' do context 'when the project owner is not explicitly a member of the project' do
@ -33,7 +52,7 @@ RSpec.describe Projects::Members::EffectiveAccessLevelFinder, '#execute' do
project.members.find_by(user_id: project.namespace.owner.id).destroy! project.members.find_by(user_id: project.namespace.owner.id).destroy!
end end
it_behaves_like 'includes access level of the owner of the project as Maintainer' it_behaves_like 'includes access level of the owner of the project'
end end
end end
@ -84,17 +103,32 @@ RSpec.describe Projects::Members::EffectiveAccessLevelFinder, '#execute' do
context 'for a project within a group' do context 'for a project within a group' do
context 'project in a root group' do context 'project in a root group' do
it 'includes access levels of users who are direct members of the parent group' do context 'includes access levels of users who are direct members of the parent group' do
group_member = create(:group_member, :developer, source: group) it 'when access level is developer' do
group_member = create(:group_member, :developer, source: group)
expect(subject).to( expect(subject).to(
include( include(
hash_including( hash_including(
'user_id' => group_member.user.id, 'user_id' => group_member.user.id,
'access_level' => Gitlab::Access::DEVELOPER 'access_level' => Gitlab::Access::DEVELOPER
)
) )
) )
) end
it 'when access level is owner' do
group_member = create(:group_member, :owner, source: group)
expect(subject).to(
include(
hash_including(
'user_id' => group_member.user.id,
'access_level' => Gitlab::Access::OWNER
)
)
)
end
end end
end end

View File

@ -40,15 +40,6 @@ RSpec.describe Gitlab::Checks::BranchCheck do
expect { subject.validate! }.not_to raise_error expect { subject.validate! }.not_to raise_error
end end
end end
context "the feature flag is disabled" do
it "doesn't prohibit a 40-character hexadecimal branch name" do
stub_feature_flags(prohibit_hexadecimal_branch_names: false)
allow(subject).to receive(:branch_name).and_return("267208abfe40e546f5e847444276f7d43a39503e")
expect { subject.validate! }.not_to raise_error
end
end
end end
context 'protected branches check' do context 'protected branches check' do

View File

@ -47,13 +47,15 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
end end
def loggable_data(count:, db_count: nil) def loggable_data(count:, db_count: nil)
keys = %w[ database_name = Ci::ApplicationRecord.connection.pool.db_config.name
keys = %W[
expensive_operation_duration_s expensive_operation_duration_s
expensive_operation_db_count expensive_operation_db_count
expensive_operation_db_primary_count expensive_operation_db_primary_count
expensive_operation_db_primary_duration_s expensive_operation_db_primary_duration_s
expensive_operation_db_main_count expensive_operation_db_#{database_name}_count
expensive_operation_db_main_duration_s expensive_operation_db_#{database_name}_duration_s
] ]
data = keys.each.with_object({}) do |key, accumulator| data = keys.each.with_object({}) do |key, accumulator|
@ -75,7 +77,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
end end
context 'with a single query' do context 'with a single query' do
let(:operation) { -> { Project.count } } let(:operation) { -> { Ci::Pipeline.count } }
it { is_expected.to eq(operation.call) } it { is_expected.to eq(operation.call) }

View File

@ -439,6 +439,12 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
end end
context 'when the migration is running against the ci database', if: Gitlab::Database.has_config?(:ci) do context 'when the migration is running against the ci database', if: Gitlab::Database.has_config?(:ci) do
around do |example|
Gitlab::Database::SharedModel.using_connection(::Ci::ApplicationRecord.connection) do
example.run
end
end
it_behaves_like 'helpers that enqueue background migrations', BackgroundMigration::CiDatabaseWorker, 'ci' it_behaves_like 'helpers that enqueue background migrations', BackgroundMigration::CiDatabaseWorker, 'ci'
end end

View File

@ -34,12 +34,28 @@ RSpec.describe Gitlab::ProjectAuthorizations do
.to include(owned_project.id, other_project.id, group_project.id) .to include(owned_project.id, other_project.id, group_project.id)
end end
it 'includes the correct access levels' do context 'when personal_project_owner_with_owner_access feature flag is enabled' do
mapping = map_access_levels(authorizations) it 'includes the correct access levels' do
mapping = map_access_levels(authorizations)
expect(mapping[owned_project.id]).to eq(Gitlab::Access::MAINTAINER) expect(mapping[owned_project.id]).to eq(Gitlab::Access::OWNER)
expect(mapping[other_project.id]).to eq(Gitlab::Access::REPORTER) expect(mapping[other_project.id]).to eq(Gitlab::Access::REPORTER)
expect(mapping[group_project.id]).to eq(Gitlab::Access::DEVELOPER) expect(mapping[group_project.id]).to eq(Gitlab::Access::DEVELOPER)
end
end
context 'when personal_project_owner_with_owner_access feature flag is disabled' do
before do
stub_feature_flags(personal_project_owner_with_owner_access: false)
end
it 'includes the correct access levels' do
mapping = map_access_levels(authorizations)
expect(mapping[owned_project.id]).to eq(Gitlab::Access::MAINTAINER)
expect(mapping[other_project.id]).to eq(Gitlab::Access::REPORTER)
expect(mapping[group_project.id]).to eq(Gitlab::Access::DEVELOPER)
end
end end
end end

View File

@ -36,13 +36,13 @@ RSpec.describe MetricsServer do # rubocop:disable RSpec/FilePath
%w(puma sidekiq).each do |target| %w(puma sidekiq).each do |target|
context "when targeting #{target}" do context "when targeting #{target}" do
describe '.spawn' do describe '.fork' do
context 'when in parent process' do context 'when in parent process' do
it 'forks into a new process and detaches it' do it 'forks into a new process and detaches it' do
expect(Process).to receive(:fork).and_return(99) expect(Process).to receive(:fork).and_return(99)
expect(Process).to receive(:detach).with(99) expect(Process).to receive(:detach).with(99)
described_class.spawn(target, metrics_dir: metrics_dir) described_class.fork(target, metrics_dir: metrics_dir)
end end
end end
@ -58,13 +58,47 @@ RSpec.describe MetricsServer do # rubocop:disable RSpec/FilePath
expect(server).to receive(:start) expect(server).to receive(:start)
end end
described_class.spawn(target, metrics_dir: metrics_dir) described_class.fork(target, metrics_dir: metrics_dir)
end end
it 'resets signal handlers from parent process' do it 'resets signal handlers from parent process' do
expect(Gitlab::ProcessManagement).to receive(:modify_signals).with(%i[A B], 'DEFAULT') expect(Gitlab::ProcessManagement).to receive(:modify_signals).with(%i[A B], 'DEFAULT')
described_class.spawn(target, metrics_dir: metrics_dir, trapped_signals: %i[A B]) described_class.fork(target, metrics_dir: metrics_dir, reset_signals: %i[A B])
end
end
end
describe '.spawn' do
let(:expected_env) do
{
'METRICS_SERVER_TARGET' => target,
'WIPE_METRICS_DIR' => '0'
}
end
it 'spawns a new server process and returns its PID' do
expect(Process).to receive(:spawn).with(
expected_env,
end_with('bin/metrics-server'),
hash_including(pgroup: true)
).and_return(99)
expect(Process).to receive(:detach).with(99)
pid = described_class.spawn(target, metrics_dir: metrics_dir)
expect(pid).to eq(99)
end
context 'when path to gitlab.yml is passed' do
it 'sets the GITLAB_CONFIG environment variable' do
expect(Process).to receive(:spawn).with(
expected_env.merge('GITLAB_CONFIG' => 'path/to/config/gitlab.yml'),
end_with('bin/metrics-server'),
hash_including(pgroup: true)
).and_return(99)
described_class.spawn(target, metrics_dir: metrics_dir, gitlab_config: 'path/to/config/gitlab.yml')
end end
end end
end end
@ -72,6 +106,14 @@ RSpec.describe MetricsServer do # rubocop:disable RSpec/FilePath
end end
context 'when targeting invalid target' do context 'when targeting invalid target' do
describe '.fork' do
it 'raises an error' do
expect { described_class.fork('unsupported', metrics_dir: metrics_dir) }.to(
raise_error('Target must be one of [puma,sidekiq]')
)
end
end
describe '.spawn' do describe '.spawn' do
it 'raises an error' do it 'raises an error' do
expect { described_class.spawn('unsupported', metrics_dir: metrics_dir) }.to( expect { described_class.spawn('unsupported', metrics_dir: metrics_dir) }.to(
@ -81,64 +123,86 @@ RSpec.describe MetricsServer do # rubocop:disable RSpec/FilePath
end end
end end
describe '#start' do shared_examples 'a metrics exporter' do |target, expected_name|
let(:exporter_class) { Class.new(Gitlab::Metrics::Exporter::BaseExporter) } describe '#start' do
let(:exporter_double) { double('fake_exporter', start: true) } let(:exporter_double) { double('exporter', start: true) }
let(:settings) { { "fake_exporter" => { "enabled" => true } } } let(:wipe_metrics_dir) { true }
subject(:metrics_server) { described_class.new('fake', metrics_dir, true)} subject(:metrics_server) { described_class.new(target, metrics_dir, wipe_metrics_dir) }
before do it 'configures ::Prometheus::Client' do
stub_const('Gitlab::Metrics::Exporter::FakeExporter', exporter_class) metrics_server.start
expect(exporter_class).to receive(:instance).with(
settings['fake_exporter'], gc_requests: true, synchronous: true
).and_return(exporter_double)
expect(Settings).to receive(:monitoring).and_return(settings)
end
it 'configures ::Prometheus::Client' do expect(prometheus_config.multiprocess_files_dir).to eq metrics_dir
metrics_server.start expect(::Prometheus::Client.configuration.pid_provider.call).to eq expected_name
end
expect(prometheus_config.multiprocess_files_dir).to eq metrics_dir it 'ensures that metrics directory exists in correct mode (0700)' do
expect(::Prometheus::Client.configuration.pid_provider.call).to eq 'fake_exporter' expect(FileUtils).to receive(:mkdir_p).with(metrics_dir, mode: 0700)
end
it 'ensures that metrics directory exists in correct mode (0700)' do metrics_server.start
expect(FileUtils).to receive(:mkdir_p).with(metrics_dir, mode: 0700) end
metrics_server.start context 'when wipe_metrics_dir is true' do
end it 'removes any old metrics files' do
FileUtils.touch("#{metrics_dir}/remove_this.db")
context 'when wipe_metrics_dir is true' do expect { metrics_server.start }.to change { Dir.empty?(metrics_dir) }.from(false).to(true)
subject(:metrics_server) { described_class.new('fake', metrics_dir, true)} end
end
it 'removes any old metrics files' do context 'when wipe_metrics_dir is false' do
FileUtils.touch("#{metrics_dir}/remove_this.db") let(:wipe_metrics_dir) { false }
expect { metrics_server.start }.to change { Dir.empty?(metrics_dir) }.from(false).to(true) it 'does not remove any old metrics files' do
FileUtils.touch("#{metrics_dir}/remove_this.db")
expect { metrics_server.start }.not_to change { Dir.empty?(metrics_dir) }.from(false)
end
end
it 'starts a metrics server' do
expect(exporter_double).to receive(:start)
metrics_server.start
end
it 'starts a RubySampler instance' do
expect(ruby_sampler_double).to receive(:start)
subject.start
end end
end end
context 'when wipe_metrics_dir is false' do describe '#name' do
subject(:metrics_server) { described_class.new('fake', metrics_dir, false)} let(:exporter_double) { double('exporter', start: true) }
it 'does not remove any old metrics files' do subject(:name) { described_class.new(target, metrics_dir, true).name }
FileUtils.touch("#{metrics_dir}/remove_this.db")
expect { metrics_server.start }.not_to change { Dir.empty?(metrics_dir) }.from(false) it { is_expected.to eq(expected_name) }
end
end
it 'starts a metrics server' do
expect(exporter_double).to receive(:start)
metrics_server.start
end
it 'starts a RubySampler instance' do
expect(ruby_sampler_double).to receive(:start)
subject.start
end end
end end
context 'for puma' do
before do
allow(Gitlab::Metrics::Exporter::WebExporter).to receive(:instance).with(
gc_requests: true, synchronous: true
).and_return(exporter_double)
end
it_behaves_like 'a metrics exporter', 'puma', 'web_exporter'
end
context 'for sidekiq' do
let(:settings) { { "sidekiq_exporter" => { "enabled" => true } } }
before do
allow(::Settings).to receive(:monitoring).and_return(settings)
allow(Gitlab::Metrics::Exporter::SidekiqExporter).to receive(:instance).with(
settings['sidekiq_exporter'], gc_requests: true, synchronous: true
).and_return(exporter_double)
end
it_behaves_like 'a metrics exporter', 'sidekiq', 'sidekiq_exporter'
end
end end

View File

@ -225,7 +225,7 @@ RSpec.describe ProjectTeam do
let_it_be(:maintainer) { create(:user) } let_it_be(:maintainer) { create(:user) }
let_it_be(:developer) { create(:user) } let_it_be(:developer) { create(:user) }
let_it_be(:guest) { create(:user) } let_it_be(:guest) { create(:user) }
let_it_be(:project) { create(:project, namespace: maintainer.namespace) } let_it_be(:project) { create(:project, group: create(:group)) }
let_it_be(:access_levels) { [Gitlab::Access::DEVELOPER, Gitlab::Access::MAINTAINER] } let_it_be(:access_levels) { [Gitlab::Access::DEVELOPER, Gitlab::Access::MAINTAINER] }
subject(:members_with_access_levels) { project.team.members_with_access_levels(access_levels) } subject(:members_with_access_levels) { project.team.members_with_access_levels(access_levels) }

View File

@ -3717,7 +3717,7 @@ RSpec.describe User do
context 'with min_access_level' do context 'with min_access_level' do
let!(:user) { create(:user) } let!(:user) { create(:user) }
let!(:project) { create(:project, :private, namespace: user.namespace) } let!(:project) { create(:project, :private, group: create(:group)) }
before do before do
project.add_developer(user) project.add_developer(user)

View File

@ -675,13 +675,30 @@ RSpec.describe API::Members do
end end
context 'adding owner to project' do context 'adding owner to project' do
it 'returns 403' do context 'when personal_project_owner_with_owner_access feature flag is enabled' do
expect do it 'returns created status' do
post api("/projects/#{project.id}/members", maintainer), expect do
params: { user_id: stranger.id, access_level: Member::OWNER } post api("/projects/#{project.id}/members", maintainer),
params: { user_id: stranger.id, access_level: Member::OWNER }
expect(response).to have_gitlab_http_status(:bad_request) expect(response).to have_gitlab_http_status(:created)
end.not_to change { project.members.count } end.to change { project.members.count }.by(1)
end
end
context 'when personal_project_owner_with_owner_access feature flag is disabled' do
before do
stub_feature_flags(personal_project_owner_with_owner_access: false)
end
it 'returns created status' do
expect do
post api("/projects/#{project.id}/members", maintainer),
params: { user_id: stranger.id, access_level: Member::OWNER }
expect(response).to have_gitlab_http_status(:bad_request)
end.not_to change { project.members.count }
end
end end
end end

View File

@ -40,7 +40,7 @@ RSpec.describe AuthorizedProjectUpdate::FindRecordsDueForRefreshService do
it 'is called' do it 'is called' do
ProjectAuthorization.delete_all ProjectAuthorization.delete_all
expect(callback).to receive(:call).with(project.id, Gitlab::Access::MAINTAINER).once expect(callback).to receive(:call).with(project.id, Gitlab::Access::OWNER).once
service.execute service.execute
end end
@ -60,20 +60,20 @@ RSpec.describe AuthorizedProjectUpdate::FindRecordsDueForRefreshService do
to_be_removed = [project2.id] to_be_removed = [project2.id]
to_be_added = [ to_be_added = [
{ user_id: user.id, project_id: project.id, access_level: Gitlab::Access::MAINTAINER } { user_id: user.id, project_id: project.id, access_level: Gitlab::Access::OWNER }
] ]
expect(service.execute).to eq([to_be_removed, to_be_added]) expect(service.execute).to eq([to_be_removed, to_be_added])
end end
it 'finds duplicate entries that has to be removed' do it 'finds duplicate entries that has to be removed' do
[Gitlab::Access::MAINTAINER, Gitlab::Access::REPORTER].each do |access_level| [Gitlab::Access::OWNER, Gitlab::Access::REPORTER].each do |access_level|
user.project_authorizations.create!(project: project, access_level: access_level) user.project_authorizations.create!(project: project, access_level: access_level)
end end
to_be_removed = [project.id] to_be_removed = [project.id]
to_be_added = [ to_be_added = [
{ user_id: user.id, project_id: project.id, access_level: Gitlab::Access::MAINTAINER } { user_id: user.id, project_id: project.id, access_level: Gitlab::Access::OWNER }
] ]
expect(service.execute).to eq([to_be_removed, to_be_added]) expect(service.execute).to eq([to_be_removed, to_be_added])
@ -85,7 +85,7 @@ RSpec.describe AuthorizedProjectUpdate::FindRecordsDueForRefreshService do
to_be_removed = [project.id] to_be_removed = [project.id]
to_be_added = [ to_be_added = [
{ user_id: user.id, project_id: project.id, access_level: Gitlab::Access::MAINTAINER } { user_id: user.id, project_id: project.id, access_level: Gitlab::Access::OWNER }
] ]
expect(service.execute).to eq([to_be_removed, to_be_added]) expect(service.execute).to eq([to_be_removed, to_be_added])
@ -143,16 +143,16 @@ RSpec.describe AuthorizedProjectUpdate::FindRecordsDueForRefreshService do
end end
it 'sets the keys to the project IDs' do it 'sets the keys to the project IDs' do
expect(hash.keys).to eq([project.id]) expect(hash.keys).to match_array([project.id])
end end
it 'sets the values to the access levels' do it 'sets the values to the access levels' do
expect(hash.values).to eq([Gitlab::Access::MAINTAINER]) expect(hash.values).to match_array([Gitlab::Access::OWNER])
end end
context 'personal projects' do context 'personal projects' do
it 'includes the project with the right access level' do it 'includes the project with the right access level' do
expect(hash[project.id]).to eq(Gitlab::Access::MAINTAINER) expect(hash[project.id]).to eq(Gitlab::Access::OWNER)
end end
end end
@ -242,7 +242,7 @@ RSpec.describe AuthorizedProjectUpdate::FindRecordsDueForRefreshService do
value = hash.values[0] value = hash.values[0]
expect(value.project_id).to eq(project.id) expect(value.project_id).to eq(project.id)
expect(value.access_level).to eq(Gitlab::Access::MAINTAINER) expect(value.access_level).to eq(Gitlab::Access::OWNER)
end end
end end
@ -267,7 +267,7 @@ RSpec.describe AuthorizedProjectUpdate::FindRecordsDueForRefreshService do
end end
it 'includes the access level for every row' do it 'includes the access level for every row' do
expect(row.access_level).to eq(Gitlab::Access::MAINTAINER) expect(row.access_level).to eq(Gitlab::Access::OWNER)
end end
end end
end end
@ -283,7 +283,7 @@ RSpec.describe AuthorizedProjectUpdate::FindRecordsDueForRefreshService do
rows = service.fresh_authorizations.to_a rows = service.fresh_authorizations.to_a
expect(rows.length).to eq(1) expect(rows.length).to eq(1)
expect(rows.first.access_level).to eq(Gitlab::Access::MAINTAINER) expect(rows.first.access_level).to eq(Gitlab::Access::OWNER)
end end
context 'every returned row' do context 'every returned row' do
@ -294,7 +294,7 @@ RSpec.describe AuthorizedProjectUpdate::FindRecordsDueForRefreshService do
end end
it 'includes the access level' do it 'includes the access level' do
expect(row.access_level).to eq(Gitlab::Access::MAINTAINER) expect(row.access_level).to eq(Gitlab::Access::OWNER)
end end
end end
end end

View File

@ -9,8 +9,8 @@ RSpec.describe Members::Projects::CreatorService do
end end
describe '.access_levels' do describe '.access_levels' do
it 'returns Gitlab::Access.sym_options' do it 'returns Gitlab::Access.sym_options_with_owner' do
expect(described_class.access_levels).to eq(Gitlab::Access.sym_options) expect(described_class.access_levels).to eq(Gitlab::Access.sym_options_with_owner)
end end
end end
end end

View File

@ -3312,7 +3312,7 @@ RSpec.describe NotificationService, :mailer do
describe "##{sym}" do describe "##{sym}" do
subject(:notify!) { notification.send(sym, domain) } subject(:notify!) { notification.send(sym, domain) }
it 'emails current watching maintainers' do it 'emails current watching maintainers and owners' do
expect(Notify).to receive(:"#{sym}_email").at_least(:once).and_call_original expect(Notify).to receive(:"#{sym}_email").at_least(:once).and_call_original
notify! notify!
@ -3410,7 +3410,7 @@ RSpec.describe NotificationService, :mailer do
reset_delivered_emails! reset_delivered_emails!
end end
it 'emails current watching maintainers' do it 'emails current watching maintainers and owners' do
notification.remote_mirror_update_failed(remote_mirror) notification.remote_mirror_update_failed(remote_mirror)
should_only_email(u_maintainer1, u_maintainer2, u_owner) should_only_email(u_maintainer1, u_maintainer2, u_owner)

View File

@ -116,14 +116,34 @@ RSpec.describe Projects::CreateService, '#execute' do
end end
context 'user namespace' do context 'user namespace' do
it 'creates a project in user namespace' do context 'when personal_project_owner_with_owner_access feature flag is enabled' do
project = create_project(user, opts) it 'creates a project in user namespace' do
project = create_project(user, opts)
expect(project).to be_valid expect(project).to be_valid
expect(project.first_owner).to eq(user) expect(project.first_owner).to eq(user)
expect(project.team.maintainers).to include(user) expect(project.team.maintainers).not_to include(user)
expect(project.namespace).to eq(user.namespace) expect(project.team.owners).to contain_exactly(user)
expect(project.project_namespace).to be_in_sync_with_project(project) expect(project.namespace).to eq(user.namespace)
expect(project.project_namespace).to be_in_sync_with_project(project)
end
end
context 'when personal_project_owner_with_owner_access feature flag is disabled' do
before do
stub_feature_flags(personal_project_owner_with_owner_access: false)
end
it 'creates a project in user namespace' do
project = create_project(user, opts)
expect(project).to be_valid
expect(project.first_owner).to eq(user)
expect(project.team.maintainers).to contain_exactly(user)
expect(project.team.owners).to contain_exactly(user)
expect(project.namespace).to eq(user.namespace)
expect(project.project_namespace).to be_in_sync_with_project(project)
end
end end
end end
@ -162,7 +182,7 @@ RSpec.describe Projects::CreateService, '#execute' do
expect(project).to be_persisted expect(project).to be_persisted
expect(project.owner).to eq(user) expect(project.owner).to eq(user)
expect(project.first_owner).to eq(user) expect(project.first_owner).to eq(user)
expect(project.team.maintainers).to contain_exactly(user) expect(project.team.owners).to contain_exactly(user)
expect(project.namespace).to eq(user.namespace) expect(project.namespace).to eq(user.namespace)
expect(project.project_namespace).to be_in_sync_with_project(project) expect(project.project_namespace).to be_in_sync_with_project(project)
end end

View File

@ -52,7 +52,7 @@ RSpec.describe Users::RefreshAuthorizedProjectsService do
it 'is called' do it 'is called' do
ProjectAuthorization.delete_all ProjectAuthorization.delete_all
expect(callback).to receive(:call).with(project.id, Gitlab::Access::MAINTAINER).once expect(callback).to receive(:call).with(project.id, Gitlab::Access::OWNER).once
service.execute service.execute
end end
@ -73,7 +73,7 @@ RSpec.describe Users::RefreshAuthorizedProjectsService do
to_be_removed = [project_authorization.project_id] to_be_removed = [project_authorization.project_id]
to_be_added = [ to_be_added = [
{ user_id: user.id, project_id: project.id, access_level: Gitlab::Access::MAINTAINER } { user_id: user.id, project_id: project.id, access_level: Gitlab::Access::OWNER }
] ]
expect(service).to receive(:update_authorizations) expect(service).to receive(:update_authorizations)
@ -83,14 +83,14 @@ RSpec.describe Users::RefreshAuthorizedProjectsService do
end end
it 'removes duplicate entries' do it 'removes duplicate entries' do
[Gitlab::Access::MAINTAINER, Gitlab::Access::REPORTER].each do |access_level| [Gitlab::Access::OWNER, Gitlab::Access::REPORTER].each do |access_level|
user.project_authorizations.create!(project: project, access_level: access_level) user.project_authorizations.create!(project: project, access_level: access_level)
end end
to_be_removed = [project.id] to_be_removed = [project.id]
to_be_added = [ to_be_added = [
{ user_id: user.id, project_id: project.id, access_level: Gitlab::Access::MAINTAINER } { user_id: user.id, project_id: project.id, access_level: Gitlab::Access::OWNER }
] ]
expect(service).to( expect(service).to(
receive(:update_authorizations) receive(:update_authorizations)
@ -103,7 +103,7 @@ RSpec.describe Users::RefreshAuthorizedProjectsService do
project_authorization = ProjectAuthorization.where( project_authorization = ProjectAuthorization.where(
project_id: project.id, project_id: project.id,
user_id: user.id, user_id: user.id,
access_level: Gitlab::Access::MAINTAINER) access_level: Gitlab::Access::OWNER)
expect(project_authorization).to exist expect(project_authorization).to exist
end end
@ -116,7 +116,7 @@ RSpec.describe Users::RefreshAuthorizedProjectsService do
to_be_removed = [project_authorization.project_id] to_be_removed = [project_authorization.project_id]
to_be_added = [ to_be_added = [
{ user_id: user.id, project_id: project.id, access_level: Gitlab::Access::MAINTAINER } { user_id: user.id, project_id: project.id, access_level: Gitlab::Access::OWNER }
] ]
expect(service).to receive(:update_authorizations) expect(service).to receive(:update_authorizations)

View File

@ -1436,15 +1436,15 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.10.2.tgz#0798c03351f0dea1a5a4cabddf26a55a7cbee590" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.10.2.tgz#0798c03351f0dea1a5a4cabddf26a55a7cbee590"
integrity sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ== integrity sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==
"@rails/actioncable@6.1.4-1": "@rails/actioncable@6.1.4-6":
version "6.1.4-1" version "6.1.4-6"
resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.1.4-1.tgz#69982e7f352d732f71fda0cc01b7ba8269c9945b" resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.1.4-6.tgz#22dd0f60e634f237f2a19d031f4e7afa26a924b4"
integrity sha512-b6sLoMop3gX22Wm2P5LPpKcZGwsf1ZoAGS+g1HrTrdlsZ/ENOKIBiSNnHOJajHwcYlF0TefBs7e7jIYZHVYihQ== integrity sha512-cGwo5AWlEg6Q5JeUl2r8cmgaSlJtgR9BOOGF7Yb1PyKOinuWod6PW6UeQLgXf+n2MNiWz+yqldb1m3+Aun/2lg==
"@rails/ujs@6.1.4-1": "@rails/ujs@6.1.4-6":
version "6.1.4-1" version "6.1.4-6"
resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.1.4-1.tgz#37507fe288a1c7c3a593602aa4dea42e5cb5797f" resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.1.4-6.tgz#244520a1580b5791cebc81471978bc6b3c8966c0"
integrity sha512-Fewm2wHk1n6Kf4E86dzzHDJOFg4EWcSHH3FsMEGs59bTdmf7099mjkOssOQtBqju4R39iaAOQNui7r8P+Q5Dgg== integrity sha512-j2ejw0ShVHiDWtq1Yv1PGX/GFCDiXX+5YiUY2Z17eeMJhQkxXeg3maQZkkT5OT/YBOI+jiWqtp03GM1Hdp/arA==
"@sentry/browser@5.30.0": "@sentry/browser@5.30.0":
version "5.30.0" version "5.30.0"