Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-06-28 18:08:39 +00:00
parent 59469a1782
commit e4632f4c63
45 changed files with 624 additions and 517 deletions

View File

@ -33,6 +33,7 @@ Documentation-related MRs should be reviewed by a Technical Writer for a non-blo
- [ ] The headings should be something you'd do a Google search for. Instead of `Default behavior`, say something like `Default behavior when you close an issue`.
- [ ] The headings (other than the page title) should be active. Instead of `Configuring GDK`, say something like `Configure GDK`.
- [ ] Any task steps should be written as a numbered list.
- If the content still needs to be edited for topic types, you can create a follow-up issue with the ~"docs-technical-debt" label.
- [ ] Review by assigned maintainer, who can always request/require the above reviews. Maintainer's review can occur before or after a technical writer review.
- [ ] Ensure a release milestone is set.

View File

@ -1 +1 @@
b6648dea882b755e747d7d6be7a2355a9f28b6ae
28e6eebf54f40a2ff820c996698c720b1a898266

View File

@ -124,7 +124,7 @@ gem 'fog-aws', '~> 3.9'
# Locked until fog-google resolves https://github.com/fog/fog-google/issues/421.
# Also see config/initializers/fog_core_patch.rb.
gem 'fog-core', '= 2.1.0'
gem 'gitlab-fog-google', '~> 1.13', require: 'fog/google'
gem 'fog-google', '~> 1.15', require: 'fog/google'
gem 'fog-local', '~> 0.6'
gem 'fog-openstack', '~> 1.0'
gem 'fog-rackspace', '~> 0.1.1'

View File

@ -412,6 +412,12 @@ GEM
excon (~> 0.58)
formatador (~> 0.2)
mime-types
fog-google (1.15.0)
fog-core (<= 2.1.0)
fog-json (~> 1.2)
fog-xml (~> 0.1.0)
google-api-client (>= 0.44.2, < 0.51)
google-cloud-env (~> 1.2)
fog-json (1.2.0)
fog-core
multi_json (~> 1.10)
@ -475,13 +481,6 @@ GEM
fog-json (~> 1.2.0)
mime-types
ms_rest_azure (~> 0.12.0)
gitlab-fog-google (1.13.0)
addressable (>= 2.7.0)
fog-core (<= 2.1.0)
fog-json (~> 1.2)
fog-xml (~> 0.1.0)
google-api-client (>= 0.44.2, < 0.51)
google-cloud-env (~> 1.2)
gitlab-labkit (0.18.0)
actionpack (>= 5.0.0, < 7.0.0)
activesupport (>= 5.0.0, < 7.0.0)
@ -531,7 +530,7 @@ GEM
retriable (>= 2.0, < 4.0)
rexml
signet (~> 0.12)
google-cloud-env (1.4.0)
google-cloud-env (1.5.0)
faraday (>= 0.17.3, < 2.0)
google-protobuf (3.17.1)
googleapis-common-protos-types (1.0.6)
@ -1474,6 +1473,7 @@ DEPENDENCIES
fog-aliyun (~> 0.3)
fog-aws (~> 3.9)
fog-core (= 2.1.0)
fog-google (~> 1.15)
fog-local (~> 0.6)
fog-openstack (~> 1.0)
fog-rackspace (~> 0.1.1)
@ -1489,7 +1489,6 @@ DEPENDENCIES
gitlab-dangerfiles (~> 2.1.2)
gitlab-experiment (~> 0.5.4)
gitlab-fog-azure-rm (~> 1.1.1)
gitlab-fog-google (~> 1.13)
gitlab-labkit (~> 0.18.0)
gitlab-license (~> 1.5)
gitlab-mail_room (~> 0.0.9)

View File

@ -155,6 +155,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-deprecated-functional-template -->
<template functional>
<div :class="$options.classNameMap(props)" class="diff-grid-row diff-tr line_holder">
<div

View File

@ -296,7 +296,7 @@ class Group < Namespace
end
def add_users(users, access_level, current_user: nil, expires_at: nil)
GroupMember.add_users(
Members::Groups::CreatorService.add_users( # rubocop:todo CodeReuse/ServiceClass
self,
users,
access_level,
@ -306,14 +306,13 @@ class Group < Namespace
end
def add_user(user, access_level, current_user: nil, expires_at: nil, ldap: false)
GroupMember.add_user(
self,
user,
access_level,
current_user: current_user,
expires_at: expires_at,
ldap: ldap
)
Members::Groups::CreatorService.new(self, # rubocop:todo CodeReuse/ServiceClass
user,
access_level,
current_user: current_user,
expires_at: expires_at,
ldap: ldap)
.execute
end
def add_guest(user, current_user = nil)

View File

@ -232,140 +232,9 @@ class Member < ApplicationRecord
find_by(invite_token: invite_token)
end
def add_user(source, user, access_level, existing_members: nil, current_user: nil, expires_at: nil, ldap: false)
# rubocop: disable CodeReuse/ServiceClass
# `user` can be either a User object, User ID or an email to be invited
member = retrieve_member(source, user, existing_members)
access_level = retrieve_access_level(access_level)
return member unless can_update_member?(current_user, member)
set_member_attributes(
member,
access_level,
current_user: current_user,
expires_at: expires_at,
ldap: ldap
)
if member.request?
::Members::ApproveAccessRequestService.new(
current_user,
access_level: access_level
).execute(
member,
skip_authorization: ldap,
skip_log_audit_event: ldap
)
else
member.save
end
member
# rubocop: enable CodeReuse/ServiceClass
end
# Populates the attributes of a member.
#
# This logic resides in a separate method so that EE can extend this logic,
# without having to patch the `add_user` method directly.
def set_member_attributes(member, access_level, current_user: nil, expires_at: nil, ldap: false)
member.attributes = {
created_by: member.created_by || current_user,
access_level: access_level,
expires_at: expires_at
}
end
def add_users(source, users, access_level, current_user: nil, expires_at: nil)
return [] unless users.present?
emails, users, existing_members = parse_users_list(source, users)
self.transaction do
(emails + users).map! do |user|
add_user(
source,
user,
access_level,
existing_members: existing_members,
current_user: current_user,
expires_at: expires_at
)
end
end
end
def access_levels
Gitlab::Access.sym_options
end
def valid_email?(email)
Devise.email_regexp.match?(email)
end
private
def parse_users_list(source, list)
emails = []
user_ids = []
users = []
existing_members = {}
list.each do |item|
case item
when User
users << item
when Integer
user_ids << item
when /\A\d+\Z/
user_ids << item.to_i
when Devise.email_regexp
emails << item
end
end
if user_ids.present?
users.concat(User.where(id: user_ids))
# the below will automatically discard invalid user_ids
existing_members = source.members_and_requesters.where(user_id: user_ids).index_by(&:user_id)
end
[emails, users, existing_members]
end
# This method is used to find users that have been entered into the "Add members" field.
# These can be the User objects directly, their IDs, their emails, or new emails to be invited.
def retrieve_user(user)
return user if user.is_a?(User)
return User.find_by(id: user) if user.is_a?(Integer)
User.find_by_any_email(user) || user
end
def retrieve_member(source, user, existing_members)
user = retrieve_user(user)
if user.is_a?(User)
if existing_members
existing_members[user.id] || source.members.build(user_id: user.id)
else
source.members_and_requesters.find_or_initialize_by(user_id: user.id)
end
else
source.members.build(invite_email: user)
end
end
def retrieve_access_level(access_level)
access_levels.fetch(access_level) { access_level.to_i }
end
def can_update_member?(current_user, member)
# There is no current user for bulk actions, in which case anything is allowed
!current_user || current_user.can?(:"update_#{member.type.underscore}", member)
end
end
def real_source_type

View File

@ -32,10 +32,6 @@ class GroupMember < Member
Gitlab::Access.options_with_owner
end
def self.access_levels
Gitlab::Access.sym_options_with_owner
end
def self.pluck_user_ids
pluck(:user_id)
end

View File

@ -48,7 +48,7 @@ class ProjectMember < Member
project_ids.each do |project_id|
project = Project.find(project_id)
add_users(
Members::Projects::CreatorService.add_users( # rubocop:todo CodeReuse/ServiceClass
project,
users,
access_level,
@ -80,12 +80,6 @@ class ProjectMember < Member
def access_level_roles
Gitlab::Access.options
end
private
def can_update_member?(current_user, member)
super || (member.owner? && member.new_record?)
end
end
def project

View File

@ -42,7 +42,7 @@ class ProjectTeam
end
def add_users(users, access_level, current_user: nil, expires_at: nil)
ProjectMember.add_users(
Members::Projects::CreatorService.add_users( # rubocop:todo CodeReuse/ServiceClass
project,
users,
access_level,
@ -52,13 +52,12 @@ class ProjectTeam
end
def add_user(user, access_level, current_user: nil, expires_at: nil)
ProjectMember.add_user(
project,
user,
access_level,
current_user: current_user,
expires_at: expires_at
)
Members::Projects::CreatorService.new(project, # rubocop:todo CodeReuse/ServiceClass
user,
access_level,
current_user: current_user,
expires_at: expires_at)
.execute
end
# Remove all users from project team

View File

@ -0,0 +1,172 @@
# frozen_string_literal: true
module Members
# This class serves as more of an app-wide way we add/create members
# All roads to add members should take this path.
class CreatorService
class << self
def parsed_access_level(access_level)
access_levels.fetch(access_level) { access_level.to_i }
end
def access_levels
raise NotImplementedError
end
def add_users(source, users, access_level, current_user: nil, expires_at: nil)
return [] unless users.present?
emails, users, existing_members = parse_users_list(source, users)
Member.transaction do
(emails + users).map! do |user|
new(source,
user,
access_level,
existing_members: existing_members,
current_user: current_user,
expires_at: expires_at)
.execute
end
end
end
private
def parse_users_list(source, list)
emails = []
user_ids = []
users = []
existing_members = {}
list.each do |item|
case item
when User
users << item
when Integer
user_ids << item
when /\A\d+\Z/
user_ids << item.to_i
when Devise.email_regexp
emails << item
end
end
if user_ids.present?
users.concat(User.id_in(user_ids))
# the below will automatically discard invalid user_ids
existing_members = source.members_and_requesters.where(user_id: user_ids).index_by(&:user_id) # rubocop:todo CodeReuse/ActiveRecord
end
[emails, users, existing_members]
end
end
def initialize(source, user, access_level, **args)
@source = source
@user = user
@access_level = self.class.parsed_access_level(access_level)
@args = args
end
def execute
find_or_build_member
update_member
member
end
private
attr_reader :source, :user, :access_level, :member, :args
def update_member
return unless can_update_member?
member.attributes = member_attributes
if member.request?
approve_request
else
member.save
end
end
def can_update_member?
# There is no current user for bulk actions, in which case anything is allowed
!current_user # inheriting classes will add more logic
end
# Populates the attributes of a member.
#
# This logic resides in a separate method so that EE can extend this logic,
# without having to patch the `add_user` method directly.
def member_attributes
{
created_by: member.created_by || current_user,
access_level: access_level,
expires_at: args[:expires_at]
}
end
def approve_request
::Members::ApproveAccessRequestService.new(current_user,
access_level: access_level)
.execute(
member,
skip_authorization: ldap,
skip_log_audit_event: ldap
)
end
def current_user
args[:current_user]
end
def find_or_build_member
@user = parse_user_param
@member = if user.is_a?(User)
find_or_initialize_member_by_user
else
source.members.build(invite_email: user)
end
end
# This method is used to find users that have been entered into the "Add members" field.
# These can be the User objects directly, their IDs, their emails, or new emails to be invited.
def parse_user_param
case user
when User
user
when Integer
# might not return anything - this needs enhancement
User.find_by(id: user) # rubocop:todo CodeReuse/ActiveRecord
else
# must be an email or at least we'll consider it one
User.find_by_any_email(user) || user
end
end
def find_or_initialize_member_by_user
if existing_members
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/334062
# i'm not so sure this is needed as the parse_users_list looks at members_and_requesters...
# so it is like we could just do a find or initialize by here and be fine
existing_members[user.id] || source.members.build(user_id: user.id)
else
source.members_and_requesters.find_or_initialize_by(user_id: user.id) # rubocop:todo CodeReuse/ActiveRecord
end
end
def existing_members
args[:existing_members]
end
def ldap
args[:ldap] || false
end
end
end
Members::CreatorService.prepend_mod_with('Members::CreatorService')

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
module Members
module Groups
class CreatorService < Members::CreatorService
def self.access_levels
Gitlab::Access.sym_options_with_owner
end
private
def can_update_member?
super || current_user.can?(:update_group_member, member)
end
end
end
end

View File

@ -21,7 +21,7 @@ module Members
def validate_invites!
super
# we need the below due to add_users hitting Member#parse_users_list and ignoring invalid emails
# we need the below due to add_users hitting Members::CreatorService.parse_users_list and ignoring invalid emails
# ideally we wouldn't need this, but we can't really change the add_users method
valid, invalid = invites.partition { |email| Member.valid_email?(email) }
@invites = valid

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
module Members
module Projects
class CreatorService < Members::CreatorService
def self.access_levels
Gitlab::Access.sym_options
end
private
def can_update_member?
super || current_user.can?(:update_project_member, member) || adding_a_new_owner?
end
def adding_a_new_owner?
# this condition is reached during testing setup a lot due to use of `.add_user`
member.owner? && member.new_record?
end
end
end
end

View File

@ -231,7 +231,7 @@ standard Azure B2C user flows [do not send the OpenID `email` claim](https://git
other words, they do not work with the [`allow_single_sign_on` or `auto_link_user`
parameters](../../integration/omniauth.md#initial-omniauth-configuration).
With a standard Azure B2C policy, GitLab cannot create a new account or
link to an existing one with an e-mail address.
link to an existing one with an email address.
Carefully follow the instructions for [creating a custom policy](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-custom-policy).
@ -330,7 +330,7 @@ gitlab_rails['omniauth_providers'] = [
the respective client IDs in the XML policy files.
- Add `https://jwt.ms` as a redirect URI to the app, and use the [custom policy tester](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-custom-policy#test-the-custom-policy).
Make sure the payload includes `email` that matches the user's e-mail access.
Make sure the payload includes `email` that matches the user's email access.
- After you enable the custom policy, users might see "Invalid username or password" after they try to sign in. This might be a configuration
issue with the `IdentityExperienceFramework` app. See [this Microsoft comment](https://docs.microsoft.com/en-us/answers/questions/50355/unable-to-sign-on-using-custom-policy.html?childToView=122370#comment-122370)

View File

@ -16,7 +16,7 @@ GitLab has several features based on receiving incoming email messages:
- [New merge request by email](../user/project/merge_requests/creating_merge_requests.md#new-merge-request-by-email):
allow GitLab users to create a new merge request by sending an email to a
user-specific email address.
- [Service Desk](../user/project/service_desk.md): provide e-mail support to
- [Service Desk](../user/project/service_desk.md): provide email support to
your customers through GitLab.
## Requirements

View File

@ -119,7 +119,7 @@ an SMTP server, but you're not seeing mail delivered. Here's how to check the se
irb(main):003:0> Notify.test_email('youremail@email.com', 'Hello World', 'This is a test message').deliver_now
```
If you do not receive an e-mail and/or see an error message, then check
If you do not receive an email and/or see an error message, then check
your mail server settings.
## Advanced Issues

View File

@ -20,7 +20,7 @@ POST /import/github
| `repo_id` | integer | yes | GitHub repository ID |
| `new_name` | string | no | New repository name |
| `target_namespace` | string | yes | Namespace to import repository into. Supports subgroups like `/namespace/subgroup`. |
| `github_hostname` | string | no | Custom GitHub enterprise hostname. Defaults to GitHub.com if `github_hostname` is not set. |
| `github_hostname` | string | no | Custom GitHub Enterprise hostname. Do not set for GitHub.com. |
```shell
curl --request POST \
@ -31,7 +31,8 @@ curl --request POST \
"personal_access_token": "aBc123abC12aBc123abC12abC123+_A/c123",
"repo_id": "12345",
"target_namespace": "group/subgroup",
"new_name": "NEW-NAME"
"new_name": "NEW-NAME",
"github_hostname": "https://github.example.com"
}'
```

View File

@ -262,16 +262,18 @@ GET /groups/:id/billable_members
The supported values for the `sort` attribute are:
| Value | Description |
| ------------------- | ------------------------ |
| `access_level_asc` | Access level, ascending |
| `access_level_desc` | Access level, descending |
| `last_joined` | Last joined |
| `name_asc` | Name, ascending |
| `name_desc` | Name, descending |
| `oldest_joined` | Oldest joined |
| `oldest_sign_in` | Oldest sign in |
| `recent_sign_in` | Recent sign in |
| Value | Description |
| ----------------------- | ---------------------------- |
| `access_level_asc` | Access level, ascending |
| `access_level_desc` | Access level, descending |
| `last_joined` | Last joined |
| `name_asc` | Name, ascending |
| `name_desc` | Name, descending |
| `oldest_joined` | Oldest joined |
| `oldest_sign_in` | Oldest sign in |
| `recent_sign_in` | Recent sign in |
| `last_activity_on_asc` | Last active date, ascending |
| `last_activity_on_desc` | Last active date, descending |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/billable_members"

View File

@ -258,7 +258,7 @@ listed in the descriptions of the relevant settings.
| `disabled_oauth_sign_in_sources` | array of strings | no | Disabled OAuth sign-in sources. |
| `dns_rebinding_protection_enabled` | boolean | no | Enforce DNS rebinding attack protection. |
| `domain_denylist_enabled` | boolean | no | (**If enabled, requires:** `domain_denylist`) Allows blocking sign-ups from emails from specific domains. |
| `domain_denylist` | array of strings | no | Users with e-mail addresses that match these domain(s) **cannot** sign up. Wildcards allowed. Use separate lines for multiple entries. Ex: `domain.com`, `*.domain.com`. |
| `domain_denylist` | array of strings | no | Users with email addresses that match these domain(s) **cannot** sign up. Wildcards allowed. Use separate lines for multiple entries. Ex: `domain.com`, `*.domain.com`. |
| `domain_allowlist` | array of strings | no | Force people to use only corporate emails for sign-up. Default is `null`, meaning there is no restriction. |
| `dsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded DSA key. Default is `0` (no restriction). `-1` disables DSA keys. |
| `ecdsa_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ECDSA key. Default is `0` (no restriction). `-1` disables ECDSA keys. |
@ -413,7 +413,7 @@ listed in the descriptions of the relevant settings.
| `unique_ips_limit_time_window` | integer | required by: `unique_ips_limit_enabled` | How many seconds an IP is counted towards the limit. |
| `usage_ping_enabled` | boolean | no | Every week GitLab reports license usage back to GitLab, Inc. |
| `user_default_external` | boolean | no | Newly registered users are external by default. |
| `user_default_internal_regex` | string | no | Specify an e-mail address regex pattern to identify default internal users. |
| `user_default_internal_regex` | string | no | Specify an email address regex pattern to identify default internal users. |
| `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider. |
| `user_show_add_ssh_key_message` | boolean | no | When set to `false` disable the `You won't be able to pull or push project code via SSH` warning shown to users with no uploaded SSH key. |
| `version_check_enabled` | boolean | no | Let GitLab inform you when an update is available. |

View File

@ -1352,7 +1352,7 @@ Parameters:
- `id` (required) - ID of specified user
- `email` (required) - email address
- `skip_confirmation` (optional) - Skip confirmation and assume e-mail is verified - true or false (default)
- `skip_confirmation` (optional) - Skip confirmation and assume email is verified - true or false (default)
## Delete email for current user

View File

@ -91,8 +91,32 @@ To skip some checks based on tags when pushing, you can set the `LEFTHOOK_EXCLUD
LEFTHOOK_EXCLUDE=frontend,documentation git push ...
```
As an alternative, you can create `lefthook-local.yml` with this structure:
```yaml
pre-push:
exclude_tags:
- frontend
- documentation
```
For more information, check out [Lefthook documentation](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#skip-some-tags-on-the-fly).
### Skip or enable a specific Lefthook check
To skip or enable a check based on its name when pushing, you can add `skip: true`
or `skip: false` to the `lefthook-local.yml` section for that hook. For instance,
you might want to enable the gettext check to detect issues with `locale/gitlab.pot`:
```yaml
pre-push:
commands:
gettext:
skip: false
```
For more information, check out [this Lefthook documentation section](https://github.com/evilmartians/lefthook/blob/master/docs/full_guide.md#skipping-commands).
## Ruby, Rails, RSpec
Our codebase style is defined and enforced by [RuboCop](https://github.com/rubocop-hq/rubocop).

View File

@ -60,6 +60,10 @@ Do not use. If the user doesn't find the process to be these things, we lose the
Do not use Latin abbreviations. Use **for example**, **such as**, **for instance**, or **like** instead. ([Vale](../testing.md#vale) rule: [`LatinTerms.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/LatinTerms.yml))
## email
Do not use **e-mail** with a hyphen. When plural, use **emails** or **email messages**.
## enable
See [the Microsoft style guide](https://docs.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/e/enable-enables) for guidance.

View File

@ -243,7 +243,7 @@ Ruby on Rails applications. In GitLab, Sidekiq performs the heavy
lifting of many activities, including:
- Updating merge requests after a push.
- Sending e-mails.
- Sending email messages.
- Updating user authorizations.
- Processing CI builds and pipelines.
@ -276,7 +276,7 @@ in a timely manner:
this to `ProcessCommitWorker`.
- Redistribute/gerrymander Sidekiq processes by queue
types. Long-running jobs (for example, relating to project import) can often
squeeze out jobs that run fast (for example, delivering e-mail). [This technique
squeeze out jobs that run fast (for example, delivering email). [This technique
was used in to optimize our existing Sidekiq deployment](https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/7219#note_218019483).
- Optimize jobs. Eliminating unnecessary work, reducing network calls
(including SQL and Gitaly), and optimizing processor time can yield significant

View File

@ -67,7 +67,7 @@ The following changes are needed to enable Shibboleth:
your users appear to be authenticated by Shibboleth and Apache, but GitLab
rejects their account with a URI that contains "e-mail is invalid" then your
Shibboleth Identity Provider or Attribute Authority may be asserting multiple
e-mail addresses. In this instance, you might consider setting the
email addresses. In this instance, you might consider setting the
`multi_values` argument to `first`.
The file should look like this:

View File

@ -392,10 +392,10 @@ end
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36617) in GitLab 13.2.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/251234) in GitLab 13.5.
> - [Updated](https://gitlab.com/gitlab-org/gitlab/-/issues/220333) in GitLab 14.1
> - Showing related feature flags in issues [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220333) in GitLab 14.1.
You can link related issues to a feature flag. In the **Linked issues** section,
click the `+` button and input the issue reference number or the full URL of the issue.
The issue(s) will then appear in the related feautre flag and vice versa.
The issues then appear in the related feature flag and the other way round.
This feature is similar to the [linked issues](../user/project/issues/related_issues.md) feature.

View File

@ -14,7 +14,7 @@ reports in the Admin Area.
## Receiving notifications of abuse reports
To receive notifications of new abuse reports by e-mail, follow these steps:
To receive notifications of new abuse reports by email, follow these steps:
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Settings > Reporting**.

View File

@ -70,7 +70,7 @@ For more details on the specific data persisted in a group export, see the
![Export group panel](img/export_panel_v13_0.png)
1. After the export is generated, you should receive an e-mail with a link to the [exported contents](#exported-contents)
1. After the export is generated, you should receive an email with a link to the [exported contents](#exported-contents)
in a compressed tar archive, with contents in NDJSON format.
1. Alternatively, you can come back to the project settings and download the

View File

@ -51,7 +51,7 @@ The Bitbucket Server importer works as follows:
### User assignment
When issues/pull requests are being imported, the Bitbucket importer tries to
find the author's e-mail address with a confirmed e-mail address in the GitLab
find the author's email address with a confirmed email address in the GitLab
user database. If no such user is available, the project creator is set as
the author. The importer appends a note in the comment to mark the original
creator.

View File

@ -231,7 +231,7 @@ _In GitLab 11.7, we updated the format of the generated email address.
However the older format is still supported, allowing existing aliases
or contacts to continue working._
### Adding patches when creating a merge request via e-mail
### Adding patches when creating a merge request via email
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/22723) in GitLab 11.5.

View File

@ -60,7 +60,7 @@ might be slightly different. Follow the
sudo certbot certonly -a manual -d example.com --email your@email.com
```
Alternatively, you can register without adding an e-mail account,
Alternatively, you can register without adding an email account,
but you aren't notified about the certificate expiration's date:
```shell

View File

@ -234,7 +234,7 @@ The configuration options are the same as for configuring
> Introduced in [GitLab 13.11](https://gitlab.com/gitlab-org/gitlab/-/issues/214900)
Service Desk can be configured to read Microsoft Exchange Online mailboxes with the Microsoft
Graph API instead of IMAP. Follow the [documentation in the incoming e-mail section for setting up an OAuth2 application for Microsoft Graph](../../administration/incoming_email.md#microsoft-graph).
Graph API instead of IMAP. Follow the [documentation in the incoming email section for setting up an OAuth2 application for Microsoft Graph](../../administration/incoming_email.md#microsoft-graph).
- Example for Omnibus GitLab installations:

View File

@ -156,7 +156,7 @@ To export a project and its data, follow these steps:
![Export button](img/import_export_export_button.png)
1. Once the export is generated, you should receive an e-mail with a link to
1. Once the export is generated, you should receive an email with a link to
download the file:
![Email download link](img/import_export_mail_link.png)

View File

@ -38,3 +38,9 @@ pre-push:
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
glob: 'doc/*.md'
run: if command -v vale 2> /dev/null; then vale --config .vale.ini --minAlertLevel error {files}; else echo "Vale not found. Install Vale"; fi
gettext:
skip: true # This is disabled by default. You can enable this check by adding skip: false in lefhook-local.yml https://github.com/evilmartians/lefthook/blob/master/docs/full_guide.md#skipping-commands
tags: backend frontend view haml
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD | while read file;do git diff --unified=1 $(git merge-base origin/master HEAD)..HEAD $file | grep -Fqe '_(' && echo $file;done; true
glob: "*.{haml,rb,js,vue}"
run: bin/rake gettext:updated_check

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
module Gitlab
class BackupLogger < Gitlab::JsonLogger
def self.file_name_noext
'backup_json'
end
end
end

View File

@ -42,10 +42,6 @@ module Gitlab
end
end
def metrics_params
super.merge(project: project&.full_path)
end
def metrics_event
:receive_email_service_desk
end

View File

@ -282,6 +282,7 @@ namespace :gitlab do
def puts_time(msg)
progress.puts "#{Time.now} -- #{msg}"
Gitlab::BackupLogger.info(message: "#{Rainbow.uncolor(msg)}")
end
def progress

View File

@ -54,7 +54,7 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler do
it 'adds metric events for incoming and reply emails' do
metric_transaction = double('Gitlab::Metrics::WebTransaction', increment: true, observe: true)
allow(::Gitlab::Metrics::BackgroundTransaction).to receive(:current).and_return(metric_transaction)
expect(metric_transaction).to receive(:add_event).with(:receive_email_service_desk, anything)
expect(metric_transaction).to receive(:add_event).with(:receive_email_service_desk, { handler: 'Gitlab::Email::Handler::ServiceDeskHandler' })
expect(metric_transaction).to receive(:add_event).with(:service_desk_thank_you_email)
receiver.execute

View File

@ -508,282 +508,6 @@ RSpec.describe Member do
end
end
describe '.add_user' do
%w[project group].each do |source_type|
context "when source is a #{source_type}" do
let_it_be(:source, reload: true) { create(source_type, :public) }
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
it 'returns a <Source>Member object' do
member = described_class.add_user(source, user, :maintainer)
expect(member).to be_a "#{source_type.classify}Member".constantize
expect(member).to be_persisted
end
context 'when admin mode is enabled', :enable_admin_mode do
it 'sets members.created_by to the given admin current_user' do
member = described_class.add_user(source, user, :maintainer, current_user: admin)
expect(member.created_by).to eq(admin)
end
end
context 'when admin mode is disabled' do
it 'rejects setting members.created_by to the given admin current_user' do
member = described_class.add_user(source, user, :maintainer, current_user: admin)
expect(member.created_by).to be_nil
end
end
it 'sets members.expires_at to the given expires_at' do
member = described_class.add_user(source, user, :maintainer, expires_at: Date.new(2016, 9, 22))
expect(member.expires_at).to eq(Date.new(2016, 9, 22))
end
described_class.access_levels.each do |sym_key, int_access_level|
it "accepts the :#{sym_key} symbol as access level" do
expect(source.users).not_to include(user)
member = described_class.add_user(source, user.id, sym_key)
expect(member.access_level).to eq(int_access_level)
expect(source.users.reload).to include(user)
end
it "accepts the #{int_access_level} integer as access level" do
expect(source.users).not_to include(user)
member = described_class.add_user(source, user.id, int_access_level)
expect(member.access_level).to eq(int_access_level)
expect(source.users.reload).to include(user)
end
end
context 'with no current_user' do
context 'when called with a known user id' do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
described_class.add_user(source, user.id, :maintainer)
expect(source.users.reload).to include(user)
end
end
context 'when called with an unknown user id' do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
described_class.add_user(source, non_existing_record_id, :maintainer)
expect(source.users.reload).not_to include(user)
end
end
context 'when called with a user object' do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
described_class.add_user(source, user, :maintainer)
expect(source.users.reload).to include(user)
end
end
context 'when called with a requester user object' do
before do
source.request_access(user)
end
it 'adds the requester as a member' do
expect(source.users).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
expect { described_class.add_user(source, user, :maintainer) }
.to raise_error(Gitlab::Access::AccessDeniedError)
expect(source.users.reload).not_to include(user)
expect(source.requesters.reload.exists?(user_id: user)).to be_truthy
end
end
context 'when called with a known user email' do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
described_class.add_user(source, user.email, :maintainer)
expect(source.users.reload).to include(user)
end
end
context 'when called with a known user secondary email' do
let(:secondary_email) { create(:email, email: 'secondary@example.com', user: user) }
it 'adds the user as a member' do
expect(source.users).not_to include(user)
described_class.add_user(source, secondary_email.email, :maintainer)
expect(source.users.reload).to include(user)
end
end
context 'when called with an unknown user email' do
it 'creates an invited member' do
expect(source.users).not_to include(user)
described_class.add_user(source, 'user@example.com', :maintainer)
expect(source.members.invite.pluck(:invite_email)).to include('user@example.com')
end
end
context 'when called with an unknown user email starting with a number' do
it 'creates an invited member', :aggregate_failures do
email_starting_with_number = "#{user.id}_email@example.com"
described_class.add_user(source, email_starting_with_number, :maintainer)
expect(source.members.invite.pluck(:invite_email)).to include(email_starting_with_number)
expect(source.users.reload).not_to include(user)
end
end
end
context 'when current_user can update member', :enable_admin_mode do
it 'creates the member' do
expect(source.users).not_to include(user)
described_class.add_user(source, user, :maintainer, current_user: admin)
expect(source.users.reload).to include(user)
end
context 'when called with a requester user object' do
before do
source.request_access(user)
end
it 'adds the requester as a member' do
expect(source.users).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
described_class.add_user(source, user, :maintainer, current_user: admin)
expect(source.users.reload).to include(user)
expect(source.requesters.reload.exists?(user_id: user)).to be_falsy
end
end
end
context 'when current_user cannot update member' do
it 'does not create the member' do
expect(source.users).not_to include(user)
member = described_class.add_user(source, user, :maintainer, current_user: user)
expect(source.users.reload).not_to include(user)
expect(member).not_to be_persisted
end
context 'when called with a requester user object' do
before do
source.request_access(user)
end
it 'does not destroy the requester' do
expect(source.users).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
described_class.add_user(source, user, :maintainer, current_user: user)
expect(source.users.reload).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
end
end
end
context 'when member already exists' do
before do
source.add_user(user, :developer)
end
context 'with no current_user' do
it 'updates the member' do
expect(source.users).to include(user)
described_class.add_user(source, user, :maintainer)
expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
context 'when current_user can update member', :enable_admin_mode do
it 'updates the member' do
expect(source.users).to include(user)
described_class.add_user(source, user, :maintainer, current_user: admin)
expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
context 'when current_user cannot update member' do
it 'does not update the member' do
expect(source.users).to include(user)
described_class.add_user(source, user, :maintainer, current_user: user)
expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::DEVELOPER)
end
end
end
end
end
end
describe '.add_users' do
%w[project group].each do |source_type|
context "when source is a #{source_type}" do
let_it_be(:source) { create(source_type, :public) }
let_it_be(:admin) { create(:admin) }
let_it_be(:user1) { create(:user) }
let_it_be(:user2) { create(:user) }
it 'returns a <Source>Member objects' do
members = described_class.add_users(source, [user1, user2], :maintainer)
expect(members).to be_a Array
expect(members.size).to eq(2)
expect(members.first).to be_a "#{source_type.classify}Member".constantize
expect(members.first).to be_persisted
end
it 'returns an empty array' do
members = described_class.add_users(source, [], :maintainer)
expect(members).to be_a Array
expect(members).to be_empty
end
it 'supports differents formats' do
list = ['joe@local.test', admin, user1.id, user2.id.to_s]
members = described_class.add_users(source, list, :maintainer)
expect(members.size).to eq(4)
expect(members.first).to be_invite
end
end
end
end
describe '#accept_request' do
let(:member) { create(:project_member, requested_at: Time.current.utc) }

View File

@ -47,27 +47,6 @@ RSpec.describe GroupMember do
end
end
describe '.access_levels' do
it 'returns Gitlab::Access.options_with_owner' do
expect(described_class.access_levels).to eq(Gitlab::Access.sym_options_with_owner)
end
end
describe '.add_users' do
it 'adds the given users to the given group' do
group = create(:group)
users = create_list(:user, 2)
described_class.add_users(
group,
[users.first.id, users.second],
described_class::MAINTAINER
)
expect(group.users).to include(users.first, users.second)
end
end
it_behaves_like 'members notifications', :group
describe '#namespace_id' do

View File

@ -23,19 +23,6 @@ RSpec.describe ProjectMember do
end
end
describe '.add_user' do
it 'adds the user as a member' do
user = create(:user)
project = create(:project)
expect(project.users).not_to include(user)
described_class.add_user(project, user, :maintainer, current_user: project.owner)
expect(project.users.reload).to include(user)
end
end
describe '#real_source_type' do
subject { create(:project_member).real_source_type }

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Members::Groups::CreatorService do
it_behaves_like 'member creation' do
let_it_be(:source, reload: true) { create(:group, :public) }
let_it_be(:member_type) { GroupMember }
end
describe '.access_levels' do
it 'returns Gitlab::Access.options_with_owner' do
expect(described_class.access_levels).to eq(Gitlab::Access.sym_options_with_owner)
end
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Members::Projects::CreatorService do
it_behaves_like 'member creation' do
let_it_be(:source, reload: true) { create(:project, :public) }
let_it_be(:member_type) { ProjectMember }
end
describe '.access_levels' do
it 'returns Gitlab::Access.sym_options' do
expect(described_class.access_levels).to eq(Gitlab::Access.sym_options)
end
end
end

View File

@ -75,3 +75,259 @@ RSpec.shared_examples '#valid_level_roles' do |entity_name|
expect(presenter.valid_level_roles).to eq(expected_roles)
end
end
RSpec.shared_examples_for "member creation" do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
describe '#execute' do
it 'returns a Member object', :aggregate_failures do
member = described_class.new(source, user, :maintainer).execute
expect(member).to be_a member_type
expect(member).to be_persisted
end
context 'when admin mode is enabled', :enable_admin_mode do
it 'sets members.created_by to the given admin current_user' do
member = described_class.new(source, user, :maintainer, current_user: admin).execute
expect(member.created_by).to eq(admin)
end
end
context 'when admin mode is disabled' do
it 'rejects setting members.created_by to the given admin current_user' do
member = described_class.new(source, user, :maintainer, current_user: admin).execute
expect(member.created_by).to be_nil
end
end
it 'sets members.expires_at to the given expires_at' do
member = described_class.new(source, user, :maintainer, expires_at: Date.new(2016, 9, 22)).execute
expect(member.expires_at).to eq(Date.new(2016, 9, 22))
end
described_class.access_levels.each do |sym_key, int_access_level|
it "accepts the :#{sym_key} symbol as access level", :aggregate_failures do
expect(source.users).not_to include(user)
member = described_class.new(source, user.id, sym_key).execute
expect(member.access_level).to eq(int_access_level)
expect(source.users.reload).to include(user)
end
it "accepts the #{int_access_level} integer as access level", :aggregate_failures do
expect(source.users).not_to include(user)
member = described_class.new(source, user.id, int_access_level).execute
expect(member.access_level).to eq(int_access_level)
expect(source.users.reload).to include(user)
end
end
context 'with no current_user' do
context 'when called with a known user id' do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
described_class.new(source, user.id, :maintainer).execute
expect(source.users.reload).to include(user)
end
end
context 'when called with an unknown user id' do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
described_class.new(source, non_existing_record_id, :maintainer).execute
expect(source.users.reload).not_to include(user)
end
end
context 'when called with a user object' do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
described_class.new(source, user, :maintainer).execute
expect(source.users.reload).to include(user)
end
end
context 'when called with a requester user object' do
before do
source.request_access(user)
end
it 'adds the requester as a member', :aggregate_failures do
expect(source.users).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
expect do
described_class.new(source, user, :maintainer).execute
end.to raise_error(Gitlab::Access::AccessDeniedError)
expect(source.users.reload).not_to include(user)
expect(source.requesters.reload.exists?(user_id: user)).to be_truthy
end
end
context 'when called with a known user email' do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
described_class.new(source, user.email, :maintainer).execute
expect(source.users.reload).to include(user)
end
end
context 'when called with an unknown user email' do
it 'creates an invited member' do
expect(source.users).not_to include(user)
described_class.new(source, 'user@example.com', :maintainer).execute
expect(source.members.invite.pluck(:invite_email)).to include('user@example.com')
end
end
context 'when called with an unknown user email starting with a number' do
it 'creates an invited member', :aggregate_failures do
email_starting_with_number = "#{user.id}_email@example.com"
described_class.new(source, email_starting_with_number, :maintainer).execute
expect(source.members.invite.pluck(:invite_email)).to include(email_starting_with_number)
expect(source.users.reload).not_to include(user)
end
end
end
context 'when current_user can update member', :enable_admin_mode do
it 'creates the member' do
expect(source.users).not_to include(user)
described_class.new(source, user, :maintainer, current_user: admin).execute
expect(source.users.reload).to include(user)
end
context 'when called with a requester user object' do
before do
source.request_access(user)
end
it 'adds the requester as a member', :aggregate_failures do
expect(source.users).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
described_class.new(source, user, :maintainer, current_user: admin).execute
expect(source.users.reload).to include(user)
expect(source.requesters.reload.exists?(user_id: user)).to be_falsy
end
end
end
context 'when current_user cannot update member' do
it 'does not create the member', :aggregate_failures do
expect(source.users).not_to include(user)
member = described_class.new(source, user, :maintainer, current_user: user).execute
expect(source.users.reload).not_to include(user)
expect(member).not_to be_persisted
end
context 'when called with a requester user object' do
before do
source.request_access(user)
end
it 'does not destroy the requester', :aggregate_failures do
expect(source.users).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
described_class.new(source, user, :maintainer, current_user: user).execute
expect(source.users.reload).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
end
end
end
context 'when member already exists' do
before do
source.add_user(user, :developer)
end
context 'with no current_user' do
it 'updates the member' do
expect(source.users).to include(user)
described_class.new(source, user, :maintainer).execute
expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
context 'when current_user can update member', :enable_admin_mode do
it 'updates the member' do
expect(source.users).to include(user)
described_class.new(source, user, :maintainer, current_user: admin).execute
expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
context 'when current_user cannot update member' do
it 'does not update the member' do
expect(source.users).to include(user)
described_class.new(source, user, :maintainer, current_user: user).execute
expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::DEVELOPER)
end
end
end
end
describe '.add_users' do
let_it_be(:user1) { create(:user) }
let_it_be(:user2) { create(:user) }
it 'returns a Member objects' do
members = described_class.add_users(source, [user1, user2], :maintainer)
expect(members).to be_a Array
expect(members.size).to eq(2)
expect(members.first).to be_a member_type
expect(members.first).to be_persisted
end
it 'returns an empty array' do
members = described_class.add_users(source, [], :maintainer)
expect(members).to be_a Array
expect(members).to be_empty
end
it 'supports different formats' do
list = ['joe@local.test', admin, user1.id, user2.id.to_s]
members = described_class.add_users(source, list, :maintainer)
expect(members.size).to eq(4)
expect(members.first).to be_invite
end
end
end

View File

@ -209,6 +209,23 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do
expect { run_rake_task("gitlab:backup:#{task}:create") }.to output(/Dumping /).to_stdout_from_any_process
end
end
it 'logs the progress to log file' do
expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping database ... ")
expect(Gitlab::BackupLogger).to receive(:info).with(message: "[SKIPPED]")
expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping repositories ...")
expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping uploads ... ")
expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping builds ... ")
expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping artifacts ... ")
expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping pages ... ")
expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping lfs objects ... ")
expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping container registry images ... ")
expect(Gitlab::BackupLogger).to receive(:info).with(message: "done").exactly(7).times
task_list.each do |task|
run_rake_task("gitlab:backup:#{task}:create")
end
end
end
end