Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
861cc0c363
commit
a2ef93ba41
45 changed files with 371 additions and 565 deletions
2
Gemfile
2
Gemfile
|
@ -488,7 +488,7 @@ gem 'flipper', '~> 0.21.0'
|
||||||
gem 'flipper-active_record', '~> 0.21.0'
|
gem 'flipper-active_record', '~> 0.21.0'
|
||||||
gem 'flipper-active_support_cache_store', '~> 0.21.0'
|
gem 'flipper-active_support_cache_store', '~> 0.21.0'
|
||||||
gem 'unleash', '~> 3.2.2'
|
gem 'unleash', '~> 3.2.2'
|
||||||
gem 'gitlab-experiment', '~> 0.6.2'
|
gem 'gitlab-experiment', '~> 0.6.3'
|
||||||
|
|
||||||
# Structured logging
|
# Structured logging
|
||||||
gem 'lograge', '~> 0.5'
|
gem 'lograge', '~> 0.5'
|
||||||
|
|
|
@ -463,7 +463,7 @@ GEM
|
||||||
gitlab-dangerfiles (2.3.0)
|
gitlab-dangerfiles (2.3.0)
|
||||||
danger (>= 8.3.1)
|
danger (>= 8.3.1)
|
||||||
danger-gitlab (>= 8.0.0)
|
danger-gitlab (>= 8.0.0)
|
||||||
gitlab-experiment (0.6.2)
|
gitlab-experiment (0.6.3)
|
||||||
activesupport (>= 3.0)
|
activesupport (>= 3.0)
|
||||||
request_store (>= 1.0)
|
request_store (>= 1.0)
|
||||||
scientist (~> 1.6, >= 1.6.0)
|
scientist (~> 1.6, >= 1.6.0)
|
||||||
|
@ -1468,7 +1468,7 @@ DEPENDENCIES
|
||||||
github-markup (~> 1.7.0)
|
github-markup (~> 1.7.0)
|
||||||
gitlab-chronic (~> 0.10.5)
|
gitlab-chronic (~> 0.10.5)
|
||||||
gitlab-dangerfiles (~> 2.3.0)
|
gitlab-dangerfiles (~> 2.3.0)
|
||||||
gitlab-experiment (~> 0.6.2)
|
gitlab-experiment (~> 0.6.3)
|
||||||
gitlab-fog-azure-rm (~> 1.1.1)
|
gitlab-fog-azure-rm (~> 1.1.1)
|
||||||
gitlab-labkit (~> 0.21.0)
|
gitlab-labkit (~> 0.21.0)
|
||||||
gitlab-license (~> 2.0)
|
gitlab-license (~> 2.0)
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Members
|
|
||||||
module Mailgun
|
|
||||||
class PermanentFailuresController < ApplicationController
|
|
||||||
respond_to :json
|
|
||||||
|
|
||||||
skip_before_action :authenticate_user!
|
|
||||||
skip_before_action :verify_authenticity_token
|
|
||||||
|
|
||||||
before_action :ensure_feature_enabled!
|
|
||||||
before_action :authenticate_signature!
|
|
||||||
before_action :validate_invite_email!
|
|
||||||
|
|
||||||
feature_category :authentication_and_authorization
|
|
||||||
|
|
||||||
def create
|
|
||||||
webhook_processor.execute
|
|
||||||
|
|
||||||
head :ok
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def ensure_feature_enabled!
|
|
||||||
render_406 unless Gitlab::CurrentSettings.mailgun_events_enabled?
|
|
||||||
end
|
|
||||||
|
|
||||||
def authenticate_signature!
|
|
||||||
access_denied! unless valid_signature?
|
|
||||||
end
|
|
||||||
|
|
||||||
def valid_signature?
|
|
||||||
return false if Gitlab::CurrentSettings.mailgun_signing_key.blank?
|
|
||||||
|
|
||||||
# per this guide: https://documentation.mailgun.com/en/latest/user_manual.html#webhooks
|
|
||||||
digest = OpenSSL::Digest.new('SHA256')
|
|
||||||
data = [params.dig(:signature, :timestamp), params.dig(:signature, :token)].join
|
|
||||||
|
|
||||||
hmac_digest = OpenSSL::HMAC.hexdigest(digest, Gitlab::CurrentSettings.mailgun_signing_key, data)
|
|
||||||
|
|
||||||
ActiveSupport::SecurityUtils.secure_compare(params.dig(:signature, :signature), hmac_digest)
|
|
||||||
end
|
|
||||||
|
|
||||||
def validate_invite_email!
|
|
||||||
# permanent_failures webhook does not provide a way to filter failures, so we'll get them all on this endpoint
|
|
||||||
# and we only care about our invite_emails
|
|
||||||
render_406 unless payload[:tags]&.include?(::Members::Mailgun::INVITE_EMAIL_TAG)
|
|
||||||
end
|
|
||||||
|
|
||||||
def webhook_processor
|
|
||||||
::Members::Mailgun::ProcessWebhookService.new(payload)
|
|
||||||
end
|
|
||||||
|
|
||||||
def payload
|
|
||||||
@payload ||= params.permit!['event-data']
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_406
|
|
||||||
# failure to stop retries per https://documentation.mailgun.com/en/latest/user_manual.html#webhooks
|
|
||||||
head :not_acceptable
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -154,10 +154,10 @@ module Emails
|
||||||
end
|
end
|
||||||
|
|
||||||
def invite_email_headers
|
def invite_email_headers
|
||||||
if Gitlab::CurrentSettings.mailgun_events_enabled?
|
if Gitlab.dev_env_or_com?
|
||||||
{
|
{
|
||||||
'X-Mailgun-Tag' => ::Members::Mailgun::INVITE_EMAIL_TAG,
|
'X-Mailgun-Tag' => 'invite_email',
|
||||||
'X-Mailgun-Variables' => { ::Members::Mailgun::INVITE_EMAIL_TOKEN_KEY => @token }.to_json
|
'X-Mailgun-Variables' => { 'invite_token' => @token }.to_json
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Members
|
|
||||||
module Mailgun
|
|
||||||
INVITE_EMAIL_TAG = 'invite_email'
|
|
||||||
INVITE_EMAIL_TOKEN_KEY = :invite_token
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,39 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Members
|
|
||||||
module Mailgun
|
|
||||||
class ProcessWebhookService
|
|
||||||
ProcessWebhookServiceError = Class.new(StandardError)
|
|
||||||
|
|
||||||
def initialize(payload)
|
|
||||||
@payload = payload
|
|
||||||
end
|
|
||||||
|
|
||||||
def execute
|
|
||||||
@member = Member.find_by_invite_token(invite_token)
|
|
||||||
update_member_and_log if member
|
|
||||||
rescue ProcessWebhookServiceError => e
|
|
||||||
Gitlab::ErrorTracking.track_exception(e)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
attr_reader :payload, :member
|
|
||||||
|
|
||||||
def update_member_and_log
|
|
||||||
log_update_event if member.update(invite_email_success: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
def log_update_event
|
|
||||||
Gitlab::AppLogger.info "UPDATED MEMBER INVITE_EMAIL_SUCCESS: member_id: #{member.id}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def invite_token
|
|
||||||
# may want to validate schema in some way using ::JSONSchemer.schema(SCHEMA_PATH).valid?(message) if this
|
|
||||||
# gets more complex
|
|
||||||
payload.dig('user-variables', ::Members::Mailgun::INVITE_EMAIL_TOKEN_KEY) ||
|
|
||||||
raise(ProcessWebhookServiceError, "Failed to receive #{::Members::Mailgun::INVITE_EMAIL_TOKEN_KEY} in user-variables: #{payload}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
- return unless Feature.enabled?(:mailgun_events_receiver)
|
||||||
|
|
||||||
- expanded = integration_expanded?('mailgun_')
|
- expanded = integration_expanded?('mailgun_')
|
||||||
%section.settings.as-mailgun.no-animate#js-mailgun-settings{ class: ('expanded' if expanded) }
|
%section.settings.as-mailgun.no-animate#js-mailgun-settings{ class: ('expanded' if expanded) }
|
||||||
.settings-header
|
.settings-header
|
||||||
|
|
|
@ -68,7 +68,8 @@
|
||||||
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
|
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
|
||||||
= expanded_by_default? ? _('Collapse') : _('Expand')
|
= expanded_by_default? ? _('Collapse') : _('Expand')
|
||||||
%p
|
%p
|
||||||
= _('Configure limit for issues created per minute by web and API requests.')
|
= _('Limit the number of issues per minute a user can create through web and API requests.')
|
||||||
|
= link_to _('Learn more.'), help_page_path('user/admin_area/settings/rate_limit_on_issues_creation.md'), target: '_blank', rel: 'noopener noreferrer'
|
||||||
.settings-content
|
.settings-content
|
||||||
= render 'issue_limits'
|
= render 'issue_limits'
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,3 @@
|
||||||
- if group_sidebar_link?(:runners)
|
|
||||||
= nav_link(path: 'groups/runners#index') do
|
|
||||||
= link_to group_runners_path(@group), title: _('CI/CD'), class: 'has-sub-items' do
|
|
||||||
.nav-icon-container
|
|
||||||
= sprite_icon('rocket')
|
|
||||||
%span.nav-item-name
|
|
||||||
= _('CI/CD')
|
|
||||||
%ul.sidebar-sub-level-items
|
|
||||||
= nav_link(path: 'groups/runners#index', html_options: { class: "fly-out-top-item" } ) do
|
|
||||||
= link_to group_runners_path(@group), title: _('CI/CD') do
|
|
||||||
%strong.fly-out-top-item-name
|
|
||||||
= _('CI/CD')
|
|
||||||
%li.divider.fly-out-top-item
|
|
||||||
= nav_link(path: 'groups/runners#index') do
|
|
||||||
= link_to group_runners_path(@group), title: s_('Runners|Runners') do
|
|
||||||
%span= s_('Runners|Runners')
|
|
||||||
|
|
||||||
- if group_sidebar_link?(:kubernetes)
|
- if group_sidebar_link?(:kubernetes)
|
||||||
= nav_link(controller: [:clusters]) do
|
= nav_link(controller: [:clusters]) do
|
||||||
= link_to group_clusters_path(@group) do
|
= link_to group_clusters_path(@group) do
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: mailgun_events_receiver
|
||||||
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64249
|
||||||
|
rollout_issue_url:
|
||||||
|
milestone: '14.1'
|
||||||
|
type: development
|
||||||
|
group: group::expansion
|
||||||
|
default_enabled: false
|
|
@ -10,6 +10,10 @@ value_type: number
|
||||||
status: data_available
|
status: data_available
|
||||||
time_frame: 28d
|
time_frame: 28d
|
||||||
data_source: redis_hll
|
data_source: redis_hll
|
||||||
|
instrumentation_class: RedisHLLMetric
|
||||||
|
options:
|
||||||
|
events:
|
||||||
|
- i_search_total
|
||||||
distribution:
|
distribution:
|
||||||
- ce
|
- ce
|
||||||
- ee
|
- ee
|
||||||
|
|
|
@ -10,6 +10,12 @@ value_type: number
|
||||||
status: data_available
|
status: data_available
|
||||||
time_frame: 28d
|
time_frame: 28d
|
||||||
data_source: redis_hll
|
data_source: redis_hll
|
||||||
|
instrumentation_class: RedisHLLMetric
|
||||||
|
options:
|
||||||
|
events:
|
||||||
|
- i_search_total
|
||||||
|
- i_search_advanced
|
||||||
|
- i_search_paid
|
||||||
distribution:
|
distribution:
|
||||||
- ce
|
- ce
|
||||||
- ee
|
- ee
|
||||||
|
|
|
@ -10,6 +10,12 @@ value_type: number
|
||||||
status: data_available
|
status: data_available
|
||||||
time_frame: 28d
|
time_frame: 28d
|
||||||
data_source: redis_hll
|
data_source: redis_hll
|
||||||
|
instrumentation_class: RedisHLLMetric
|
||||||
|
options:
|
||||||
|
events:
|
||||||
|
- i_search_total
|
||||||
|
- i_search_advanced
|
||||||
|
- i_search_paid
|
||||||
distribution:
|
distribution:
|
||||||
- ce
|
- ce
|
||||||
- ee
|
- ee
|
||||||
|
|
|
@ -10,6 +10,10 @@ value_type: number
|
||||||
status: data_available
|
status: data_available
|
||||||
time_frame: 7d
|
time_frame: 7d
|
||||||
data_source: redis_hll
|
data_source: redis_hll
|
||||||
|
instrumentation_class: RedisHLLMetric
|
||||||
|
options:
|
||||||
|
events:
|
||||||
|
- i_search_total
|
||||||
distribution:
|
distribution:
|
||||||
- ee
|
- ee
|
||||||
- ce
|
- ce
|
||||||
|
|
|
@ -10,6 +10,12 @@ value_type: number
|
||||||
status: data_available
|
status: data_available
|
||||||
time_frame: 7d
|
time_frame: 7d
|
||||||
data_source: redis_hll
|
data_source: redis_hll
|
||||||
|
instrumentation_class: RedisHLLMetric
|
||||||
|
options:
|
||||||
|
events:
|
||||||
|
- i_search_total
|
||||||
|
- i_search_advanced
|
||||||
|
- i_search_paid
|
||||||
distribution:
|
distribution:
|
||||||
- ee
|
- ee
|
||||||
- ce
|
- ce
|
||||||
|
|
|
@ -221,7 +221,6 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
draw :snippets
|
draw :snippets
|
||||||
draw :profile
|
draw :profile
|
||||||
draw :members
|
|
||||||
|
|
||||||
# Product analytics collector
|
# Product analytics collector
|
||||||
match '/collector/i', to: ProductAnalytics::CollectorApp.new, via: :all
|
match '/collector/i', to: ProductAnalytics::CollectorApp.new, via: :all
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
namespace :members do
|
|
||||||
namespace :mailgun do
|
|
||||||
resources :permanent_failures, only: [:create]
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2,12 +2,10 @@
|
||||||
|
|
||||||
class AddInviteEmailSuccessToMember < ActiveRecord::Migration[6.1]
|
class AddInviteEmailSuccessToMember < ActiveRecord::Migration[6.1]
|
||||||
def up
|
def up
|
||||||
unless column_exists?(:members, :invite_email_success)
|
# no-op
|
||||||
add_column :members, :invite_email_success, :boolean, null: false, default: true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def down
|
def down
|
||||||
remove_column :members, :invite_email_success
|
# no-op
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14758,8 +14758,7 @@ CREATE TABLE members (
|
||||||
requested_at timestamp without time zone,
|
requested_at timestamp without time zone,
|
||||||
expires_at date,
|
expires_at date,
|
||||||
ldap boolean DEFAULT false NOT NULL,
|
ldap boolean DEFAULT false NOT NULL,
|
||||||
override boolean DEFAULT false NOT NULL,
|
override boolean DEFAULT false NOT NULL
|
||||||
invite_email_success boolean DEFAULT true NOT NULL
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE SEQUENCE members_id_seq
|
CREATE SEQUENCE members_id_seq
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
---
|
|
||||||
stage: Growth
|
|
||||||
group: Expansion
|
|
||||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
|
||||||
type: reference, howto
|
|
||||||
---
|
|
||||||
|
|
||||||
# Mailgun and GitLab **(FREE SELF)**
|
|
||||||
|
|
||||||
When you use Mailgun to send emails for your GitLab instance and [Mailgun](https://www.mailgun.com/)
|
|
||||||
integration is enabled and configured in GitLab, you can receive their webhook for
|
|
||||||
permanent invite email failures. To set up the integration, you must:
|
|
||||||
|
|
||||||
1. [Configure your Mailgun domain](#configure-your-mailgun-domain).
|
|
||||||
1. [Enable Mailgun integration](#enable-mailgun-integration).
|
|
||||||
|
|
||||||
After completing the integration, Mailgun `permanent_failure` webhooks are sent to your GitLab instance.
|
|
||||||
|
|
||||||
## Configure your Mailgun domain
|
|
||||||
|
|
||||||
Before you can enable Mailgun in GitLab, set up your own Mailgun permanent failure endpoint to receive the webhooks.
|
|
||||||
|
|
||||||
Using the [Mailgun webhook guide](https://www.mailgun.com/blog/a-guide-to-using-mailguns-webhooks/):
|
|
||||||
|
|
||||||
1. Add a webhook with the **Event type** set to **Permanent Failure**.
|
|
||||||
1. Fill in the URL of your instance and include the `/-/members/mailgun/permanent_failures` path.
|
|
||||||
- Example: `https://myinstance.gitlab.com/-/members/mailgun/permanent_failures`
|
|
||||||
|
|
||||||
## Enable Mailgun integration
|
|
||||||
|
|
||||||
After configuring your Mailgun domain for the permanent failures endpoint,
|
|
||||||
you're ready to enable the Mailgun integration:
|
|
||||||
|
|
||||||
1. Sign in to GitLab as an [Administrator](../../user/permissions.md) user.
|
|
||||||
1. On the top bar, select **Menu >** **{admin}** **Admin**.
|
|
||||||
1. In the left sidebar, go to **Settings > General** and expand the **Mailgun** section.
|
|
||||||
1. Select the **Enable Mailgun** check box.
|
|
||||||
1. Enter the Mailgun HTTP webhook signing key as described in
|
|
||||||
[the Mailgun documentation](https://documentation.mailgun.com/en/latest/user_manual.html#webhooks) and
|
|
||||||
shown in the [API security](https://app.mailgun.com/app/account/security/api_keys) section for your Mailgun account.
|
|
||||||
1. Select **Save changes**.
|
|
|
@ -41,8 +41,8 @@ Fields removed in [GitLab 14.0](https://gitlab.com/gitlab-org/gitlab/-/merge_req
|
||||||
Fields removed in [GitLab 13.6](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44866):
|
Fields removed in [GitLab 13.6](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44866):
|
||||||
|
|
||||||
| Field name | GraphQL type | Deprecated in | Use instead |
|
| Field name | GraphQL type | Deprecated in | Use instead |
|
||||||
| -------------------- | -------------------- | ------------- | -------------------------- |
|
|----------------------|--------------------------|---------------|----------------------------|
|
||||||
| `date` | `Timelog` **(STARTER)** | 12.10 | `spentAt` |
|
| `date` | `Timelog` | 12.10 | `spentAt` |
|
||||||
| `designs` | `Issue`, `EpicIssue` | 12.2 | `designCollection` |
|
| `designs` | `Issue`, `EpicIssue` | 12.2 | `designCollection` |
|
||||||
| `latestPipeline` | `Commit` | 12.5 | `pipelines` |
|
| `latestPipeline` | `Commit` | 12.5 | `pipelines` |
|
||||||
| `mergeCommitMessage` | `MergeRequest` | 11.8 | `latestMergeCommitMessage` |
|
| `mergeCommitMessage` | `MergeRequest` | 11.8 | `latestMergeCommitMessage` |
|
||||||
|
|
|
@ -328,7 +328,7 @@ listed in the descriptions of the relevant settings.
|
||||||
| `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Disabled by default.|
|
| `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Disabled by default.|
|
||||||
| `keep_latest_artifact` | boolean | no | Prevent the deletion of the artifacts from the most recent successful jobs, regardless of the expiry time. Enabled by default. |
|
| `keep_latest_artifact` | boolean | no | Prevent the deletion of the artifacts from the most recent successful jobs, regardless of the expiry time. Enabled by default. |
|
||||||
| `local_markdown_version` | integer | no | Increase this value when any cached Markdown should be invalidated. |
|
| `local_markdown_version` | integer | no | Increase this value when any cached Markdown should be invalidated. |
|
||||||
| `mailgun_signing_key` | string | no | The Mailgun HTTP webhook signing key for receiving events from webhook. |
|
| `mailgun_signing_key` | string | no | The Mailgun HTTP webhook signing key for receiving events from webhook |
|
||||||
| `mailgun_events_enabled` | boolean | no | Enable Mailgun event receiver. |
|
| `mailgun_events_enabled` | boolean | no | Enable Mailgun event receiver. |
|
||||||
| `maintenance_mode_message` | string | no | **(PREMIUM)** Message displayed when instance is in maintenance mode. |
|
| `maintenance_mode_message` | string | no | **(PREMIUM)** Message displayed when instance is in maintenance mode. |
|
||||||
| `maintenance_mode` | boolean | no | **(PREMIUM)** When instance is in maintenance mode, non-administrative users can sign in with read-only access and make read-only API requests. |
|
| `maintenance_mode` | boolean | no | **(PREMIUM)** When instance is in maintenance mode, non-administrative users can sign in with read-only access and make read-only API requests. |
|
||||||
|
|
|
@ -434,7 +434,7 @@ Parameters:
|
||||||
| `email` | Yes | Email |
|
| `email` | Yes | Email |
|
||||||
| `extern_uid` | No | External UID |
|
| `extern_uid` | No | External UID |
|
||||||
| `external` | No | Flags the user as external - true or false (default) |
|
| `external` | No | Flags the user as external - true or false (default) |
|
||||||
| `extra_shared_runners_minutes_limit` | No | Extra pipeline minutes quota for this user (purchased in addition to the minutes included in the plan) **(STARTER)** |
|
| `extra_shared_runners_minutes_limit` | No | Extra pipeline minutes quota for this user (purchased in addition to the minutes included in the plan) **(PREMIUM)** |
|
||||||
| `force_random_password` | No | Set user password to a random value - true or false (default) |
|
| `force_random_password` | No | Set user password to a random value - true or false (default) |
|
||||||
| `group_id_for_saml` | No | ID of group where SAML has been configured |
|
| `group_id_for_saml` | No | ID of group where SAML has been configured |
|
||||||
| `linkedin` | No | LinkedIn |
|
| `linkedin` | No | LinkedIn |
|
||||||
|
@ -447,7 +447,7 @@ Parameters:
|
||||||
| `projects_limit` | No | Number of projects user can create |
|
| `projects_limit` | No | Number of projects user can create |
|
||||||
| `provider` | No | External provider name |
|
| `provider` | No | External provider name |
|
||||||
| `reset_password` | No | Send user password reset link - true or false(default) |
|
| `reset_password` | No | Send user password reset link - true or false(default) |
|
||||||
| `shared_runners_minutes_limit` | No | Pipeline minutes quota for this user (included in plan). Can be `nil` (default; inherit system default), `0` (unlimited) or `> 0` **(STARTER)** |
|
| `shared_runners_minutes_limit` | No | Pipeline minutes quota for this user (included in plan). Can be `nil` (default; inherit system default), `0` (unlimited) or `> 0` **(PREMIUM)** |
|
||||||
| `skip_confirmation` | No | Skip confirmation - true or false (default) |
|
| `skip_confirmation` | No | Skip confirmation - true or false (default) |
|
||||||
| `skype` | No | Skype ID |
|
| `skype` | No | Skype ID |
|
||||||
| `theme_id` | No | The GitLab theme for the user (see [the user preference docs](../user/profile/preferences.md#navigation-theme) for more information) |
|
| `theme_id` | No | The GitLab theme for the user (see [the user preference docs](../user/profile/preferences.md#navigation-theme) for more information) |
|
||||||
|
@ -476,7 +476,7 @@ Parameters:
|
||||||
| `email` | No | Email |
|
| `email` | No | Email |
|
||||||
| `extern_uid` | No | External UID |
|
| `extern_uid` | No | External UID |
|
||||||
| `external` | No | Flags the user as external - true or false (default) |
|
| `external` | No | Flags the user as external - true or false (default) |
|
||||||
| `extra_shared_runners_minutes_limit` | No | Extra pipeline minutes quota for this user (purchased in addition to the minutes included in the plan) **(STARTER)** |
|
| `extra_shared_runners_minutes_limit` | No | Extra pipeline minutes quota for this user (purchased in addition to the minutes included in the plan) **(PREMIUM)** |
|
||||||
| `group_id_for_saml` | No | ID of group where SAML has been configured |
|
| `group_id_for_saml` | No | ID of group where SAML has been configured |
|
||||||
| `id` | Yes | The ID of the user |
|
| `id` | Yes | The ID of the user |
|
||||||
| `linkedin` | No | LinkedIn |
|
| `linkedin` | No | LinkedIn |
|
||||||
|
@ -489,7 +489,7 @@ Parameters:
|
||||||
| `projects_limit` | No | Limit projects each user can create |
|
| `projects_limit` | No | Limit projects each user can create |
|
||||||
| `provider` | No | External provider name |
|
| `provider` | No | External provider name |
|
||||||
| `public_email` | No | The public email of the user (must be already verified) |
|
| `public_email` | No | The public email of the user (must be already verified) |
|
||||||
| `shared_runners_minutes_limit` | No | Pipeline minutes quota for this user (included in plan). Can be `nil` (default; inherit system default), `0` (unlimited) or `> 0` **(STARTER)** |
|
| `shared_runners_minutes_limit` | No | Pipeline minutes quota for this user (included in plan). Can be `nil` (default; inherit system default), `0` (unlimited) or `> 0` **(PREMIUM)** |
|
||||||
| `skip_reconfirmation` | No | Skip reconfirmation - true or false (default) |
|
| `skip_reconfirmation` | No | Skip reconfirmation - true or false (default) |
|
||||||
| `skype` | No | Skype ID |
|
| `skype` | No | Skype ID |
|
||||||
| `theme_id` | No | The GitLab theme for the user (see [the user preference docs](../user/profile/preferences.md#navigation-theme) for more information) |
|
| `theme_id` | No | The GitLab theme for the user (see [the user preference docs](../user/profile/preferences.md#navigation-theme) for more information) |
|
||||||
|
@ -859,9 +859,13 @@ Example response:
|
||||||
|
|
||||||
Get the counts (same as in top right menu) of the currently signed in user.
|
Get the counts (same as in top right menu) of the currently signed in user.
|
||||||
|
|
||||||
| Attribute | Type | Description |
|
| Attribute | Type | Description |
|
||||||
| ---------------- | ------ | ------------------------------------------------------------ |
|
| --------------------------------- | ------ | ---------------------------------------------------------------------------- |
|
||||||
| `merge_requests` | number | Merge requests that are active and assigned to current user. |
|
| `assigned_issues` | number | Number of issues that are open and assigned to the current user. [Added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66909) in GitLab 14.2. |
|
||||||
|
| `assigned_merge_requests` | number | Number of merge requests that are active and assigned to the current user. [Added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50026) in GitLab 13.8. |
|
||||||
|
| `merge_requests` | number | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50026) in GitLab 13.8. Equivalent to and replaced by `assigned_merge_requests`. |
|
||||||
|
| `review_requested_merge_requests` | number | Number of merge requests that the current user has been requested to review. [Added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50026) in GitLab 13.8. |
|
||||||
|
| `todos` | number | Number of pending to-do items for current user. [Added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66909) in GitLab 14.2. |
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
GET /user_counts
|
GET /user_counts
|
||||||
|
@ -875,7 +879,11 @@ Example response:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"merge_requests": 4
|
"merge_requests": 4,
|
||||||
|
"assigned_issues": 15,
|
||||||
|
"assigned_merge_requests": 11,
|
||||||
|
"review_requested_merge_requests": 0,
|
||||||
|
"todos": 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,10 @@ Try to avoid. Be as specific as you can. Do not use **and so on** as a replaceme
|
||||||
- Avoid: You can update objects, like merge requests, issues, etc.
|
- Avoid: You can update objects, like merge requests, issues, etc.
|
||||||
- Use instead: You can update objects, like merge requests and issues.
|
- Use instead: You can update objects, like merge requests and issues.
|
||||||
|
|
||||||
|
## foo
|
||||||
|
|
||||||
|
Do not use in product documentation. You can use it in our API and contributor documentation, but try to use a clearer and more meaningful example instead.
|
||||||
|
|
||||||
## future tense
|
## future tense
|
||||||
|
|
||||||
When possible, use present tense instead. For example, use `after you execute this command, GitLab displays the result` instead of `after you execute this command, GitLab will display the result`. ([Vale](../testing.md#vale) rule: [`FutureTense.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/FutureTense.yml))
|
When possible, use present tense instead. For example, use `after you execute this command, GitLab displays the result` instead of `after you execute this command, GitLab will display the result`. ([Vale](../testing.md#vale) rule: [`FutureTense.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/FutureTense.yml))
|
||||||
|
|
|
@ -1481,7 +1481,7 @@ If this happens, examine the following:
|
||||||
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#disable-or-enable-gitaly-backup).
|
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#disable-or-enable-gitaly-backup).
|
||||||
|
|
||||||
There can be
|
There can be
|
||||||
[risks when disabling released features](../user/feature_flags.md#risks-when-disabling-released-features).
|
[risks when disabling released features](../administration/feature_flags.md#risks-when-disabling-released-features).
|
||||||
Refer to this feature's version history for more details.
|
Refer to this feature's version history for more details.
|
||||||
|
|
||||||
`gitaly-backup` is used by the backup Rake task to create and restore repository backups from Gitaly.
|
`gitaly-backup` is used by the backup Rake task to create and restore repository backups from Gitaly.
|
||||||
|
|
|
@ -67,7 +67,7 @@ You can specify a custom URL to which users are directed when they:
|
||||||
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-gitlab-documentation-link-redirects). **(FREE SELF)**
|
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-gitlab-documentation-link-redirects). **(FREE SELF)**
|
||||||
|
|
||||||
This in-development feature might not be available for your use. There can be
|
This in-development feature might not be available for your use. There can be
|
||||||
[risks when enabling features still in development](../../feature_flags.md#risks-when-enabling-features-still-in-development).
|
[risks when enabling features still in development](../../../administration/feature_flags.md#risks-when-enabling-features-still-in-development).
|
||||||
Refer to this feature's version history for more details.
|
Refer to this feature's version history for more details.
|
||||||
|
|
||||||
Documentation links go to the `/help` section on the instance by default, but you can
|
Documentation links go to the `/help` section on the instance by default, but you can
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
|
@ -39,7 +39,6 @@ To access the default page for Admin Area settings:
|
||||||
| ------ | ----------- |
|
| ------ | ----------- |
|
||||||
| [Elasticsearch](../../../integration/elasticsearch.md#enabling-advanced-search) | Elasticsearch integration. Elasticsearch AWS IAM. |
|
| [Elasticsearch](../../../integration/elasticsearch.md#enabling-advanced-search) | Elasticsearch integration. Elasticsearch AWS IAM. |
|
||||||
| [Kroki](../../../administration/integration/kroki.md#enable-kroki-in-gitlab) | Allow rendering of diagrams in AsciiDoc and Markdown documents using [kroki.io](https://kroki.io). |
|
| [Kroki](../../../administration/integration/kroki.md#enable-kroki-in-gitlab) | Allow rendering of diagrams in AsciiDoc and Markdown documents using [kroki.io](https://kroki.io). |
|
||||||
| [Mailgun](../../../administration/integration/mailgun.md) | Enable your GitLab instance to receive invite email bounce events from Mailgun, if it is your email provider. |
|
|
||||||
| [PlantUML](../../../administration/integration/plantuml.md) | Allow rendering of PlantUML diagrams in documents. |
|
| [PlantUML](../../../administration/integration/plantuml.md) | Allow rendering of PlantUML diagrams in documents. |
|
||||||
| [Slack application](../../../user/project/integrations/gitlab_slack_application.md#configuration) **(FREE SAAS)** | Slack integration allows you to interact with GitLab via slash commands in a chat window. This option is only available on GitLab.com, though it may be [available for self-managed instances in the future](https://gitlab.com/gitlab-org/gitlab/-/issues/28164). |
|
| [Slack application](../../../user/project/integrations/gitlab_slack_application.md#configuration) **(FREE SAAS)** | Slack integration allows you to interact with GitLab via slash commands in a chat window. This option is only available on GitLab.com, though it may be [available for self-managed instances in the future](https://gitlab.com/gitlab-org/gitlab/-/issues/28164). |
|
||||||
| [Third party offers](third_party_offers.md) | Control the display of third party offers. |
|
| [Third party offers](third_party_offers.md) | Control the display of third party offers. |
|
||||||
|
|
|
@ -22,7 +22,7 @@ For example, if you set a limit of 300, requests using the
|
||||||
[Projects::IssuesController#create](https://gitlab.com/gitlab-org/gitlab/raw/master/app/controllers/projects/issues_controller.rb)
|
[Projects::IssuesController#create](https://gitlab.com/gitlab-org/gitlab/raw/master/app/controllers/projects/issues_controller.rb)
|
||||||
action exceeding a rate of 300 per minute are blocked. Access to the endpoint is allowed after one minute.
|
action exceeding a rate of 300 per minute are blocked. Access to the endpoint is allowed after one minute.
|
||||||
|
|
||||||
![Rate limits on issues creation](img/rate_limit_on_issues_creation_v13_1.png)
|
![Rate limits on issues creation](img/rate_limit_on_issues_creation_v14_2.png)
|
||||||
|
|
||||||
This limit is:
|
This limit is:
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ type: reference
|
||||||
> - Code Owners for merge request approvals was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4418) in GitLab Premium 11.9.
|
> - Code Owners for merge request approvals was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4418) in GitLab Premium 11.9.
|
||||||
> - Moved to GitLab Premium in 13.9.
|
> - Moved to GitLab Premium in 13.9.
|
||||||
|
|
||||||
Code Owners define who owns specific files or folders in a repository.
|
Code Owners define who owns specific files or directories in a repository.
|
||||||
|
|
||||||
- The users you define as Code Owners are displayed in the UI when you browse directories.
|
- The users you define as Code Owners are displayed in the UI when you browse directories.
|
||||||
- You can set your merge requests so they must be approved by Code Owners before merge.
|
- You can set your merge requests so they must be approved by Code Owners before merge.
|
||||||
|
@ -23,7 +23,7 @@ If you don't want to use Code Owners for approvals, you can
|
||||||
## Set up Code Owners
|
## Set up Code Owners
|
||||||
|
|
||||||
You can use Code Owners to specify users or [shared groups](members/share_project_with_groups.md)
|
You can use Code Owners to specify users or [shared groups](members/share_project_with_groups.md)
|
||||||
that are responsible for specific files and folders in a repository.
|
that are responsible for specific files and directories in a repository.
|
||||||
|
|
||||||
To set up Code Owners:
|
To set up Code Owners:
|
||||||
|
|
||||||
|
@ -41,24 +41,65 @@ To set up Code Owners:
|
||||||
filename @username1 @username2
|
filename @username1 @username2
|
||||||
|
|
||||||
# Code Owners for a directory
|
# Code Owners for a directory
|
||||||
foldername @username1 @username2
|
directoryname/ @username1 @username2
|
||||||
|
|
||||||
# All group members as Code Owners for a file
|
# All group members as Code Owners for a file
|
||||||
filename @groupname
|
filename @groupname
|
||||||
|
|
||||||
# All group members as Code Owners for a folder
|
# All group members as Code Owners for a directory
|
||||||
foldername @groupname
|
directoryname/ @groupname
|
||||||
```
|
```
|
||||||
|
|
||||||
The Code Owners are now displayed in the UI.
|
The Code Owners are now displayed in the UI. They apply to the current branch only.
|
||||||
|
|
||||||
Next steps:
|
Next steps:
|
||||||
|
|
||||||
- [Add Code Owners as merge request approvers](merge_requests/approvals/rules.md#code-owners-as-eligible-approvers).
|
- [Add Code Owners as merge request approvers](merge_requests/approvals/rules.md#code-owners-as-eligible-approvers).
|
||||||
- Set up [Code Owner approval on a protected branch](protected_branches.md#require-code-owner-approval-on-a-protected-branch).
|
- Set up [Code Owner approval on a protected branch](protected_branches.md#require-code-owner-approval-on-a-protected-branch).
|
||||||
|
|
||||||
NOTE:
|
## Groups as Code Owners
|
||||||
The Code Owners apply to the current branch only.
|
|
||||||
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53182) in GitLab 12.1.
|
||||||
|
> - Group and subgroup hierarchy support was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32432) in GitLab 13.0.
|
||||||
|
|
||||||
|
You can use members of groups and subgroups as Code Owners for a project.
|
||||||
|
|
||||||
|
For example, if you have these groups:
|
||||||
|
|
||||||
|
- **Group X** (`group-x`) with **Project A** in it.
|
||||||
|
- **Subgroup Y** (`group-x/subgroup-y`), which belongs to **Group X**, with **Project B** in it.
|
||||||
|
|
||||||
|
The eligible Code Owners:
|
||||||
|
|
||||||
|
- For **Project A** are the members of **Group X** only, because **Project A** doesn't belong to **Subgroup Y**.
|
||||||
|
- For **Project B** are the members of both **Group X** and **Subgroup Y**.
|
||||||
|
|
||||||
|
![Eligible Code Owners](img/code_owners_members_v13_4.png)
|
||||||
|
|
||||||
|
You can [invite](members/share_project_with_groups.md) **Subgroup Y** to **Project A**
|
||||||
|
so that their members also become eligible Code Owners.
|
||||||
|
|
||||||
|
![Invite subgroup members to become eligible Code Owners](img/code_owners_invite_members_v13_4.png)
|
||||||
|
|
||||||
|
If you do not invite **Subgroup Y** to **Project A**, but make them Code Owners, their approval
|
||||||
|
of the merge request becomes optional.
|
||||||
|
|
||||||
|
### Add a group as a Code Owner
|
||||||
|
|
||||||
|
To set a group as a Code Owner:
|
||||||
|
|
||||||
|
In the `CODEOWNERS` file, enter text that follows one of these patterns:
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
# All group members as Code Owners for a file
|
||||||
|
file.md @group-x
|
||||||
|
|
||||||
|
# All subgroup members as Code Owners for a file
|
||||||
|
file.md @group-x/subgroup-y
|
||||||
|
|
||||||
|
# All group and subgroup members as Code Owners for a file
|
||||||
|
file.md @group-x @group-x/subgroup-y
|
||||||
|
```
|
||||||
|
|
||||||
## When a file matches multiple `CODEOWNERS` entries
|
## When a file matches multiple `CODEOWNERS` entries
|
||||||
|
|
||||||
|
@ -74,83 +115,24 @@ README.md @user1
|
||||||
*.md @user2
|
*.md @user2
|
||||||
```
|
```
|
||||||
|
|
||||||
The user that would show for `README.md` would be `@user2`.
|
The Code Owner for `README.md` would be `@user2`.
|
||||||
|
|
||||||
## Groups as Code Owners
|
If you use sections, the last user _for each section_ is used.
|
||||||
|
|
||||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53182) in GitLab 12.1.
|
Only one CODEOWNERS pattern can match per file path.
|
||||||
> - Group and subgroup hierarchy support was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32432) in GitLab 13.0.
|
|
||||||
|
|
||||||
Groups and subgroups members are inherited as eligible Code Owners to a
|
### Organize Code Owners by putting them into sections
|
||||||
project, as long as the hierarchy is respected.
|
|
||||||
|
|
||||||
For example, consider a given group called "Group X" (slug `group-x`) and a
|
|
||||||
"Subgroup Y" (slug `group-x/subgroup-y`) that belongs to the Group X, and
|
|
||||||
suppose you have a project called "Project A" within the group and a
|
|
||||||
"Project B" within the subgroup.
|
|
||||||
|
|
||||||
The eligible Code Owners to Project B are both the members of the Group X and
|
|
||||||
the Subgroup Y. The eligible Code Owners to the Project A are just the
|
|
||||||
members of the Group X, given that Project A doesn't belong to the Subgroup Y:
|
|
||||||
|
|
||||||
![Eligible Code Owners](img/code_owners_members_v13_4.png)
|
|
||||||
|
|
||||||
But you have the option to [invite](members/share_project_with_groups.md)
|
|
||||||
the Subgroup Y to the Project A so that their members also become eligible
|
|
||||||
Code Owners:
|
|
||||||
|
|
||||||
NOTE:
|
|
||||||
If you do not invite Subgroup Y to Project A, but make them Code Owners, their approval
|
|
||||||
of the merge request becomes optional.
|
|
||||||
|
|
||||||
![Invite subgroup members to become eligible Code Owners](img/code_owners_invite_members_v13_4.png)
|
|
||||||
|
|
||||||
After being invited, any member (`@user`) of the group or subgroup can be set
|
|
||||||
as Code Owner to files of the Project A or B, and the entire Group X
|
|
||||||
(`@group-x`) or Subgroup Y (`@group-x/subgroup-y`), as follows:
|
|
||||||
|
|
||||||
```plaintext
|
|
||||||
# A member of the group or subgroup as Code Owner to a file
|
|
||||||
file.md @user
|
|
||||||
|
|
||||||
# All group members as Code Owners to a file
|
|
||||||
file.md @group-x
|
|
||||||
|
|
||||||
# All subgroup members as Code Owners to a file
|
|
||||||
file.md @group-x/subgroup-y
|
|
||||||
|
|
||||||
# All group and subgroup members as Code Owners to a file
|
|
||||||
file.md @group-x @group-x/subgroup-y
|
|
||||||
```
|
|
||||||
|
|
||||||
### Code Owners sections **(PREMIUM)**
|
|
||||||
|
|
||||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12137) in GitLab Premium 13.2 behind a feature flag, enabled by default.
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12137) in GitLab Premium 13.2 behind a feature flag, enabled by default.
|
||||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42389) in GitLab 13.4.
|
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42389) in GitLab 13.4.
|
||||||
|
|
||||||
Code Owner rules can be grouped into named sections. This allows for better
|
You can organize Code Owners by putting them into named sections.
|
||||||
organization of broader categories of Code Owner rules to be applied.
|
|
||||||
Additionally, the usual guidance that only the last pattern matching the file is
|
|
||||||
applied is expanded such that the last pattern matching _for each section_ is
|
|
||||||
applied.
|
|
||||||
|
|
||||||
For example, in a large organization, independent teams may have a common interest
|
You can use sections for shared directories, so that multiple
|
||||||
in parts of the application, for instance, a payment processing company may have
|
teams can be reviewers.
|
||||||
"development", "security", and "compliance" teams looking after common parts of
|
|
||||||
the codebase. All three teams may need to approve changes. Although approval rules
|
|
||||||
make this possible, they apply to every merge request. Also, while Code Owners are
|
|
||||||
applied based on which files are changed, only one CODEOWNERS pattern can match per
|
|
||||||
file path.
|
|
||||||
|
|
||||||
Using `CODEOWNERS` sections allows multiple teams that only need to approve certain
|
To add a section to the `CODEOWNERS` file, enter a section name in brackets,
|
||||||
changes, to set their own independent patterns by specifying discrete sections in the
|
followed by the files or directories, and users, groups, or subgroups:
|
||||||
`CODEOWNERS` file. The section rules may be used for shared paths so that multiple
|
|
||||||
teams can be added as reviewers.
|
|
||||||
|
|
||||||
Sections can be added to `CODEOWNERS` files as a new line with the name of the
|
|
||||||
section inside square brackets. Every entry following is assigned to that
|
|
||||||
section. The following example would create two Code Owner rules for the "README
|
|
||||||
Owners" section:
|
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
[README Owners]
|
[README Owners]
|
||||||
|
@ -158,43 +140,41 @@ README.md @user1 @user2
|
||||||
internal/README.md @user2
|
internal/README.md @user2
|
||||||
```
|
```
|
||||||
|
|
||||||
Multiple sections can be used, even with matching file or directory patterns.
|
Each Code Owner in the merge request widget is listed under a label.
|
||||||
Reusing the same section name groups the results together under the same
|
The following image shows a **Groups** and **Documentation** section:
|
||||||
section, with the most specific rule or last matching pattern being used. For
|
|
||||||
example, consider the following entries in a `CODEOWNERS` file:
|
|
||||||
|
|
||||||
```plaintext
|
|
||||||
[Documentation]
|
|
||||||
ee/docs @gl-docs
|
|
||||||
docs @gl-docs
|
|
||||||
|
|
||||||
[Database]
|
|
||||||
README.md @gl-database
|
|
||||||
model/db @gl-database
|
|
||||||
|
|
||||||
[DOCUMENTATION]
|
|
||||||
README.md @gl-docs
|
|
||||||
```
|
|
||||||
|
|
||||||
This results in three entries under the "Documentation" section header, and two
|
|
||||||
entries under "Database". Case is not considered when combining sections, so in
|
|
||||||
this example, entries defined under the sections "Documentation" and
|
|
||||||
"DOCUMENTATION" would be combined into one, using the case of the first instance
|
|
||||||
of the section encountered in the file.
|
|
||||||
|
|
||||||
When assigned to a section, each Code Owner rule displayed in merge requests
|
|
||||||
widget is sorted under a "section" label. In the screenshot below, we can see
|
|
||||||
the rules for "Groups" and "Documentation" sections:
|
|
||||||
|
|
||||||
![MR widget - Sectional Code Owners](img/sectional_code_owners_v13.2.png)
|
![MR widget - Sectional Code Owners](img/sectional_code_owners_v13.2.png)
|
||||||
|
|
||||||
#### Optional Code Owners sections **(PREMIUM)**
|
### Sections with duplicate names
|
||||||
|
|
||||||
|
If multiple sections have the same name, they are combined.
|
||||||
|
Also, section headings are not case-sensitive. For example:
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
[Documentation]
|
||||||
|
ee/docs/ @docs
|
||||||
|
docs/ @docs
|
||||||
|
|
||||||
|
[Database]
|
||||||
|
README.md @database
|
||||||
|
model/db/ @database
|
||||||
|
|
||||||
|
[DOCUMENTATION]
|
||||||
|
README.md @docs
|
||||||
|
```
|
||||||
|
|
||||||
|
This code results in three entries under the **Documentation** section header, and two
|
||||||
|
entries under **Database**. The entries defined under the sections **Documentation** and
|
||||||
|
**DOCUMENTATION** are combined, using the case of the first section.
|
||||||
|
|
||||||
|
### Make a Code Owners section optional
|
||||||
|
|
||||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/232995) in GitLab Premium 13.8 behind a feature flag, enabled by default.
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/232995) in GitLab Premium 13.8 behind a feature flag, enabled by default.
|
||||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53227) in GitLab 13.9.
|
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53227) in GitLab 13.9.
|
||||||
|
|
||||||
To make a certain section optional, add a Code Owners section prepended with the
|
You can make a section optional, so that approval from the Code Owners in that section is optional.
|
||||||
caret `^` character. Approvals from owners listed in the section are **not** required. For example:
|
|
||||||
|
Put a caret `^` character before the Code Owners section name. For example:
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
[Documentation]
|
[Documentation]
|
||||||
|
@ -211,83 +191,64 @@ The optional Code Owners section displays in merge requests under the **Approval
|
||||||
|
|
||||||
![MR widget - Optional Code Owners sections](img/optional_code_owners_sections_v13_8.png)
|
![MR widget - Optional Code Owners sections](img/optional_code_owners_sections_v13_8.png)
|
||||||
|
|
||||||
If a section is duplicated in the file, and one of them is marked as optional and the other isn't, the requirement prevails.
|
If a section is duplicated in the file, and one of them is marked as optional and the other isn't, the section is required.
|
||||||
|
|
||||||
For example, the Code Owners of the "Documentation" section below is still required to approve merge requests:
|
|
||||||
|
|
||||||
```plaintext
|
|
||||||
[Documentation]
|
|
||||||
*.md @root
|
|
||||||
|
|
||||||
[Ruby]
|
|
||||||
*.rb @root
|
|
||||||
|
|
||||||
^[Go]
|
|
||||||
*.go @root
|
|
||||||
|
|
||||||
^[Documentation]
|
|
||||||
*.txt @root
|
|
||||||
```
|
|
||||||
|
|
||||||
Optional sections in the `CODEOWNERS` file are treated as optional only
|
Optional sections in the `CODEOWNERS` file are treated as optional only
|
||||||
when changes are submitted by using merge requests. If a change is submitted directly
|
when changes are submitted by using merge requests. If a change is submitted directly
|
||||||
to the protected branch, approval from Code Owners is still required, even if the
|
to the protected branch, approval from Code Owners is still required, even if the
|
||||||
section is marked as optional. We plan to change this behavior in a
|
section is marked as optional. [An issue exists](https://gitlab.com/gitlab-org/gitlab/-/issues/297638)
|
||||||
[future release](https://gitlab.com/gitlab-org/gitlab/-/issues/297638),
|
to allow direct pushes to the protected branch for sections marked as optional.
|
||||||
and allow direct pushes to the protected branch for sections marked as optional.
|
|
||||||
|
|
||||||
## Example `CODEOWNERS` file
|
## Example `CODEOWNERS` file
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
# This is an example of a CODEOWNERS file
|
# This is an example of a CODEOWNERS file.
|
||||||
# lines starting with a `#` will be ignored.
|
# Lines that start with `#` are ignored.
|
||||||
|
|
||||||
# app/ @commented-rule
|
# app/ @commented-rule
|
||||||
|
|
||||||
# We can specify a default match using wildcards:
|
# Specify a default Code Owner by using a wildcard:
|
||||||
* @default-codeowner
|
* @default-codeowner
|
||||||
|
|
||||||
# We can also specify "multiple tab or space" separated codeowners:
|
# Specify multiple Code Owners by using a tab or space:
|
||||||
* @multiple @code @owners
|
* @multiple @code @owners
|
||||||
|
|
||||||
# Rules defined later in the file take precedence over the rules
|
# Rules defined later in the file take precedence over the rules
|
||||||
# defined before.
|
# defined before.
|
||||||
# This will match all files for which the file name ends in `.rb`
|
# For example, for all files with a filename ending in `.rb`:
|
||||||
*.rb @ruby-owner
|
*.rb @ruby-owner
|
||||||
|
|
||||||
# Files with a `#` can still be accessed by escaping the pound sign
|
# Files with a `#` can still be accessed by escaping the pound sign:
|
||||||
\#file_with_pound.rb @owner-file-with-pound
|
\#file_with_pound.rb @owner-file-with-pound
|
||||||
|
|
||||||
# Multiple codeowners can be specified, separated by spaces or tabs
|
# Specify multiple Code Owners separated by spaces or tabs.
|
||||||
# In the following case the CODEOWNERS file from the root of the repo
|
# In the following case the CODEOWNERS file from the root of the repo
|
||||||
# has 3 Code Owners (@multiple @code @owners)
|
# has 3 Code Owners (@multiple @code @owners):
|
||||||
CODEOWNERS @multiple @code @owners
|
CODEOWNERS @multiple @code @owners
|
||||||
|
|
||||||
# Both usernames or email addresses can be used to match
|
# You can use both usernames or email addresses to match
|
||||||
# users. Everything else will be ignored. For example this will
|
# users. Everything else is ignored. For example, this code
|
||||||
# specify `@legal` and a user with email `janedoe@gitlab.com` as the
|
# specifies the `@legal` and a user with email `janedoe@gitlab.com` as the
|
||||||
# owner for the LICENSE file
|
# owner for the LICENSE file:
|
||||||
LICENSE @legal this_does_not_match janedoe@gitlab.com
|
LICENSE @legal this_does_not_match janedoe@gitlab.com
|
||||||
|
|
||||||
# Group names can be used to match groups and nested groups to specify
|
# Use group names to match groups, and nested groups to specify
|
||||||
# them as owners for a file
|
# them as owners for a file:
|
||||||
README @group @group/with-nested/subgroup
|
README @group @group/with-nested/subgroup
|
||||||
|
|
||||||
# Ending a path in a `/` will specify the Code Owners for every file
|
# End a path in a `/` to specify the Code Owners for every file
|
||||||
# nested in that directory, on any level
|
# nested in that directory, on any level:
|
||||||
/docs/ @all-docs
|
/docs/ @all-docs
|
||||||
|
|
||||||
# Ending a path in `/*` will specify Code Owners for every file in
|
# End a path in `/*` to specify Code Owners for every file in
|
||||||
# that directory, but not nested deeper. This will match
|
# a directory, but not nested deeper. This code matches
|
||||||
# `docs/index.md` but not `docs/projects/index.md`
|
# `docs/index.md` but not `docs/projects/index.md`:
|
||||||
/docs/* @root-docs
|
/docs/* @root-docs
|
||||||
|
|
||||||
# This will make a `lib` directory nested anywhere in the repository
|
# This code makes matches a `lib` directory nested anywhere in the repository:
|
||||||
# match
|
|
||||||
lib/ @lib-owner
|
lib/ @lib-owner
|
||||||
|
|
||||||
# This will only match a `config` directory in the root of the
|
# This code match only a `config` directory in the root of the repository:
|
||||||
# repository
|
|
||||||
/config/ @config-owner
|
/config/ @config-owner
|
||||||
|
|
||||||
# If the path contains spaces, escape them like this:
|
# If the path contains spaces, escape them like this:
|
||||||
|
@ -295,14 +256,14 @@ path\ with\ spaces/ @space-owner
|
||||||
|
|
||||||
# Code Owners section:
|
# Code Owners section:
|
||||||
[Documentation]
|
[Documentation]
|
||||||
ee/docs @gl-docs
|
ee/docs @docs
|
||||||
docs @gl-docs
|
docs @docs
|
||||||
|
|
||||||
[Database]
|
[Database]
|
||||||
README.md @gl-database
|
README.md @database
|
||||||
model/db @gl-database
|
model/db @database
|
||||||
|
|
||||||
# This section will be joined with the [Documentation] section previously defined:
|
# This section is combined with the previously defined [Documentation] section:
|
||||||
[DOCUMENTATION]
|
[DOCUMENTATION]
|
||||||
README.md @gl-docs
|
README.md @docs
|
||||||
```
|
```
|
||||||
|
|
|
@ -6,15 +6,17 @@ module API
|
||||||
|
|
||||||
resource :user_counts do
|
resource :user_counts do
|
||||||
desc 'Return the user specific counts' do
|
desc 'Return the user specific counts' do
|
||||||
detail 'Open MR Count'
|
detail 'Assigned open issues, assigned MRs and pending todos count'
|
||||||
end
|
end
|
||||||
get do
|
get do
|
||||||
unauthorized! unless current_user
|
unauthorized! unless current_user
|
||||||
|
|
||||||
{
|
{
|
||||||
merge_requests: current_user.assigned_open_merge_requests_count, # @deprecated
|
merge_requests: current_user.assigned_open_merge_requests_count, # @deprecated
|
||||||
|
assigned_issues: current_user.assigned_open_issues_count,
|
||||||
assigned_merge_requests: current_user.assigned_open_merge_requests_count,
|
assigned_merge_requests: current_user.assigned_open_merge_requests_count,
|
||||||
review_requested_merge_requests: current_user.review_requested_open_merge_requests_count
|
review_requested_merge_requests: current_user.review_requested_open_merge_requests_count,
|
||||||
|
todos: current_user.todos_pending_count
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
51
lib/sidebars/groups/menus/ci_cd_menu.rb
Normal file
51
lib/sidebars/groups/menus/ci_cd_menu.rb
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Sidebars
|
||||||
|
module Groups
|
||||||
|
module Menus
|
||||||
|
class CiCdMenu < ::Sidebars::Menu
|
||||||
|
override :configure_menu_items
|
||||||
|
def configure_menu_items
|
||||||
|
add_item(runners_menu_item)
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
override :link
|
||||||
|
def link
|
||||||
|
renderable_items.first.link
|
||||||
|
end
|
||||||
|
|
||||||
|
override :title
|
||||||
|
def title
|
||||||
|
_('CI/CD')
|
||||||
|
end
|
||||||
|
|
||||||
|
override :sprite_icon
|
||||||
|
def sprite_icon
|
||||||
|
'rocket'
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def runners_menu_item
|
||||||
|
return ::Sidebars::NilMenuItem.new(item_id: :runners) unless show_runners?
|
||||||
|
|
||||||
|
::Sidebars::MenuItem.new(
|
||||||
|
title: _('Runners'),
|
||||||
|
link: group_runners_path(context.group),
|
||||||
|
active_routes: { path: 'groups/runners#index' },
|
||||||
|
item_id: :runners
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO Proper policies, such as `read_group_runners`, should be implemented per
|
||||||
|
# See https://gitlab.com/gitlab-org/gitlab/-/issues/334802
|
||||||
|
def show_runners?
|
||||||
|
can?(context.current_user, :admin_group, context.group) &&
|
||||||
|
Feature.enabled?(:runner_list_group_view_vue_ui, context.group, default_enabled: :yaml)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,6 +10,7 @@ module Sidebars
|
||||||
add_menu(Sidebars::Groups::Menus::GroupInformationMenu.new(context))
|
add_menu(Sidebars::Groups::Menus::GroupInformationMenu.new(context))
|
||||||
add_menu(Sidebars::Groups::Menus::IssuesMenu.new(context))
|
add_menu(Sidebars::Groups::Menus::IssuesMenu.new(context))
|
||||||
add_menu(Sidebars::Groups::Menus::MergeRequestsMenu.new(context))
|
add_menu(Sidebars::Groups::Menus::MergeRequestsMenu.new(context))
|
||||||
|
add_menu(Sidebars::Groups::Menus::CiCdMenu.new(context))
|
||||||
end
|
end
|
||||||
|
|
||||||
override :render_raw_menus_partial
|
override :render_raw_menus_partial
|
||||||
|
|
|
@ -8371,9 +8371,6 @@ msgstr ""
|
||||||
msgid "Configure existing installation"
|
msgid "Configure existing installation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Configure limit for issues created per minute by web and API requests."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Configure limit for notes created per minute by web and API requests."
|
msgid "Configure limit for notes created per minute by web and API requests."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -19741,6 +19738,9 @@ msgstr ""
|
||||||
msgid "Limit the number of concurrent operations this secondary node can run in the background."
|
msgid "Limit the number of concurrent operations this secondary node can run in the background."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Limit the number of issues per minute a user can create through web and API requests."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Limited to showing %d event at most"
|
msgid "Limited to showing %d event at most"
|
||||||
msgid_plural "Limited to showing %d events at most"
|
msgid_plural "Limited to showing %d events at most"
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
|
|
|
@ -236,6 +236,11 @@ RSpec.describe ApplicationExperiment, :experiment do
|
||||||
expect(subject.process_redirect_url(url)).to be_nil
|
expect(subject.process_redirect_url(url)).to be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "generates the correct urls based on where the engine was mounted" do
|
||||||
|
url = Rails.application.routes.url_helpers.experiment_redirect_url(subject, url: 'https://docs.gitlab.com')
|
||||||
|
expect(url).to include("/-/experiment/namespaced%2Fstub:#{subject.context.key}?https://docs.gitlab.com")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when resolving variants" do
|
context "when resolving variants" do
|
||||||
|
|
|
@ -30,9 +30,9 @@ RSpec.describe NewProjectReadmeContentExperiment, :experiment do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "renders redirect URLs" do
|
it "renders redirect URLs" do
|
||||||
expect(markdown).to include(
|
url = Rails.application.routes.url_helpers.experiment_redirect_url(subject, url: initial_url)
|
||||||
Rails.application.routes.url_helpers.experiment_redirect_url(subject, url: initial_url)
|
expect(url).to include("/-/experiment/#{subject.to_param}?")
|
||||||
)
|
expect(markdown).to include(url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -269,7 +269,10 @@ RSpec.describe 'Admin updates settings' do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'Integrations page' do
|
context 'Integrations page' do
|
||||||
|
let(:mailgun_events_receiver_enabled) { true }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(mailgun_events_receiver: mailgun_events_receiver_enabled)
|
||||||
visit general_admin_application_settings_path
|
visit general_admin_application_settings_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -283,16 +286,26 @@ RSpec.describe 'Admin updates settings' do
|
||||||
expect(current_settings.hide_third_party_offers).to be true
|
expect(current_settings.hide_third_party_offers).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'enabling Mailgun events', :aggregate_failures do
|
context 'when mailgun_events_receiver feature flag is enabled' do
|
||||||
page.within('.as-mailgun') do
|
it 'enabling Mailgun events', :aggregate_failures do
|
||||||
check 'Enable Mailgun event receiver'
|
page.within('.as-mailgun') do
|
||||||
fill_in 'Mailgun HTTP webhook signing key', with: 'MAILGUN_SIGNING_KEY'
|
check 'Enable Mailgun event receiver'
|
||||||
click_button 'Save changes'
|
fill_in 'Mailgun HTTP webhook signing key', with: 'MAILGUN_SIGNING_KEY'
|
||||||
end
|
click_button 'Save changes'
|
||||||
|
end
|
||||||
|
|
||||||
expect(page).to have_content 'Application settings saved successfully'
|
expect(page).to have_content 'Application settings saved successfully'
|
||||||
expect(current_settings.mailgun_events_enabled).to be true
|
expect(current_settings.mailgun_events_enabled).to be true
|
||||||
expect(current_settings.mailgun_signing_key).to eq 'MAILGUN_SIGNING_KEY'
|
expect(current_settings.mailgun_signing_key).to eq 'MAILGUN_SIGNING_KEY'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when mailgun_events_receiver feature flag is disabled' do
|
||||||
|
let(:mailgun_events_receiver_enabled) { false }
|
||||||
|
|
||||||
|
it 'does not have mailgun' do
|
||||||
|
expect(page).not_to have_selector('.as-mailgun')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -167,7 +167,6 @@ ProjectMember:
|
||||||
- expires_at
|
- expires_at
|
||||||
- ldap
|
- ldap
|
||||||
- override
|
- override
|
||||||
- invite_email_success
|
|
||||||
User:
|
User:
|
||||||
- id
|
- id
|
||||||
- username
|
- username
|
||||||
|
|
40
spec/lib/sidebars/groups/menus/ci_cd_menu_spec.rb
Normal file
40
spec/lib/sidebars/groups/menus/ci_cd_menu_spec.rb
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Sidebars::Groups::Menus::CiCdMenu do
|
||||||
|
let_it_be(:owner) { create(:user) }
|
||||||
|
let_it_be(:root_group) do
|
||||||
|
build(:group, :private).tap do |g|
|
||||||
|
g.add_owner(owner)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:group) { root_group }
|
||||||
|
let(:user) { owner }
|
||||||
|
let(:context) { Sidebars::Groups::Context.new(current_user: user, container: group) }
|
||||||
|
|
||||||
|
describe 'Menu Items' do
|
||||||
|
subject { described_class.new(context).renderable_items.index { |e| e.item_id == item_id } }
|
||||||
|
|
||||||
|
describe 'Runners' do
|
||||||
|
let(:item_id) { :runners }
|
||||||
|
|
||||||
|
specify { is_expected.not_to be_nil }
|
||||||
|
|
||||||
|
describe 'when feature flag :runner_list_group_view_vue_ui is disabled' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(runner_list_group_view_vue_ui: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify { is_expected.to be_nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when the user does not have access' do
|
||||||
|
let(:user) { nil }
|
||||||
|
|
||||||
|
specify { is_expected.to be_nil }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -832,15 +832,15 @@ RSpec.describe Notify do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when mailgun events are enabled' do
|
context 'when on gitlab.com' do
|
||||||
before do
|
before do
|
||||||
stub_application_setting(mailgun_events_enabled: true)
|
allow(Gitlab).to receive(:dev_env_or_com?).and_return(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has custom headers' do
|
it 'has custom headers' do
|
||||||
aggregate_failures do
|
aggregate_failures do
|
||||||
expect(subject).to have_header('X-Mailgun-Tag', ::Members::Mailgun::INVITE_EMAIL_TAG)
|
expect(subject).to have_header('X-Mailgun-Tag', 'invite_email')
|
||||||
expect(subject).to have_header('X-Mailgun-Variables', { ::Members::Mailgun::INVITE_EMAIL_TOKEN_KEY => project_member.invite_token }.to_json)
|
expect(subject).to have_header('X-Mailgun-Variables', { 'invite_token' => project_member.invite_token }.to_json)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe API::UserCounts do
|
RSpec.describe API::UserCounts do
|
||||||
let(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
let(:project) { create(:project, :public) }
|
let_it_be(:project) { create(:project, :public) }
|
||||||
|
let_it_be(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
|
||||||
|
let_it_be(:todo) { create(:todo, :pending, user: user, project: project) }
|
||||||
|
|
||||||
let!(:merge_request) { create(:merge_request, :simple, author: user, assignees: [user], source_project: project, title: "Test") }
|
let!(:merge_request) { create(:merge_request, :simple, author: user, assignees: [user], source_project: project, title: "Test") }
|
||||||
|
|
||||||
|
@ -18,22 +20,36 @@ RSpec.describe API::UserCounts do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when authenticated' do
|
context 'when authenticated' do
|
||||||
it 'returns open counts for current user' do
|
it 'returns assigned issue counts for current_user' do
|
||||||
get api('/user_counts', user)
|
get api('/user_counts', user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(json_response['assigned_issues']).to eq(1)
|
||||||
expect(json_response).to be_a Hash
|
|
||||||
expect(json_response['merge_requests']).to eq(1)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates the mr count when a new mr is assigned' do
|
context 'merge requests' do
|
||||||
create(:merge_request, source_project: project, author: user, assignees: [user])
|
it 'returns assigned MR counts for current user' do
|
||||||
|
get api('/user_counts', user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(json_response).to be_a Hash
|
||||||
|
expect(json_response['merge_requests']).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the mr count when a new mr is assigned' do
|
||||||
|
create(:merge_request, source_project: project, author: user, assignees: [user])
|
||||||
|
|
||||||
|
get api('/user_counts', user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(json_response).to be_a Hash
|
||||||
|
expect(json_response['merge_requests']).to eq(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns pending todo counts for current_user' do
|
||||||
get api('/user_counts', user)
|
get api('/user_counts', user)
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(json_response['todos']).to eq(1)
|
||||||
expect(json_response).to be_a Hash
|
|
||||||
expect(json_response['merge_requests']).to eq(2)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe 'receive a permanent failure' do
|
|
||||||
describe 'POST /members/mailgun/permanent_failures', :aggregate_failures do
|
|
||||||
let_it_be(:member) { create(:project_member, :invited) }
|
|
||||||
|
|
||||||
let(:raw_invite_token) { member.raw_invite_token }
|
|
||||||
let(:mailgun_events) { true }
|
|
||||||
let(:mailgun_signing_key) { 'abc123' }
|
|
||||||
|
|
||||||
subject(:post_request) { post members_mailgun_permanent_failures_path(standard_params) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
stub_application_setting(mailgun_events_enabled: mailgun_events, mailgun_signing_key: mailgun_signing_key)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'marks the member invite email success as false' do
|
|
||||||
expect { post_request }.to change { member.reload.invite_email_success }.from(true).to(false)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the change to a member is not made' do
|
|
||||||
context 'with incorrect signing key' do
|
|
||||||
context 'with incorrect signing key' do
|
|
||||||
let(:mailgun_signing_key) { '_foobar_' }
|
|
||||||
|
|
||||||
it 'does not change member status and responds as not_found' do
|
|
||||||
expect { post_request }.not_to change { member.reload.invite_email_success }
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:not_found)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with nil signing key' do
|
|
||||||
let(:mailgun_signing_key) { nil }
|
|
||||||
|
|
||||||
it 'does not change member status and responds as not_found' do
|
|
||||||
expect { post_request }.not_to change { member.reload.invite_email_success }
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:not_found)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the feature is not enabled' do
|
|
||||||
let(:mailgun_events) { false }
|
|
||||||
|
|
||||||
it 'does not change member status and responds as expected' do
|
|
||||||
expect { post_request }.not_to change { member.reload.invite_email_success }
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:not_acceptable)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when it is not an invite email' do
|
|
||||||
before do
|
|
||||||
stub_const('::Members::Mailgun::INVITE_EMAIL_TAG', '_foobar_')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not change member status and responds as expected' do
|
|
||||||
expect { post_request }.not_to change { member.reload.invite_email_success }
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:not_acceptable)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def standard_params
|
|
||||||
{
|
|
||||||
"signature": {
|
|
||||||
"timestamp": "1625056677",
|
|
||||||
"token": "eb944d0ace7227667a1b97d2d07276ae51d2b849ed2cfa68f3",
|
|
||||||
"signature": "9790cc6686eb70f0b1f869180d906870cdfd496d27fee81da0aa86b9e539e790"
|
|
||||||
},
|
|
||||||
"event-data": {
|
|
||||||
"severity": "permanent",
|
|
||||||
"tags": ["invite_email"],
|
|
||||||
"timestamp": 1521233195.375624,
|
|
||||||
"storage": {
|
|
||||||
"url": "_anything_",
|
|
||||||
"key": "_anything_"
|
|
||||||
},
|
|
||||||
"log-level": "error",
|
|
||||||
"id": "_anything_",
|
|
||||||
"campaigns": [],
|
|
||||||
"reason": "suppress-bounce",
|
|
||||||
"user-variables": {
|
|
||||||
"invite_token": raw_invite_token
|
|
||||||
},
|
|
||||||
"flags": {
|
|
||||||
"is-routed": false,
|
|
||||||
"is-authenticated": true,
|
|
||||||
"is-system-test": false,
|
|
||||||
"is-test-mode": false
|
|
||||||
},
|
|
||||||
"recipient-domain": "example.com",
|
|
||||||
"envelope": {
|
|
||||||
"sender": "bob@mg.gitlab.com",
|
|
||||||
"transport": "smtp",
|
|
||||||
"targets": "alice@example.com"
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"headers": {
|
|
||||||
"to": "Alice <alice@example.com>",
|
|
||||||
"message-id": "20130503192659.13651.20287@mg.gitlab.com",
|
|
||||||
"from": "Bob <bob@mg.gitlab.com>",
|
|
||||||
"subject": "Test permanent_fail webhook"
|
|
||||||
},
|
|
||||||
"attachments": [],
|
|
||||||
"size": 111
|
|
||||||
},
|
|
||||||
"recipient": "alice@example.com",
|
|
||||||
"event": "failed",
|
|
||||||
"delivery-status": {
|
|
||||||
"attempt-no": 1,
|
|
||||||
"message": "",
|
|
||||||
"code": 605,
|
|
||||||
"description": "Not delivering to previously bounced address",
|
|
||||||
"session-seconds": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,42 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe Members::Mailgun::ProcessWebhookService do
|
|
||||||
describe '#execute', :aggregate_failures do
|
|
||||||
let_it_be(:member) { create(:project_member, :invited) }
|
|
||||||
|
|
||||||
let(:raw_invite_token) { member.raw_invite_token }
|
|
||||||
let(:payload) { { 'user-variables' => { ::Members::Mailgun::INVITE_EMAIL_TOKEN_KEY => raw_invite_token } } }
|
|
||||||
|
|
||||||
subject(:service) { described_class.new(payload).execute }
|
|
||||||
|
|
||||||
it 'marks the member invite email success as false' do
|
|
||||||
expect(Gitlab::AppLogger).to receive(:info).with(/^UPDATED MEMBER INVITE_EMAIL_SUCCESS/).and_call_original
|
|
||||||
|
|
||||||
expect { service }.to change { member.reload.invite_email_success }.from(true).to(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when member can not be found' do
|
|
||||||
let(:raw_invite_token) { '_foobar_' }
|
|
||||||
|
|
||||||
it 'does not change member status' do
|
|
||||||
expect(Gitlab::AppLogger).not_to receive(:info).with(/^UPDATED MEMBER INVITE_EMAIL_SUCCESS/)
|
|
||||||
|
|
||||||
expect { service }.not_to change { member.reload.invite_email_success }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when invite token is not found in payload' do
|
|
||||||
let(:payload) { {} }
|
|
||||||
|
|
||||||
it 'does not change member status and logs an error' do
|
|
||||||
expect(Gitlab::AppLogger).not_to receive(:info).with(/^UPDATED MEMBER INVITE_EMAIL_SUCCESS/)
|
|
||||||
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
|
|
||||||
an_instance_of(described_class::ProcessWebhookServiceError))
|
|
||||||
|
|
||||||
expect { service }.not_to change { member.reload.invite_email_success }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -3,10 +3,17 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe 'layouts/nav/sidebar/_group' do
|
RSpec.describe 'layouts/nav/sidebar/_group' do
|
||||||
let_it_be(:group) { create(:group) }
|
let_it_be(:owner) { create(:user) }
|
||||||
|
let_it_be(:group) do
|
||||||
|
create(:group).tap do |g|
|
||||||
|
g.add_owner(owner)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
assign(:group, group)
|
assign(:group, group)
|
||||||
|
|
||||||
|
allow(view).to receive(:current_user).and_return(owner)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'has nav sidebar'
|
it_behaves_like 'has nav sidebar'
|
||||||
|
@ -79,4 +86,18 @@ RSpec.describe 'layouts/nav/sidebar/_group' do
|
||||||
expect(rendered).to have_css('span.badge.badge-pill.merge_counter.js-merge-counter')
|
expect(rendered).to have_css('span.badge.badge-pill.merge_counter.js-merge-counter')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'CI/CD' do
|
||||||
|
it 'has a default link to the runners list path' do
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).to have_link('CI/CD', href: group_runners_path(group))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has a link to the runners list page' do
|
||||||
|
render
|
||||||
|
|
||||||
|
expect(rendered).to have_link('Runners', href: group_runners_path(group))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue