Skip to content
Snippets Groups Projects
Commit f54a50aa authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent c77fda90
No related branches found
No related tags found
No related merge requests found
Showing
with 233 additions and 69 deletions
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import { n__ } from '~/locale';
 
export default {
name: 'AssigneeTitle',
components: {
GlLoadingIcon,
},
props: {
loading: {
type: Boolean,
Loading
Loading
@@ -34,7 +38,7 @@ export default {
<template>
<div class="title hide-collapsed">
{{ assigneeTitle }}
<i v-if="loading" aria-hidden="true" class="fa fa-spinner fa-spin block-loading"></i>
<gl-loading-icon v-if="loading" inline class="align-bottom" />
<a
v-if="editable"
class="js-sidebar-dropdown-toggle edit-link float-right"
Loading
Loading
Loading
Loading
@@ -15,9 +15,6 @@ module Clusters
include ::Clusters::Concerns::ApplicationData
include ::Gitlab::Utils::StrongMemoize
 
include IgnorableColumns
ignore_column :kibana_hostname, remove_with: '12.9', remove_after: '2020-02-22'
default_value_for :version, VERSION
 
def chart
Loading
Loading
Loading
Loading
@@ -44,6 +44,8 @@ class Issue < ApplicationRecord
has_many :assignees, class_name: "User", through: :issue_assignees
has_many :zoom_meetings
has_many :user_mentions, class_name: "IssueUserMention", dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_many :sent_notifications, as: :noteable
has_one :sentry_issue
 
accepts_nested_attributes_for :sentry_issue
Loading
Loading
Loading
Loading
@@ -3,7 +3,13 @@
class Milestone < ApplicationRecord
# Represents a "No Milestone" state used for filtering Issues and Merge
# Requests that have no milestone assigned.
MilestoneStruct = Struct.new(:title, :name, :id)
MilestoneStruct = Struct.new(:title, :name, :id) do
# Ensure these models match the interface required for exporting
def serializable_hash(_opts = {})
{ title: title, name: name, id: id }
end
end
None = MilestoneStruct.new('No Milestone', 'No Milestone', 0)
Any = MilestoneStruct.new('Any Milestone', '', -1)
Upcoming = MilestoneStruct.new('Upcoming', '#upcoming', -2)
Loading
Loading
@@ -128,11 +134,12 @@ class Milestone < ApplicationRecord
reorder(nil).group(:state).count
end
 
def predefined_id?(id)
[Any.id, None.id, Upcoming.id, Started.id].include?(id)
end
def predefined?(milestone)
milestone == Any ||
milestone == None ||
milestone == Upcoming ||
milestone == Started
predefined_id?(milestone&.id)
end
end
 
Loading
Loading
Loading
Loading
@@ -47,7 +47,7 @@ module Projects
 
private
 
def trash_repositories!
def trash_project_repositories!
unless remove_repository(project.repository)
raise_error(s_('DeleteProject|Failed to remove project repository. Please try again or contact administrator.'))
end
Loading
Loading
@@ -57,6 +57,18 @@ module Projects
end
end
 
def trash_relation_repositories!
unless remove_snippets
raise_error(s_('DeleteProject|Failed to remove project snippets. Please try again or contact administrator.'))
end
end
def remove_snippets
response = Snippets::BulkDestroyService.new(current_user, project.snippets).execute
response.success?
end
def remove_repository(repository)
return true unless repository
 
Loading
Loading
@@ -95,7 +107,8 @@ module Projects
 
Project.transaction do
log_destroy_event
trash_repositories!
trash_relation_repositories!
trash_project_repositories!
 
# Rails attempts to load all related records into memory before
# destroying: https://github.com/rails/rails/issues/22510
Loading
Loading
@@ -103,7 +116,7 @@ module Projects
#
# Exclude container repositories because its before_destroy would be
# called multiple times, and it doesn't destroy any database records.
project.destroy_dependent_associations_in_batches(exclude: [:container_repositories])
project.destroy_dependent_associations_in_batches(exclude: [:container_repositories, :snippets])
project.destroy!
end
end
Loading
Loading
Loading
Loading
@@ -7,8 +7,8 @@ class Repositories::BaseService < BaseService
 
attr_reader :repository
 
delegate :project, :disk_path, :full_path, to: :repository
delegate :repository_storage, to: :project
delegate :container, :disk_path, :full_path, to: :repository
delegate :repository_storage, to: :container
 
def initialize(repository)
@repository = repository
Loading
Loading
@@ -31,7 +31,7 @@ class Repositories::BaseService < BaseService
# gitlab/cookies.git -> gitlab/cookies+119+deleted.git
#
def removal_path
"#{disk_path}+#{project.id}#{DELETED_FLAG}"
"#{disk_path}+#{container.id}#{DELETED_FLAG}"
end
 
# If we get a Gitaly error, the repository may be corrupted. We can
Loading
Loading
@@ -40,7 +40,7 @@ class Repositories::BaseService < BaseService
def ignore_git_errors(&block)
yield
rescue Gitlab::Git::CommandError => e
Gitlab::GitLogger.warn(class: self.class.name, project_id: project.id, disk_path: disk_path, message: e.to_s)
Gitlab::GitLogger.warn(class: self.class.name, container_id: container.id, disk_path: disk_path, message: e.to_s)
end
 
def move_error(path)
Loading
Loading
Loading
Loading
@@ -14,11 +14,11 @@ class Repositories::DestroyService < Repositories::BaseService
log_info(%Q{Repository "#{disk_path}" moved to "#{removal_path}" for repository "#{full_path}"})
 
current_repository = repository
project.run_after_commit do
container.run_after_commit do
Repositories::ShellDestroyService.new(current_repository).execute
end
 
log_info("Project \"#{project.full_path}\" was removed")
log_info("Repository \"#{full_path}\" was removed")
 
success
else
Loading
Loading
# frozen_string_literal: true
module Snippets
class BulkDestroyService
include Gitlab::Allowable
attr_reader :current_user, :snippets
DeleteRepositoryError = Class.new(StandardError)
SnippetAccessError = Class.new(StandardError)
def initialize(user, snippets)
@current_user = user
@snippets = snippets
end
def execute
return ServiceResponse.success(message: 'No snippets found.') if snippets.empty?
user_can_delete_snippets!
attempt_delete_repositories!
snippets.destroy_all # rubocop: disable DestroyAll
ServiceResponse.success(message: 'Snippets were deleted.')
rescue SnippetAccessError
service_response_error("You don't have access to delete these snippets.", 403)
rescue DeleteRepositoryError
attempt_rollback_repositories
service_response_error('Failed to delete snippet repositories.', 400)
rescue
# In case the delete operation fails
attempt_rollback_repositories
service_response_error('Failed to remove snippets.', 400)
end
private
def user_can_delete_snippets!
allowed = DeclarativePolicy.user_scope do
snippets.find_each.all? { |snippet| user_can_delete_snippet?(snippet) }
end
raise SnippetAccessError unless allowed
end
def user_can_delete_snippet?(snippet)
can?(current_user, :admin_snippet, snippet)
end
def attempt_delete_repositories!
snippets.each do |snippet|
result = Repositories::DestroyService.new(snippet.repository).execute
raise DeleteRepositoryError if result[:status] == :error
end
end
def attempt_rollback_repositories
snippets.each do |snippet|
result = Repositories::DestroyRollbackService.new(snippet.repository).execute
log_rollback_error(snippet) if result[:status] == :error
end
end
def log_rollback_error(snippet)
Gitlab::AppLogger.error("Repository #{snippet.full_path} in path #{snippet.disk_path} could not be rolled back")
end
def service_response_error(message, http_status)
ServiceResponse.error(message: message, http_status: http_status)
end
end
end
Loading
Loading
@@ -4,12 +4,13 @@ module Snippets
class DestroyService
include Gitlab::Allowable
 
attr_reader :current_user, :project
attr_reader :current_user, :snippet
DestroyError = Class.new(StandardError)
 
def initialize(user, snippet)
@current_user = user
@snippet = snippet
@project = snippet&.project
end
 
def execute
Loading
Loading
@@ -24,16 +25,29 @@ module Snippets
)
end
 
if snippet.destroy
ServiceResponse.success(message: 'Snippet was deleted.')
else
service_response_error('Failed to remove snippet.', 400)
end
attempt_destroy!
ServiceResponse.success(message: 'Snippet was deleted.')
rescue DestroyError
service_response_error('Failed to remove snippet repository.', 400)
rescue
attempt_rollback_repository
service_response_error('Failed to remove snippet.', 400)
end
 
private
 
attr_reader :snippet
def attempt_destroy!
result = Repositories::DestroyService.new(snippet.repository).execute
raise DestroyError if result[:status] == :error
snippet.destroy!
end
def attempt_rollback_repository
Repositories::DestroyRollbackService.new(snippet.repository).execute
end
 
def user_can_delete_snippet?
can?(current_user, :admin_snippet, snippet)
Loading
Loading
Loading
Loading
@@ -56,10 +56,13 @@ module Users
 
MigrateToGhostUserService.new(user).execute unless options[:hard_delete]
 
response = Snippets::BulkDestroyService.new(current_user, user.snippets).execute
raise DestroyError, response.message if response.error?
# Rails attempts to load all related records into memory before
# destroying: https://github.com/rails/rails/issues/22510
# This ensures we delete records in batches.
user.destroy_dependent_associations_in_batches
user.destroy_dependent_associations_in_batches(exclude: [:snippets])
 
# Destroy the namespace after destroying the user since certain methods may depend on the namespace existing
user_data = user.destroy
Loading
Loading
Loading
Loading
@@ -4,7 +4,7 @@
#js-vue-sidebar-assignees{ data: { field: "#{issuable_type}", signed_in: signed_in } }
.title.hide-collapsed
= _('Assignee')
= icon('spinner spin')
.spinner.spinner-sm.align-bottom
 
.selectbox.hide-collapsed
- if assignees.none?
Loading
Loading
---
title: Add/update services to delete snippets repositories
merge_request: 22672
author:
type: added
---
title: Update moved service desk issues notifications
merge_request: 25640
author:
type: added
---
title: Migrated from .fa-spinner to .spinner in app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
merge_request: 24919
author: rk4bir
type: changed
Loading
Loading
@@ -283,10 +283,17 @@ incoming_email:
idle_timeout: 60
```
 
#### MS Exchange
#### Microsoft Exchange Server
 
Example configuration for Microsoft Exchange mail server with IMAP enabled. Assumes the
catch-all mailbox incoming@exchange.example.com.
Example configurations for Microsoft Exchange Server with IMAP enabled. Since
Exchange does not support sub-addressing, only two options exist:
- Catch-all mailbox (recommended for Exchange-only)
- Dedicated email address (supports Reply by Email only)
##### Catch-all mailbox
Assumes the catch-all mailbox `incoming@exchange.example.com`.
 
Example for Omnibus installs:
 
Loading
Loading
@@ -335,11 +342,53 @@ incoming_email:
port: 993
# Whether the IMAP server uses SSL
ssl: true
# Whether the IMAP server uses StartTLS
start_tls: false
```
 
# The mailbox where incoming mail will end up. Usually "inbox".
mailbox: "inbox"
# The IDLE command timeout.
idle_timeout: 60
##### Dedicated email address
Assumes the dedicated email address `incoming@exchange.example.com`.
Example for Omnibus installs:
```ruby
gitlab_rails['incoming_email_enabled'] = true
# Exchange does not support sub-addressing, and we're not using a catch-all mailbox so %{key} is not used here
gitlab_rails['incoming_email_address'] = "incoming@exchange.example.com"
# Email account username
# Typically this is the userPrincipalName (UPN)
gitlab_rails['incoming_email_email'] = "incoming@ad-domain.example.com"
# Email account password
gitlab_rails['incoming_email_password'] = "[REDACTED]"
# IMAP server host
gitlab_rails['incoming_email_host'] = "exchange.example.com"
# IMAP server port
gitlab_rails['incoming_email_port'] = 993
# Whether the IMAP server uses SSL
gitlab_rails['incoming_email_ssl'] = true
```
Example for source installs:
```yaml
incoming_email:
enabled: true
# Exchange does not support sub-addressing, and we're not using a catch-all mailbox so %{key} is not used here
address: "incoming@exchange.example.com"
# Email account username
# Typically this is the userPrincipalName (UPN)
user: "incoming@ad-domain.example.com"
# Email account password
password: "[REDACTED]"
# IMAP server host
host: "exchange.example.com"
# IMAP server port
port: 993
# Whether the IMAP server uses SSL
ssl: true
```
Loading
Loading
@@ -37,23 +37,9 @@ Activity history for projects and individuals' profiles was limited to one year
 
## Number of webhooks
 
A maximum number of webhooks applies to each GitLab.com tier. Limits apply to project and group webhooks.
On GitLab.com, the [maximum number of webhooks](../user/gitlab_com/index.md#maximum-number-of-webhooks) per project, and per group, is limited.
 
### Project Webhooks
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20730) in GitLab 12.6.
Check the [Maximum number of project webhooks (per tier)](../user/project/integrations/webhooks.md#maximum-number-of-project-webhooks-per-tier) section in the Webhooks page.
### Group Webhooks
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25129) in GitLab 12.9.
Check the [Maximum number of group webhooks (per tier)](../user/project/integrations/webhooks.md#maximum-number-of-group-webhooks-per-tier) section in the Webhooks page.
### Setting the limit on a self-hosted installation
To set this limit on a self-hosted installation, run the following in the
To set this limit on a self-managed installation, run the following in the
[GitLab Rails console](https://docs.gitlab.com/omnibus/maintenance/#starting-a-rails-console-session):
 
```ruby
Loading
Loading
Loading
Loading
@@ -260,7 +260,7 @@ returned with status code `404`:
Example of a valid API call and a request using cURL with sudo request,
providing a username:
 
```
```plaintext
GET /projects?private_token=<your_access_token>&sudo=username
```
 
Loading
Loading
@@ -271,7 +271,7 @@ curl --header "Private-Token: <your_access_token>" --header "Sudo: username" "ht
Example of a valid API call and a request using cURL with sudo request,
providing an ID:
 
```
```plaintext
GET /projects?private_token=<your_access_token>&sudo=23
```
 
Loading
Loading
@@ -444,7 +444,7 @@ URL-encoded.
 
For example, `/` is represented by `%2F`:
 
```
```plaintext
GET /api/v4/projects/diaspora%2Fdiaspora
```
 
Loading
Loading
@@ -460,7 +460,7 @@ URL-encoded.
 
For example, `/` is represented by `%2F`:
 
```
```plaintext
GET /api/v4/projects/1/branches/my%2Fbranch/commits
```
 
Loading
Loading
@@ -604,13 +604,13 @@ to a [W3 recommendation](http://www.w3.org/Addressing/URL/4_URI_Recommentations.
causes a `+` to be interpreted as a space. For example, in an ISO 8601 date, you may want to pass
a time in Mountain Standard Time, such as:
 
```
```plaintext
2017-10-17T23:11:13.000+05:30
```
 
The correct encoding for the query parameter would be:
 
```
```plaintext
2017-10-17T23:11:13.000%2B05:30
```
 
Loading
Loading
Loading
Loading
@@ -15,7 +15,7 @@ Epics are available only in the [Ultimate/Gold tier](https://about.gitlab.com/pr
 
Gets all child epics of an epic.
 
```
```plaintext
GET /groups/:id/epics/:epic_iid/epics
```
 
Loading
Loading
@@ -69,7 +69,7 @@ Example response:
 
Creates an association between two epics, designating one as the parent epic and the other as the child epic. A parent epic can have multiple child epics. If the new child epic already belonged to another epic, it is unassigned from that previous parent.
 
```
```plaintext
POST /groups/:id/epics/:epic_iid/epics
```
 
Loading
Loading
@@ -122,7 +122,7 @@ Example response:
 
Creates a a new epic and associates it with provided parent epic. The response is LinkedEpic object.
 
```
```plaintext
POST /groups/:id/epics/:epic_iid/epics
```
 
Loading
Loading
@@ -155,7 +155,7 @@ Example response:
 
## Re-order a child epic
 
```
```plaintext
PUT /groups/:id/epics/:epic_iid/epics/:child_epic_id
```
 
Loading
Loading
@@ -212,7 +212,7 @@ Example response:
 
Unassigns a child epic from a parent epic.
 
```
```plaintext
DELETE /groups/:id/epics/:epic_iid/epics/:child_epic_id
```
 
Loading
Loading
Loading
Loading
@@ -15,7 +15,7 @@ are [paginated](README.md#pagination).
 
Gets all feature flags of the requested project.
 
```
```plaintext
GET /projects/:id/feature_flags
```
 
Loading
Loading
@@ -145,7 +145,7 @@ Example response:
 
Creates a new feature flag.
 
```
```plaintext
POST /projects/:id/feature_flags
```
 
Loading
Loading
@@ -219,7 +219,7 @@ Example response:
 
Gets a single feature flag.
 
```
```plaintext
GET /projects/:id/feature_flags/:name
```
 
Loading
Loading
@@ -294,7 +294,7 @@ Example response:
 
Deletes a feature flag.
 
```
```plaintext
DELETE /projects/:id/feature_flags/:name
```
 
Loading
Loading
Loading
Loading
@@ -123,7 +123,7 @@ Example response:
 
Gets issues count statistics for given project.
 
```
```plaintext
GET /projects/:id/issues_statistics
GET /projects/:id/issues_statistics?labels=foo
GET /projects/:id/issues_statistics?labels=foo,bar
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment