Commit 0f006dc0 authored by blackst0ne's avatar blackst0ne

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce

parents 5af8ac8e cdf87fe8
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
}, },
"extends": "airbnb-base", "extends": "airbnb-base",
"globals": { "globals": {
"__webpack_public_path__": true,
"_": false, "_": false,
"gl": false, "gl": false,
"gon": false, "gon": false,
......
This diff is collapsed.
...@@ -396,12 +396,12 @@ gem 'net-ssh', '~> 4.1.0' ...@@ -396,12 +396,12 @@ gem 'net-ssh', '~> 4.1.0'
# Required for ED25519 SSH host key support # Required for ED25519 SSH host key support
group :ed25519 do group :ed25519 do
gem 'rbnacl-libsodium' gem 'rbnacl-libsodium'
gem 'rbnacl', '~> 3.2' gem 'rbnacl', '~> 4.0'
gem 'bcrypt_pbkdf', '~> 1.0' gem 'bcrypt_pbkdf', '~> 1.0'
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly', '~> 0.29.0' gem 'gitaly', '~> 0.30.0'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -203,7 +203,7 @@ GEM ...@@ -203,7 +203,7 @@ GEM
multi_json multi_json
fast_gettext (1.4.0) fast_gettext (1.4.0)
ffaker (2.4.0) ffaker (2.4.0)
ffi (1.9.10) ffi (1.9.18)
flay (2.8.1) flay (2.8.1)
erubis (~> 2.7.0) erubis (~> 2.7.0)
path_expander (~> 1.0) path_expander (~> 1.0)
...@@ -275,7 +275,7 @@ GEM ...@@ -275,7 +275,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly (0.29.0) gitaly (0.30.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -682,7 +682,7 @@ GEM ...@@ -682,7 +682,7 @@ GEM
rake (12.0.0) rake (12.0.0)
rblineprof (0.3.6) rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3) debugger-ruby_core_source (~> 1.3)
rbnacl (3.4.0) rbnacl (4.0.2)
ffi ffi
rbnacl-libsodium (1.0.11) rbnacl-libsodium (1.0.11)
rbnacl (>= 3.0.1) rbnacl (>= 3.0.1)
...@@ -1019,7 +1019,7 @@ DEPENDENCIES ...@@ -1019,7 +1019,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly (~> 0.29.0) gitaly (~> 0.30.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1) gitlab-markup (~> 1.5.1)
...@@ -1107,7 +1107,7 @@ DEPENDENCIES ...@@ -1107,7 +1107,7 @@ DEPENDENCIES
rainbow (~> 2.2) rainbow (~> 2.2)
raindrops (~> 0.18) raindrops (~> 0.18)
rblineprof (~> 0.3.6) rblineprof (~> 0.3.6)
rbnacl (~> 3.2) rbnacl (~> 4.0)
rbnacl-libsodium rbnacl-libsodium
rdoc (~> 4.2) rdoc (~> 4.2)
re2 (~> 1.1.1) re2 (~> 1.1.1)
......
9.5.0-pre 9.6.0-pre
/* eslint-disable no-underscore-dangle, camelcase */
/* global __webpack_public_path__ */
import monacoContext from 'monaco-editor/dev/vs/loader'; import monacoContext from 'monaco-editor/dev/vs/loader';
monacoContext.require.config({ monacoContext.require.config({
paths: { paths: {
vs: `${__webpack_public_path__}monaco-editor/vs`, vs: `${__webpack_public_path__}monaco-editor/vs`, // eslint-disable-line camelcase
}, },
}); });
// eslint-disable-next-line no-underscore-dangle
window.__monaco_context__ = monacoContext; window.__monaco_context__ = monacoContext;
export default monacoContext.require; export default monacoContext.require;
...@@ -5,5 +5,5 @@ ...@@ -5,5 +5,5 @@
*/ */
if (gon && gon.webpack_public_path) { if (gon && gon.webpack_public_path) {
__webpack_public_path__ = gon.webpack_public_path; // eslint-disable-line __webpack_public_path__ = gon.webpack_public_path; // eslint-disable-line camelcase
} }
...@@ -13,6 +13,9 @@ ...@@ -13,6 +13,9 @@
img { img {
/*max-width: 100%;*/ /*max-width: 100%;*/
margin: 0 0 8px; margin: 0 0 8px;
}
img.lazy {
min-width: 200px; min-width: 200px;
min-height: 100px; min-height: 100px;
background-color: $gray-lightest; background-color: $gray-lightest;
......
...@@ -286,6 +286,9 @@ ...@@ -286,6 +286,9 @@
.gpg-status-box { .gpg-status-box {
padding: 2px 10px;
margin-right: $gl-padding;
&:empty { &:empty {
display: none; display: none;
} }
...@@ -314,7 +317,6 @@ ...@@ -314,7 +317,6 @@
&.valid { &.valid {
svg { svg {
border: 1px solid $brand-success; border: 1px solid $brand-success;
fill: $brand-success; fill: $brand-success;
} }
} }
...@@ -322,7 +324,6 @@ ...@@ -322,7 +324,6 @@
&.invalid { &.invalid {
svg { svg {
border: 1px solid $common-gray-light; border: 1px solid $common-gray-light;
fill: $common-gray-light; fill: $common-gray-light;
} }
} }
......
class Admin::LogsController < Admin::ApplicationController class Admin::LogsController < Admin::ApplicationController
def show
@loggers = [
Gitlab::AppLogger,
Gitlab::GitLogger,
Gitlab::EnvironmentLogger,
Gitlab::SidekiqLogger,
Gitlab::RepositoryCheckLogger
]
end
end end
...@@ -26,6 +26,13 @@ class GroupsController < Groups::ApplicationController ...@@ -26,6 +26,13 @@ class GroupsController < Groups::ApplicationController
def new def new
@group = Group.new @group = Group.new
if params[:parent_id].present?
parent = Group.find_by(id: params[:parent_id])
if can?(current_user, :create_subgroup, parent)
@group.parent = parent
end
end
end end
def create def create
......
...@@ -4,7 +4,6 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -4,7 +4,6 @@ class Projects::ServicesController < Projects::ApplicationController
# Authorize # Authorize
before_action :authorize_admin_project! before_action :authorize_admin_project!
before_action :service, only: [:edit, :update, :test] before_action :service, only: [:edit, :update, :test]
before_action :update_service, only: [:update, :test]
respond_to :html respond_to :html
...@@ -14,6 +13,8 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -14,6 +13,8 @@ class Projects::ServicesController < Projects::ApplicationController
end end
def update def update
@service.attributes = service_params[:service]
if @service.save(context: :manual_change) if @service.save(context: :manual_change)
redirect_to(project_settings_integrations_path(@project), notice: success_message) redirect_to(project_settings_integrations_path(@project), notice: success_message)
else else
...@@ -24,7 +25,7 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -24,7 +25,7 @@ class Projects::ServicesController < Projects::ApplicationController
def test def test
message = {} message = {}
if @service.can_test? if @service.can_test? && @service.update_attributes(service_params[:service])
data = @service.test_data(project, current_user) data = @service.test_data(project, current_user)
outcome = @service.test(data) outcome = @service.test(data)
...@@ -50,10 +51,6 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -50,10 +51,6 @@ class Projects::ServicesController < Projects::ApplicationController
end end
end end
def update_service
@service.assign_attributes(service_params[:service])
end
def service def service
@service ||= @project.find_or_initialize_service(params[:id]) @service ||= @project.find_or_initialize_service(params[:id])
end end
......
...@@ -116,6 +116,7 @@ module ApplicationSettingsHelper ...@@ -116,6 +116,7 @@ module ApplicationSettingsHelper
:email_author_in_body, :email_author_in_body,
:enabled_git_access_protocol, :enabled_git_access_protocol,
:gravatar_enabled, :gravatar_enabled,
:hashed_storage_enabled,
:help_page_hide_commercial_content, :help_page_hide_commercial_content,
:help_page_support_url, :help_page_support_url,
:help_page_text, :help_page_text,
......
...@@ -12,11 +12,18 @@ module AvatarsHelper ...@@ -12,11 +12,18 @@ module AvatarsHelper
avatar_size = options[:size] || 16 avatar_size = options[:size] || 16
user_name = options[:user].try(:name) || options[:user_name] user_name = options[:user].try(:name) || options[:user_name]
avatar_url = options[:url] || avatar_icon(options[:user] || options[:user_email], avatar_size) avatar_url = options[:url] || avatar_icon(options[:user] || options[:user_email], avatar_size)
data_attributes = { container: 'body' } has_tooltip = options[:has_tooltip].nil? ? true : options[:has_tooltip]
data_attributes = {}
css_class = %W[avatar s#{avatar_size}].push(*options[:css_class])
if has_tooltip
css_class.push('has-tooltip')
data_attributes = { container: 'body' }
end
image_tag( image_tag(
avatar_url, avatar_url,
class: %W[avatar has-tooltip s#{avatar_size}].push(*options[:css_class]), class: css_class,
alt: "#{user_name}'s avatar", alt: "#{user_name}'s avatar",
title: user_name, title: user_name,
data: data_attributes, data: data_attributes,
......
...@@ -114,7 +114,7 @@ module CommitsHelper ...@@ -114,7 +114,7 @@ module CommitsHelper
end end
def commit_signature_badge_classes(additional_classes) def commit_signature_badge_classes(additional_classes)
%w(btn status-box gpg-status-box) + Array(additional_classes) %w(btn gpg-status-box) + Array(additional_classes)
end end
protected protected
......
...@@ -181,6 +181,7 @@ module EventsHelper ...@@ -181,6 +181,7 @@ module EventsHelper
end end
def event_commit_title(message) def event_commit_title(message)
message ||= ''
(message.split("\n").first || "").truncate(70) (message.split("\n").first || "").truncate(70)
rescue rescue
"--broken encoding" "--broken encoding"
......
...@@ -19,11 +19,21 @@ class BroadcastMessage < ActiveRecord::Base ...@@ -19,11 +19,21 @@ class BroadcastMessage < ActiveRecord::Base
after_commit :flush_redis_cache after_commit :flush_redis_cache
def self.current def self.current
Rails.cache.fetch(CACHE_KEY) do messages = Rails.cache.fetch(CACHE_KEY) { current_and_future_messages.to_a }
where('ends_at > :now AND starts_at <= :now', now: Time.zone.now)
.reorder(id: :asc) return messages if messages.empty?
.to_a
end now_or_future = messages.select(&:now_or_future?)
# If there are cached entries but none are to be displayed we'll purge the
# cache so we don't keep running this code all the time.
Rails.cache.delete(CACHE_KEY) if now_or_future.empty?
now_or_future.select(&:now?)
end
def self.current_and_future_messages
where('ends_at > :now', now: Time.zone.now).reorder(id: :asc)
end end
def active? def active?
...@@ -38,6 +48,18 @@ class BroadcastMessage < ActiveRecord::Base ...@@ -38,6 +48,18 @@ class BroadcastMessage < ActiveRecord::Base
ends_at < Time.zone.now ends_at < Time.zone.now
end end
def now?
(starts_at..ends_at).cover?(Time.zone.now)
end
def future?
starts_at > Time.zone.now
end
def now_or_future?
now? || future?
end
def flush_redis_cache def flush_redis_cache
Rails.cache.delete(CACHE_KEY) Rails.cache.delete(CACHE_KEY)
end end
......
module Storage
module LegacyProject
extend ActiveSupport::Concern
def disk_path
full_path
end
def ensure_storage_path_exist
gitlab_shell.add_namespace(repository_storage_path, namespace.full_path)
end
def rename_repo
path_was = previous_changes['path'].first
old_path_with_namespace = File.join(namespace.full_path, path_was)
new_path_with_namespace = File.join(namespace.full_path, path)
Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}"
if has_container_registry_tags?
Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!"
# we currently doesn't support renaming repository if it contains images in container registry
raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
end
expire_caches_before_rename(old_path_with_namespace)
if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
# If repository moved successfully we need to send update instructions to users.
# However we cannot allow rollback since we moved repository
# So we basically we mute exceptions in next actions
begin
gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
send_move_instructions(old_path_with_namespace)
expires_full_path_cache
@old_path_with_namespace = old_path_with_namespace
SystemHooksService.new.execute_hooks_for(self, :rename)
@repository = nil
rescue => e
Rails.logger.error "Exception renaming #{old_path_with_namespace} -> #{new_path_with_namespace}: #{e}"
# Returning false does not rollback after_* transaction but gives
# us information about failing some of tasks
false
end
else
Rails.logger.error "Repository could not be renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
raise StandardError.new('repository cannot be renamed')
end
Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path)
Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path)
end
def create_repository(force: false)
# Forked import is handled asynchronously
return if forked? && !force
if gitlab_shell.add_repository(repository_storage_path, path_with_namespace)
repository.after_create
true
else
errors.add(:base, 'Failed to create repository via gitlab-shell')
false
end
end
end
end
...@@ -406,7 +406,7 @@ class Event < ActiveRecord::Base ...@@ -406,7 +406,7 @@ class Event < ActiveRecord::Base
def body? def body?
if push? if push?
push_with_commits? || rm_ref? push_with_commits?
elsif note? elsif note?
true true
else else
......
...@@ -50,7 +50,10 @@ class Issue < ActiveRecord::Base ...@@ -50,7 +50,10 @@ class Issue < ActiveRecord::Base
scope :preload_associations, -> { preload(:labels, project: :namespace) } scope :preload_associations, -> { preload(:labels, project: :namespace) }
scope :public_only, -> { where(confidential: false) }
after_save :expire_etag_cache after_save :expire_etag_cache
after_commit :update_project_counter_caches, on: :destroy
attr_spammable :title, spam_title: true attr_spammable :title, spam_title: true
attr_spammable :description, spam_description: true attr_spammable :description, spam_description: true
...@@ -266,6 +269,10 @@ class Issue < ActiveRecord::Base ...@@ -266,6 +269,10 @@ class Issue < ActiveRecord::Base
end end
end end
def update_project_counter_caches
Projects::OpenIssuesCountService.new(project).refresh_cache
end
private private
# Returns `true` if the given User can read the current Issue. # Returns `true` if the given User can read the current Issue.
......
...@@ -31,6 +31,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -31,6 +31,7 @@ class MergeRequest < ActiveRecord::Base
after_create :ensure_merge_request_diff, unless: :importing? after_create :ensure_merge_request_diff, unless: :importing?
after_update :reload_diff_if_branch_changed after_update :reload_diff_if_branch_changed
after_commit :update_project_counter_caches, on: :destroy
# When this attribute is true some MR validation is ignored # When this attribute is true some MR validation is ignored
# It allows us to close or modify broken merge requests # It allows us to close or modify broken merge requests
...@@ -936,6 +937,10 @@ class MergeRequest < ActiveRecord::Base ...@@ -936,6 +937,10 @@ class MergeRequest < ActiveRecord::Base
true true
end end
def update_project_counter_caches
Projects::OpenMergeRequestsCountService.new(target_project).refresh_cache
end
private private
def write_ref def write_ref
......
...@@ -17,7 +17,6 @@ class Project < ActiveRecord::Base ...@@ -17,7 +17,6 @@ class Project < ActiveRecord::Base
include ProjectFeaturesCompatibility include ProjectFeaturesCompatibility
include SelectForProjectAuthorization include SelectForProjectAuthorization
include Routable include Routable
include Storage::LegacyProject
extend Gitlab::ConfigHelper extend Gitlab::ConfigHelper
...@@ -25,6 +24,7 @@ class Project < ActiveRecord::Base ...@@ -25,6 +24,7 @@ class Project < ActiveRecord::Base
NUMBER_OF_PERMITTED_BOARDS = 1 NUMBER_OF_PERMITTED_BOARDS = 1
UNKNOWN_IMPORT_URL = 'http://unknown.git'.freeze UNKNOWN_IMPORT_URL = 'http://unknown.git'.freeze
LATEST_STORAGE_VERSION = 1
cache_markdown_field :description, pipeline: :description cache_markdown_field :description, pipeline: :description
...@@ -32,6 +32,8 @@ class Project < ActiveRecord::Base ...@@ -32,6 +32,8 @@ class Project < ActiveRecord::Base
:merge_requests_enabled?, :issues_enabled?, to: :project_feature, :merge_requests_enabled?, :issues_enabled?, to: :project_feature,
allow_nil: true allow_nil: true
delegate :base_dir, :disk_path, :ensure_storage_path_exists, to: :storage
default_value_for :archived, false default_value_for :archived, false
default_value_for :visibility_level, gitlab_config_features.visibility_level default_value_for :visibility_level, gitlab_config_features.visibility_level
default_value_for :container_registry_enabled, gitlab_config_features.container_registry default_value_for :container_registry_enabled, gitlab_config_features.container_registry
...@@ -44,32 +46,24 @@ class Project < ActiveRecord::Base ...@@ -44,32 +46,24 @@ class Project < ActiveRecord::Base
default_value_for :snippets_enabled, gitlab_config_features.snippets default_value_for :snippets_enabled, gitlab_config_features.snippets
default_value_for :only_allow_merge_if_all_discussions_are_resolved, false default_value_for :only_allow_merge_if_all_discussions_are_resolved, false
after_create :ensure_storage_path_exist add_authentication_token_field :runners_token
after_create :create_project_feature, unless: :project_feature before_save :ensure_runners_token
after_save :update_project_statistics, if: :namespace_id_changed?
# set last_activity_at to the same as created_at after_save :update_project_statistics, if: :namespace_id_changed?
after_create :create_project_feature, unless: :project_feature
after_create :set_last_activity_at after_create :set_last_activity_at
def set_last_activity_at
update_column(:last_activity_at, self.created_at)
end
after_create :set_last_repository_updated_at after_create :set_last_repository_updated_at
def set_last_repository_updated_at after_update :update_forks_visibility_level
update_column(:last_repository_updated_at, self.created_at)
end
before_destroy :remove_private_deploy_keys before_destroy :remove_private_deploy_keys
after_destroy -> { run_after_commit { remove_pages } } after_destroy -> { run_after_commit { remove_pages } }
# update visibility_level of forks
after_update :update_forks_visibility_level
after_validation :check_pending_delete after_validation :check_pending_delete
# Legacy Storage specific hooks # Storage specific hooks
after_initialize :use_hashed_storage
after_save :ensure_storage_path_exist, if: :namespace_id_changed? after_create :ensure_storage_path_exists
after_save :ensure_storage_path_exists, if: :namespace_id_changed?
acts_as_taggable acts_as_taggable
...@@ -238,9 +232,6 @@ class Project < ActiveRecord::Base ...@@ -238,9 +232,6 @@ class Project < ActiveRecord::Base
presence: true, presence: true,
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } } inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
add_authentication_token_field :runners_token
before_save :ensure_runners_token
mount_uploader :avatar, AvatarUploader mount_uploader :avatar, AvatarUploader
has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
...@@ -487,6 +478,10 @@ class Project < ActiveRecord::Base ...@@ -487,6 +478,10 @@ class Project < ActiveRecord::Base
@repository ||= Repository.new(full_path, self, disk_path: disk_path) @repository ||= Repository.new(full_path, self, disk_path: disk_path)
end end
def reload_repository!
@repository = nil
end
def container_registry_url def container_registry_url
if Gitlab.config.registry.enabled if Gitlab.config.registry.enabled
"#{Gitlab.config.registry.host_port}/#{full_path.downcase}" "#{Gitlab.config.registry.host_port}/#{full_path.downcase}"
...@@ -1004,6 +999,19 @@ class Project < ActiveRecord::Base ...@@ -1004,6 +999,19 @@ class Project < ActiveRecord::Base
end end
end end
def create_repository(force: false)
# Forked import is handled asynchronously
return if forked? && !force
if gitlab_shell.add_repository(repository_storage_path, disk_path)
repository.after_create
true
else
errors.add(:base, 'Failed to create repository via gitlab-shell')
false
end
end
def hook_attrs(backward: true) def hook_attrs(backward: true)
attrs = { attrs = {
name: name, name: name,
...@@ -1086,6 +1094,7 @@ class Project < ActiveRecord::Base ...@@ -1086,6 +1094,7 @@ class Project < ActiveRecord::Base
!!repository.exists? !!repository.exists?
end end
# update visibility_level of forks
def update_forks_visibility_level def update_forks_visibility_level
return unless visibility_level < visibility_level_was return unless visibility_level < visibility_level_was
...@@ -1158,7 +1167,11 @@ class Project < ActiveRecord::Base ...@@ -1158,7 +1167,11 @@ class Project < ActiveRecord::Base
end end
def open_issues_count def open_issues_count
issues.opened.count Projects::OpenIssuesCountService.new(self).count
end
def open_merge_requests_count
Projects::OpenMergeRequestsCountService.new(self).count
end end
def visibility_level_allowed_as_fork?(level = self.visibility_level) def visibility_level_allowed_as_fork?(level = self.visibility_level)
...@@ -1213,7 +1226,8 @@ class Project < ActiveRecord::Base ...@@ -1213,7 +1226,8 @@ class Project < ActiveRecord::Base
end end
def pages_path def pages_path
File.join(Settings.pages.path, disk_path) # TODO: when we migrate Pages to work with new storage types, change here to use disk_path
File.join(Settings.pages.path, full_path)
end end
def public_pages_path def public_pages_path
...@@ -1252,6 +1266,50 @@ class Project < ActiveRecord::Base ...@@ -1252,6 +1266,50 @@ class Project < ActiveRecord::Base
end end
end end
def rename_repo
new_full_path = build_full_path
Rails.logger.error "Attempting to rename #{full_path_was} -> #{new_full_path}"
if has_container_registry_tags?
Rails.logger.error "Project #{full_path_was} cannot be renamed because container registry tags are present!"
# we currently doesn't support renaming repository if it contains images in container registry
raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
end
expire_caches_before_rename(full_path_was)
if storage.rename_repo
Gitlab::AppLogger.info "Project was renamed: #{full_path_was} -> #{new_full_path}"
rename_repo_notify!
after_rename_repo
else
Rails.logger.error "Repository could not be renamed: #{full_path_was} -> #{new_full_path}"
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
raise StandardError.new('repository cannot be renamed')
end
end
def rename_repo_notify!
send_move_instructions(full_path_was)
expires_full_path_cache
self.old_path_with_namespace = full_path_was
SystemHooksService.new.execute_hooks_for(self, :rename)
reload_repository!
end
def after_rename_repo
path_before_change = previous_changes['path'].first
Gitlab::UploadsTransfer.new.rename_project(path_before_change, self.path, namespace.full_path)
Gitlab::PagesTransfer.new.rename_project(path_before_change, self.path, namespace.full_path)
end
def running_or_pending_build_count(force: false) def running_or_pending_build_count(force: false)
Rails.cache.fetch(['projects', id, 'running_or_pending_build_count'], force: force) do Rails.cache.fetch(['projects', id, 'running_or_pending_build_count'], force: force) do
builds.running_or_pending.count(:all) builds.running_or_pending.count(:all)
...@@ -1410,6 +1468,10 @@ class Project < ActiveRecord::Base ...@@ -1410,6 +1468,10 @@ class Project < ActiveRecord::Base
end end
end end
def full_path_was
File.join(namespace.full_path, previous_changes['path'].first)
end
alias_method :name_with_namespace, :full_name alias_method :name_with_namespace, :full_name
alias_method :human_name, :full_name alias_method :human_name, :full_name
# @deprecated cannot remove yet because it has an index with its name in elasticsearch # @deprecated cannot remove yet because it has an index with its name in elasticsearch
...@@ -1419,8 +1481,36 @@ class Project < ActiveRecord::Base ...@@ -1419,8 +1481,36 @@ class Project < ActiveRecord::Base
Projects::ForksCountService.new(self).count Projects::ForksCountService.new(self).count
end end
def legacy_storage?
self.storage_version.nil?
end
private private
def storage
@storage ||=
if self.storage_version && self.storage_version >= 1
Storage::HashedProject.new(self)
else
Storage::LegacyProject.new(self)
end
end
def use_hashed_storage
if self.new_record? && current_application_settings.hashed_storage_enabled
self.storage_version = LATEST_STORAGE_VERSION
end
end
# set last_activity_at to the same as created_at
def set_last_activity_at
update_column(:last_activity_at, self.created_at)
end
def set_last_repository_updated_at
update_column(:last_repository_updated_at, self.created_at)
end
def cross_namespace_reference?(from) def cross_namespace_reference?(from)
case from case from
when Project when Project
......
...@@ -24,6 +24,8 @@ class KubernetesService < DeploymentService ...@@ -24,6 +24,8 @@ class KubernetesService < DeploymentService
validates :token validates :token
end end
before_validation :enforce_namespace_to_lower_case
validates :namespace, validates :namespace,
allow_blank: true, allow_blank: true,
length: 1..63, length: 1..63,
...@@ -207,4 +209,8 @@ class KubernetesService < DeploymentService ...@@ -207,4 +209,8 @@ class KubernetesService < DeploymentService
max_session_time: current_application_settings.terminal_max_session_time max_session_time: current_application_settings.terminal_max_session_time
} }
end end
def enforce_namespace_to_lower_case
self.namespace = self.namespace&.downcase
end
end end
module Storage
class HashedProject
attr_accessor :project
delegate :gitlab_shell, :repository_storage_path, to: :project
ROOT_PATH_PREFIX = '@hashed'.freeze
def initialize(project)
@project = project
end
# Base directory
#
# @return [String] directory where repository is stored
def base_dir
"#{ROOT_PATH_PREFIX}/#{disk_hash[0..1]}/#{disk_hash[2..3]}" if disk_hash
end
# Disk path is used to build repository and project's wiki path on disk
#
# @return [String] combination of base_dir and the repository own name without `.git` or `.wiki.git` extensions
def disk_path
"#{base_dir}/#{disk_hash}" if disk_hash
end
def ensure_storage_path_exists
gitlab_shell.add_namespace(repository_storage_path, base_dir)
end
def rename_repo
true
end
private
# Generates the hash for the project path and name on disk
# If you need to refer to the repository on disk, use the `#disk_path`
def disk_hash
@disk_hash ||= Digest::SHA2.hexdigest(project.id.to_s) if project.id
end
end
end
module Storage
class LegacyProject
attr_accessor :project
delegate :namespace, :gitlab_shell, :repository_storage_path, to: :project
def initialize(project)
@project = project
end
# Base directory
#
# @return [String] directory where repository is stored
def base_dir
namespace.full_path
end
# Disk path is used to build repository and project's wiki path on disk
#
# @return [String] combination of base_dir and the repository own name without `.git` or `.wiki.git` extensions
def disk_path
project.full_path
end
def ensure_storage_path_exists
return unless namespace
gitlab_shell.add_namespace(repository_storage_path, base_dir)
end
def rename_repo
new_full_path = project.build_full_path
if gitlab_shell.mv_repository(repository_storage_path, project.full_path_was, new_full_path)
# If repository moved successfully we need to send update instructions to users.
# However we cannot allow rollback since we moved repository
# So we basically we mute exceptions in next actions
begin
gitlab_shell.mv_repository(repository_storage_path, "#{project.full_path_was}.wiki", "#{new_full_path}.wiki")
return true
rescue => e
Rails.logger.error "Exception renaming #{project.full_path_was} -> #{new_full_path}: #{e}"
# Returning false does not rollback after_* transaction but gives
# us information about failing some of tasks
return false
end
end
false
end
end
end
...@@ -13,6 +13,8 @@ class GroupPolicy < BasePolicy ...@@ -13,6 +13,8 @@ class GroupPolicy < BasePolicy
condition(:master) { access_level >= GroupMember::MASTER } condition(:master) { access_level >= GroupMember::MASTER }
condition(:reporter) { access_level >= GroupMember::REPORTER } condition(:reporter) { access_level >= GroupMember::REPORTER }
condition(:nested_groups_supported, scope: :global) { Group.supports_nested_groups? }
condition(:has_projects) do condition(:has_projects) do
GroupProjectsFinder.new(group: @subject, current_user: @user).execute.any? GroupProjectsFinder.new(group: @subject, current_user: @user).execute.any?
end end
...@@ -42,7 +44,7 @@ class GroupPolicy < BasePolicy ...@@ -42,7 +44,7 @@ class GroupPolicy < BasePolicy
enable :change_visibility_level enable :change_visibility_level
end end
rule { owner & can_create_group }.enable :create_subgroup rule { owner & can_create_group & nested_groups_supported }.enable :create_subgroup
rule { public_group | logged_in_viewable }.enable :view_globally rule { public_group | logged_in_viewable }.enable :view_globally
......
...@@ -13,9 +13,9 @@ module Groups ...@@ -13,9 +13,9 @@ module Groups
return @group return @group
end end
if @group.parent && !can?(current_user, :admin_group, @group.parent) if @group.parent && !can?(current_user, :create_subgroup, @group.parent)
@group.parent = nil @group.parent = nil
@group.errors.add(:parent_id, 'manage access required to create subgroup') @group.errors.add(:parent_id, 'You don’t have permission to create a subgroup in this group.')
return @group return @group
end end
......
...@@ -13,7 +13,7 @@ module Groups ...@@ -13,7 +13,7 @@ module Groups
# Execute the destruction of the models immediately to ensure atomic cleanup. # Execute the destruction of the models immediately to ensure atomic cleanup.
# Skip repository removal because we remove directory with namespace # Skip repository removal because we remove directory with namespace
# that contain all these repositories # that contain all these repositories
::Projects::DestroyService.new(project, current_user, skip_repo: true).execute ::Projects::DestroyService.new(project, current_user, skip_repo: project.legacy_storage?).execute
end end
group.children.each do |group| group.children.each do |group|
......
module Groups
class NestedCreateService < Groups::BaseService
attr_reader :group_path
def initialize(user, params)
@current_user, @params = user, params.dup
@group_path = @params.delete(:group_path)
end
def execute
return nil unless group_path
if group = Group.find_by_full_path(group_path)
return group
end
create_group_path
end
private
def create_group_path
group_path_segments = group_path.split('/')
last_group = nil
partial_path_segments = []
while subgroup_name = group_path_segments.shift
partial_path_segments << subgroup_name
partial_path = partial_path_segments.join('/')
new_params = params.reverse_merge(
path: subgroup_name,
name: subgroup_name,
parent: last_group
)
new_params[:visibility_level] ||= Gitlab::CurrentSettings.current_application_settings.default_group_visibility
last_group = Group.find_by_full_path(partial_path) || Groups::CreateService.new(current_user, new_params).execute
end
last_group
end
end
end
...@@ -192,6 +192,8 @@ class IssuableBaseService < BaseService ...@@ -192,6 +192,8 @@ class IssuableBaseService < BaseService
def after_create(issuable) def after_create(issuable)
# To be overridden by subclasses # To be overridden by subclasses
issuable.update_project_counter_caches
end end
def before_update(issuable) def before_update(issuable)
...@@ -200,6 +202,8 @@ class IssuableBaseService < BaseService ...@@ -200,6 +202,8 @@ class IssuableBaseService < BaseService
def after_update(issuable) def after_update(issuable)
# To be overridden by subclasses # To be overridden by subclasses
issuable.update_project_counter_caches
end end
def update(issuable) def update(issuable)
......
...@@ -27,6 +27,8 @@ module Issues ...@@ -27,6 +27,8 @@ module Issues
todo_service.new_issue(issuable, current_user) todo_service.new_issue(issuable, current_user)
user_agent_detail_service.create user_agent_detail_service.create
resolve_discussions_with_issue(issuable) resolve_discussions_with_issue(issuable)
super
end end
def resolve_discussions_with_issue(issue) def resolve_discussions_with_issue(issue)
......
...@@ -28,6 +28,8 @@ module MergeRequests ...@@ -28,6 +28,8 @@ module MergeRequests
todo_service.new_merge_request(issuable, current_user) todo_service.new_merge_request(issuable, current_user)
issuable.cache_merge_request_closes_issues!(current_user) issuable.cache_merge_request_closes_issues!(current_user)
update_merge_requests_head_pipeline(issuable) update_merge_requests_head_pipeline(issuable)
super
end end
private private
......
module Projects
# Base class for the various service classes that count project data (e.g.
# issues or forks).
class CountService
def initialize(project)
@project = project
end
def relation_for_count
raise(
NotImplementedError,
'"relation_for_count" must be implemented and return an ActiveRecord::Relation'
)
end
def count
Rails.cache.fetch(cache_key) { uncached_count }
end
def refresh_cache
Rails.cache.write(cache_key, uncached_count)
end
def uncached_count
relation_for_count.count
end
def delete_cache
Rails.cache.delete(cache_key)
end
def cache_key_name
raise(
NotImplementedError,
'"cache_key_name" must be implemented and return a String'
)
end
def cache_key
['projects', @project.id, cache_key_name]
end
end
end
module Projects module Projects
# Service class for getting and caching the number of forks of a project. # Service class for getting and caching the number of forks of a project.
class ForksCountService class ForksCountService < CountService
def initialize(project) def relation_for_count
@project = project @project.forks
end end
def count def cache_key_name
Rails.cache.fetch(cache_key) { uncached_count } 'forks_count'
end
def refresh_cache
Rails.cache.write(cache_key, uncached_count)
end
def delete_cache
Rails.cache.delete(cache_key)
end
private
def uncached_count
@project.forks.count
end
def cache_key
['projects', @project.id, 'forks_count']
end end
end end
end end
...@@ -9,7 +9,8 @@ module Projects ...@@ -9,7 +9,8 @@ module Projects
class HousekeepingService < BaseService class HousekeepingService < BaseService
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
LEASE_TIMEOUT = 3600 # Timeout set to 24h
LEASE_TIMEOUT = 86400
class LeaseTaken < StandardError class LeaseTaken < StandardError
def to_s def to_s
......
module Projects
# Service class for counting and caching the number of open issues of a
# project.
class OpenIssuesCountService < CountService
def relation_for_count
# We don't include confidential issues in this number since this would
# expose the number of confidential issues to non project members.
@project.issues.opened.public_only
end
def cache_key_name
'open_issues_count'
end
end
end
module Projects
# Service class for counting and caching the number of open merge requests of
# a project.
class OpenMergeRequestsCountService < CountService
def relation_for_count
@project.merge_requests.opened
end
def cache_key_name
'open_merge_requests_count'
end
end
end
...@@ -35,16 +35,18 @@ module Users ...@@ -35,16 +35,18 @@ module Users
Groups::DestroyService.new(group, current_user).execute Groups::DestroyService.new(group, current_user).execute
end end
namespace = user.namespace
namespace.prepare_for_destroy
user.personal_projects.each do |project| user.personal_projects.each do |project|
# Skip repository removal because we remove directory with namespace # Skip repository removal because we remove directory with namespace
# that contain all this repositories # that contain all this repositories
::Projects::DestroyService.new(project, current_user, skip_repo: true).execute ::Projects::DestroyService.new(project, current_user, skip_repo: project.legacy_storage?).execute
end end
MigrateToGhostUserService.new(user).execute unless options[:hard_delete] MigrateToGhostUserService.new(user).execute unless options[:hard_delete]
# Destroy the namespace after destroying the user since certain methods may depend on the namespace existing # Destroy the namespace after destroying the user since certain methods may depend on the namespace existing
namespace = user.namespace
user_data = user.destroy user_data = user.destroy
namespace.really_destroy! namespace.really_destroy!
......
...@@ -492,6 +492,16 @@ ...@@ -492,6 +492,16 @@
%fieldset %fieldset
%legend Repository Storage %legend Repository Storage
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :hashed_storage_enabled do
= f.check_box :hashed_storage_enabled
Create new projects using hashed storage paths
.help-block
Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents
repositories from having to be moved or renamed when the Project URL changes and may improve disk I/O performance.
%em (EXPERIMENTAL)
.form-group .form-group
= f.label :repository_storages, 'Storage paths for new projects', class: 'control-label col-sm-2' = f.label :repository_storages, 'Storage paths for new projects', class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
...@@ -501,6 +511,7 @@ ...@@ -501,6 +511,7 @@
= succeed "." do = succeed "." do
= link_to "repository storages documentation", help_page_path("administration/repository_storages") = link_to "repository storages documentation", help_page_path("administration/repository_storages")
%fieldset %fieldset
%legend Repository Checks %legend Repository Checks
.form-group .form-group
......
- @no_container = true - @no_container = true
- page_title "Logs" - page_title "Logs"
- loggers = [Gitlab::GitLogger, Gitlab::AppLogger,
Gitlab::EnvironmentLogger, Gitlab::SidekiqLogger,
Gitlab::RepositoryCheckLogger]
= render 'admin/monitoring/head' = render 'admin/monitoring/head'
%div{ class: container_class } %div{ class: container_class }
%ul.nav-links.log-tabs %ul.nav-links.log-tabs
- loggers.each do |klass| - @loggers.each do |klass|
%li{ class: active_when(klass == Gitlab::GitLogger) }> %li{ class: active_when(klass == @loggers.first) }>
= link_to klass::file_name, "##{klass::file_name_noext}", = link_to klass.file_name, "##{klass.file_name_noext}", data: { toggle: 'tab' }
'data-toggle' => 'tab'
.row-content-block .row-content-block
To prevent performance issues admin logs output the last 2000 lines To prevent performance issues admin logs output the last 2000 lines
.tab-content .tab-content
- loggers.each do |klass| - @loggers.each do |klass|
.tab-pane{ class: active_when(klass == Gitlab::GitLogger), id: klass::file_name_noext } .tab-pane{ class: active_when(klass == @loggers.first), id: klass.file_name_noext }
.file-holder#README .file-holder#README
.js-file-title.file-title .js-file-title.file-title
%i.fa.fa-file %i.fa.fa-file
= klass::file_name = klass.file_name
.pull-right .pull-right
= link_to '#', class: 'log-bottom' do = link_to '#', class: 'log-bottom' do
%i.fa.fa-arrow-down %i.fa.fa-arrow-down
......
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
%i %i
at at
= event.created_at.to_s(:short) = event.created_at.to_s(:short)
%blockquote= markdown(escape_once(event.commit_title), pipeline: :atom, project: event.project, author: event.author) - unless event.rm_ref?
- if event.commits_count > 1 %blockquote= markdown(escape_once(event.commit_title), pipeline: :atom, project: event.project, author: event.author)
%p - if event.commits_count > 1
%i %p
\... and %i
= pluralize(event.commits_count - 1, "more commit") \... and
= pluralize(event.commits_count - 1, "more commit")
...@@ -41,7 +41,3 @@ ...@@ -41,7 +41,3 @@
%li.commits-stat %li.commits-stat
= link_to create_mr_path(project.default_branch, event.ref_name, project) do = link_to create_mr_path(project.default_branch, event.ref_name, project) do
Create Merge Request Create Merge Request
- elsif event.rm_ref?
.event-body
%ul.well-list.event_commits
= render "events/commit", project: project, event: event
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
= hidden_field_tag :namespace_id, value: current_user.namespace_id = hidden_field_tag :namespace_id, value: current_user.namespace_id
.form-group.col-xs-12.col-sm-6.project-path .form-group.col-xs-12.col-sm-6.project-path
= label_tag :path, 'Project name', class: 'label-light' = label_tag :path, 'Project name', class: 'label-light'
= text_field_tag :path, nil, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, autofocus: true, required: true = text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, autofocus: true, required: true
.row .row
.form-group.col-md-12 .form-group.col-md-12
...@@ -33,7 +33,6 @@ ...@@ -33,7 +33,6 @@
.row .row
.form-group.col-sm-12 .form-group.col-sm-12
= hidden_field_tag :namespace_id, @namespace.id = hidden_field_tag :namespace_id, @namespace.id
= hidden_field_tag :path, @path
= label_tag :file, 'GitLab project export', class: 'label-light' = label_tag :file, 'GitLab project export', class: 'label-light'
.form-group .form-group
= file_field_tag :file, class: '' = file_field_tag :file, class: ''
......
...@@ -86,7 +86,8 @@ ...@@ -86,7 +86,8 @@
%span.nav-item-name %span.nav-item-name
Issues Issues
- if @project.issues_enabled? - if @project.issues_enabled?
%span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count) %span.badge.count.issue_counter
= number_with_delimiter(@project.open_issues_count)
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(controller: :issues) do = nav_link(controller: :issues) do
...@@ -116,7 +117,8 @@ ...@@ -116,7 +117,8 @@
= custom_icon('mr_bold') = custom_icon('mr_bold')
%span.nav-item-name %span.nav-item-name
Merge Requests Merge Requests
%span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count) %span.badge.count.merge_counter.js-merge-counter
= number_with_delimiter(@project.open_merge_requests_count)
- if project_nav_tab? :pipelines - if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
......
...@@ -28,7 +28,8 @@ ...@@ -28,7 +28,8 @@
%span %span
Issues Issues
- if @project.issues_enabled? - if @project.issues_enabled?
%span.badge.count.issue_counter= number_with_delimiter(issuables_count_for_state(:issues, :opened, finder: IssuesFinder.new(current_user, project_id: @project.id))) %span.badge.count.issue_counter
= number_with_delimiter(@project.open_issues_count)
- if project_nav_tab? :merge_requests - if project_nav_tab? :merge_requests
- controllers = [:merge_requests, 'projects/merge_requests/conflicts'] - controllers = [:merge_requests, 'projects/merge_requests/conflicts']
...@@ -37,7 +38,8 @@ ...@@ -37,7 +38,8 @@
= link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do = link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%span %span
Merge Requests Merge Requests
%span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(issuables_count_for_state(:merge_requests, :opened, finder: MergeRequestsFinder.new(current_user, project_id: @project.id))) %span.badge.count.merge_counter.js-merge-counter
= number_with_delimiter(@project.open_merge_requests_count)
- if project_nav_tab? :pipelines - if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :environments, :artifacts]) do = nav_link(controller: [:pipelines, :builds, :environments, :artifacts]) do
......
.project-templates-buttons.import-buttons{ data: { toggle: "buttons" } } .project-templates-buttons.import-buttons{ data: { toggle: "buttons" } }
.btn.blank-option.active .btn.blank-option.active
%input{ type: "radio", autocomplete: "off", name: "project_templates", id: "blank", checked: "true" } %input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: "blank", checked: "true", value: "" }
= icon('file-o', class: 'btn-template-icon') = icon('file-o', class: 'btn-template-icon')
Blank Blank
- Gitlab::ProjectTemplate.all.each do |template| - Gitlab::ProjectTemplate.all.each do |template|
......
...@@ -37,14 +37,14 @@ ...@@ -37,14 +37,14 @@
.commit-actions.hidden-xs .commit-actions.hidden-xs
- if commit.status(ref)
= render_commit_status(commit, ref: ref)
- if request.xhr? - if request.xhr?
= render partial: 'projects/commit/signature', object: commit.signature = render partial: 'projects/commit/signature', object: commit.signature
- else - else
= render partial: 'projects/commit/ajax_signature', locals: { commit: commit } = render partial: 'projects/commit/ajax_signature', locals: { commit: commit }
- if commit.status(ref)
= render_commit_status(commit, ref: ref)
= link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent" = link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent"
= clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard")) = clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard"))
= link_to_browse_code(project, commit) = link_to_browse_code(project, commit)
...@@ -202,8 +202,6 @@ ...@@ -202,8 +202,6 @@
.sub-section.rename-respository .sub-section.rename-respository
%h4.warning-title %h4.warning-title
Rename repository Rename repository
%p
Export this project with all its related data in order to move your project to a new GitLab instance. Once the export is finished, you can import the file from the "New Project" page.
= render 'projects/errors' = render 'projects/errors'
= form_for([@project.namespace.becomes(Namespace), @project]) do |f| = form_for([@project.namespace.becomes(Namespace), @project]) do |f|
.form-group.project_name_holder .form-group.project_name_holder
......
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('group') = page_specific_javascript_bundle_tag('group')
- parent = GroupFinder.new(current_user).execute(id: params[:parent_id] || @group.parent_id) - parent = @group.parent
- group_path = root_url - group_path = root_url
- group_path << parent.full_path + '/' if parent - group_path << parent.full_path + '/' if parent
...@@ -13,13 +13,12 @@ ...@@ -13,13 +13,12 @@
%span>= root_url %span>= root_url
- if parent - if parent
%strong= parent.full_path + '/' %strong= parent.full_path + '/'
= f.hidden_field :parent_id
= f.text_field :path, placeholder: 'open-source', class: 'form-control', = f.text_field :path, placeholder: 'open-source', class: 'form-control',
autofocus: local_assigns[:autofocus] || false, required: true, autofocus: local_assigns[:autofocus] || false, required: true,
pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS,
title: 'Please choose a group path with no special characters.', title: 'Please choose a group path with no special characters.',
"data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}" "data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
- if parent
= f.hidden_field :parent_id, value: parent.id
- if @group.persisted? - if @group.persisted?
.alert.alert-warning.prepend-top-10 .alert.alert-warning.prepend-top-10
......
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14"><g fill-rule="evenodd"><path fill-rule="nonzero" d="m0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7m1 0c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6"/><path d="m7 6h-2.702c-.154 0-.298.132-.298.295v1.41c0 .164.133.295.298.295h2.702v1.694c0 .18.095.209.213.09l2.539-2.568c.115-.116.118-.312 0-.432l-2.539-2.568c-.115-.116-.213-.079-.213.09v1.694"/></g></svg> <svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m9 6h-7a2 2 0 1 0 0 4h7v2.586a1 1 0 0 0 1.707.707l4.586-4.586a1 1 0 0 0 0-1.414l-4.586-4.586a1 1 0 0 0 -1.707.707z" fill-rule="evenodd"/></svg>
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1472 930v318q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q63 0 117 25 15 7 18 23 3 17-9 29l-49 49q-10 10-23 10-3 0-9-2-23-6-45-6h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-254q0-13 9-22l64-64q10-10 23-10 6 0 12 3 20 8 20 29zm231-489l-814 814q-24 24-57 24t-57-24l-430-430q-24-24-24-57t24-57l110-110q24-24 57-24t57 24l263 263 647-647q24-24 57-24t57 24l110 110q24 24 24 57t-24 57z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></svg>
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1024 544v448q0 14-9 23t-23 9h-320q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h224v-352q0-14 9-23t23-9h64q14 0 23 9t9 23zm416 352q0-148-73-273t-198-198-273-73-273 73-198 198-73 273 73 273 198 198 273 73 273-73 198-198 73-273zm224 0q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/></svg> <svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9 7h1c.552 0 1 .448 1 1s-.448 1-1 1H8c-.276 0-.526-.112-.707-.293S7 8.277 7 8V5c0-.552.448-1 1-1s1 .448 1 1zm-1 9c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8zm0-2c3.314 0 6-2.686 6-6s-2.686-6-6-6-6 2.686-6 6 2.686 6 6 6z"/></svg>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14" viewBox="0 0 14 14"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M10.874 2H12a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-2c-.918 0-1.74-.413-2.29-1.063a3.987 3.987 0 0 0 1.988-.984A1 1 0 0 0 10 14h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-1V3c0-.345-.044-.68-.126-1zM4 0h3a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4z"/></svg>
<path d="M13 12.75v-8.5q0-0.102-0.074-0.176t-0.176-0.074h-8.5q-0.102 0-0.176 0.074t-0.074 0.176v8.5q0 0.102 0.074 0.176t0.176 0.074h8.5q0.102 0 0.176-0.074t0.074-0.176zM14 4.25v8.5q0 0.516-0.367 0.883t-0.883 0.367h-8.5q-0.516 0-0.883-0.367t-0.367-0.883v-8.5q0-0.516 0.367-0.883t0.883-0.367h8.5q0.516 0 0.883 0.367t0.367 0.883zM11 1.25v1.25h-1v-1.25q0-0.102-0.074-0.176t-0.176-0.074h-8.5q-0.102 0-0.176 0.074t-0.074 0.176v8.5q0 0.102 0.074 0.176t0.176 0.074h1.25v1h-1.25q-0.516 0-0.883-0.367t-0.367-0.883v-8.5q0-0.516 0.367-0.883t0.883-0.367h8.5q0.516 0 0.883 0.367t0.367 0.883z"></path>
</svg>
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M672 1472q0-40-28-68t-68-28-68 28-28 68 28 68 68 28 68-28 28-68zm0-1152q0-40-28-68t-68-28-68 28-28 68 28 68 68 28 68-28 28-68zm640 128q0-40-28-68t-68-28-68 28-28 68 28 68 68 28 68-28 28-68zm96 0q0 52-26 96.5t-70 69.5q-2 287-226 414-68 38-203 81-128 40-169.5 71t-41.5 100v26q44 25 70 69.5t26 96.5q0 80-56 136t-136 56-136-56-56-136q0-52 26-96.5t70-69.5v-820q-44-25-70-69.5t-26-96.5q0-80 56-136t136-56 136 56 56 136q0 52-26 96.5t-70 69.5v497q54-26 154-57 55-17 87.5-29.5t70.5-31 59-39.5 40.5-51 28-69.5 8.5-91.5q-44-25-70-69.5t-26-96.5q0-80 56-136t136-56 136 56 56 136z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M6 11.978v.29a2 2 0 1 1-2 0V3.732a2 2 0 1 1 2 0v3.849c.592-.491 1.31-.854 2.15-1.081 1.308-.353 1.875-.882 1.893-1.743a2 2 0 1 1 2.002-.051C12.053 6.54 10.857 7.84 8.67 8.43 7.056 8.867 6.195 9.98 6 11.978zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm6 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 15a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></svg>
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M896 384q-204 0-381.5 69.5t-282 187.5-104.5 255q0 112 71.5 213.5t201.5 175.5l87 50-27 96q-24 91-70 172 152-63 275-171l43-38 57 6q69 8 130 8 204 0 381.5-69.5t282-187.5 104.5-255-104.5-255-282-187.5-381.5-69.5zm896 512q0 174-120 321.5t-326 233-450 85.5q-70 0-145-8-198 175-460 242-49 14-114 22h-5q-15 0-27-10.5t-16-27.5v-1q-3-4-.5-12t2-10 4.5-9.5l6-9 7-8.5 8-9q7-8 31-34.5t34.5-38 31-39.5 32.5-51 27-59 26-76q-157-89-247.5-220t-90.5-281q0-174 120-321.5t326-233 450-85.5 450 85.5 326 233 120 321.5z"/></svg> <svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1.707 15.707c-.63.63-1.707.184-1.707-.707v-12a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1 -3 3h-7.586zm.293-3.121 2.293-2.293a1 1 0 0 1 .707-.293h8a1 1 0 0 0 1-1v-6a1 1 0 0 0 -1-1h-10a1 1 0 0 0 -1 1z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 18" enable-background="new 0 0 36 18"><path d="m34 7h-7.2c-.9-4-4.5-7-8.8-7s-7.9 3-8.8 7h-7.2c-1.1 0-2 .9-2 2 0 1.1.9 2 2 2h7.2c.9 4 4.5 7 8.8 7s7.9-3 8.8-7h7.2c1.1 0 2-.9 2-2 0-1.1-.9-2-2-2m-16 7c-2.8 0-5-2.2-5-5s2.2-5 5-5 5 2.2 5 5-2.2 5-5 5"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3.876-1.008a4.002 4.002 0 0 1-7.752 0A1.01 1.01 0 0 1 4 9H1a1 1 0 1 1 0-2h3c.042 0 .083.003.124.008a4.002 4.002 0 0 1 7.752 0A1.01 1.01 0 0 1 12 7h3a1 1 0 0 1 0 2h-3a1.01 1.01 0 0 1-.124-.008z"/></svg>
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M888 1184l116-116-152-152-116 116v56h96v96h56zm440-720q-16-16-33 1l-350 350q-17 17-1 33t33-1l350-350q17-17 1-33zm80 594v190q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q63 0 117 25 15 7 18 23 3 17-9 29l-49 49q-14 14-32 8-23-6-45-6h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-126q0-13 9-22l64-64q15-15 35-7t20 29zm-96-738l288 288-672 672h-288v-288zm444 132l-92 92-288-288 92-92q28-28 68-28t68 28l152 152q28 28 28 68t-28 68z"/></svg> <svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m13.436 1.413 1.415 1.414a1 1 0 0 1 0 1.414l-10.316 10.315a1 1 0 0 1 -.703.293l-2.407.008-.008-2.421a1 1 0 0 1 .293-.71l10.312-10.314a1 1 0 0 1 1.414 0zm-9.608 12.436 10.315-10.315-1.413-1.414-10.313 10.312.005 1.422zm7.486-10.313 1.414 1.414-7.778 7.778-1.407.007-.007-1.421zm1.414-1.415 1.414 1.415-.707.707-1.414-1.415z"/></svg>
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1664 960q-152-236-381-353 61 104 61 225 0 185-131.5 316.5t-316.5 131.5-316.5-131.5-131.5-316.5q0-121 61-225-229 117-381 353 133 205 333.5 326.5t434.5 121.5 434.5-121.5 333.5-326.5zm-720-384q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm848 384q0 34-20 69-140 230-376.5 368.5t-499.5 138.5-499.5-139-376.5-368q-20-35-20-69t20-69q140-229 376.5-368t499.5-139 499.5 139 376.5 368q20 35 20 69z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8 14C4.816 14 2.253 12.284.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2s5.747 1.716 7.607 5.019a2 2 0 0 1 0 1.962C13.747 12.284 11.184 14 8 14zm0-2c2.41 0 4.338-1.29 5.864-4C12.338 5.29 10.411 4 8 4 5.59 4 3.662 5.29 2.136 8 3.662 10.71 5.589 12 8 12zm0-1a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm1-3a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></svg>
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M555 1335l78-141q-87-63-136-159t-49-203q0-121 61-225-229 117-381 353 167 258 427 375zm389-759q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm363-191q0 7-1 9-105 188-315 566t-316 567l-49 89q-10 16-28 16-12 0-134-70-16-10-16-28 0-12 44-87-143-65-263.5-173t-208.5-245q-20-31-20-69t20-69q153-235 380-371t496-136q89 0 180 17l54-97q10-16 28-16 5 0 18 6t31 15.5 33 18.5 31.5 18.5 19.5 11.5q16 10 16 27zm37 447q0 139-79 253.5t-209 164.5l280-502q8 45 8 84zm448 128q0 35-20 69-39 64-109 145-150 172-347.5 267t-419.5 95l74-132q212-18 392.5-137t301.5-307q-115-179-282-294l63-112q95 64 182.5 153t144.5 184q20 34 20 69z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M13.618 2.62L1.62 14.619a1 1 0 0 1-.985-1.668l1.525-1.526C1.516 10.742.926 9.927.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2c1.074 0 2.076.195 3.006.58l.944-.944a1 1 0 0 1 1.668.985zM8.068 11a3 3 0 0 0 2.931-2.932l-2.931 2.931zm-3.02-2.462a3 3 0 0 1 3.49-3.49l.884-.884A6.044 6.044 0 0 0 8 4C5.59 4 3.662 5.29 2.136 8c.445.79.924 1.46 1.439 2.011l1.473-1.473zm.421 5.06l1.658-1.658c.283.04.575.06.873.06 2.41 0 4.338-1.29 5.864-4a11.023 11.023 0 0 0-1.133-1.664l1.418-1.418a12.799 12.799 0 0 1 1.458 2.1 2 2 0 0 1 0 1.963C13.747 12.284 11.184 14 8 14a7.883 7.883 0 0 1-2.53-.402z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="m2 3c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1m.761.85c.154 2.556 1.987 4.692 4.45 5.255.328-.655 1.01-1.105 1.789-1.105 1.105 0 2 .895 2 2 0 1.105-.895 2-2 2-.89 0-1.645-.582-1.904-1.386-1.916-.376-3.548-1.5-4.596-3.044v4.493c.863.222 1.5 1.01 1.5 1.937 0 1.105-.895 2-2 2-1.105 0-2-.895-2-2 0-.74.402-1.387 1-1.732v-8.535c-.598-.346-1-.992-1-1.732 0-1.105.895-2 2-2 1.105 0 2 .895 2 2 0 .835-.512 1.551-1.239 1.85m6.239 7.15c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1m-7 4c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1" transform="translate(3)"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M11 12.268V5a1 1 0 0 0-1-1v1a.5.5 0 0 1-.8.4l-2.667-2a.5.5 0 0 1 0-.8L9.2.6a.5.5 0 0 1 .8.4v1a3 3 0 0 1 3 3v7.268a2 2 0 1 1-2 0zm-6 0a2 2 0 1 1-2 0V4.732a2 2 0 1 1 2 0v7.536zM4 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm8 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></svg>
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M491 1536l91-91-235-235-91 91v107h128v128h107zm523-928q0-22-22-22-10 0-17 7l-542 542q-7 7-7 17 0 22 22 22 10 0 17-7l542-542q7-7 7-17zm-54-192l416 416-832 832h-416v-416zm683 96q0 53-37 90l-166 166-416-416 166-165q36-38 90-38 53 0 91 38l235 234q37 39 37 91z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M13.02 1.293l1.414 1.414a1 1 0 0 1 0 1.414L4.119 14.436a1 1 0 0 1-.704.293l-2.407.008L1 12.316a1 1 0 0 1 .293-.71L11.605 1.292a1 1 0 0 1 1.414 0zm-1.416 1.415l-.707.707L12.31 4.83l.707-.707-1.414-1.415zM3.411 13.73l1.123-1.122H3.12v-1.415L2 12.312l.005 1.422 1.406-.005z"/></svg>
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M666 481q-60 92-137 273-22-45-37-72.5t-40.5-63.5-51-56.5-63-35-81.5-14.5h-224q-14 0-23-9t-9-23v-192q0-14 9-23t23-9h224q250 0 410 225zm1126 799q0 14-9 23l-320 320q-9 9-23 9-13 0-22.5-9.5t-9.5-22.5v-192q-32 0-85 .5t-81 1-73-1-71-5-64-10.5-63-18.5-58-28.5-59-40-55-53.5-56-69.5q59-93 136-273 22 45 37 72.5t40.5 63.5 51 56.5 63 35 81.5 14.5h256v-192q0-14 9-23t23-9q12 0 24 10l319 319q9 9 9 23zm0-896q0 14-9 23l-320 320q-9 9-23 9-13 0-22.5-9.5t-9.5-22.5v-192h-256q-48 0-87 15t-69 45-51 61.5-45 77.5q-32 62-78 171-29 66-49.5 111t-54 105-64 100-74 83-90 68.5-106.5 42-128 16.5h-224q-14 0-23-9t-9-23v-192q0-14 9-23t23-9h224q48 0 87-15t69-45 51-61.5 45-77.5q32-62 78-171 29-66 49.5-111t54-105 64-100 74-83 90-68.5 106.5-42 128-16.5h256v-192q0-14 9-23t23-9q12 0 24 10l319 319q9 9 9 23z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586zM5 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></svg>
<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="M0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7z"/><path d="M1 7c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6z" fill="#FFF"/><rect x="3.36" y="6.16" width="7.28" height="1.68" rx=".84"/></svg> <svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.83c.39-.39 1.024-.39 1.414 0 .39.392.39 1.025 0 1.416l-3.535 3.535c-.196.195-.452.293-.707.293-.256 0-.512-.097-.708-.292l-2.12-2.12c-.39-.392-.39-1.025 0-1.415s1.023-.39 1.413 0zM8 16c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8zm0-2c3.314 0 6-2.686 6-6s-2.686-6-6-6-6 2.686-6 6 2.686 6 6 6z"/></svg>
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M384 448q0-53-37.5-90.5t-90.5-37.5-90.5 37.5-37.5 90.5 37.5 90.5 90.5 37.5 90.5-37.5 37.5-90.5zm1067 576q0 53-37 90l-491 492q-39 37-91 37-53 0-90-37l-715-716q-38-37-64.5-101t-26.5-117v-416q0-52 38-90t90-38h416q53 0 117 26.5t102 64.5l715 714q37 39 37 91zm384 0q0 53-37 90l-491 492q-39 37-91 37-36 0-59-14t-53-45l470-470q37-37 37-90 0-52-37-91l-715-714q-38-38-102-64.5t-117-26.5h224q53 0 117 26.5t102 64.5l715 714q37 39 37 91z"/></svg> <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m7.222.222c1.657 0 3 1.343 3 3v8.327c0 .631-.298 1.226-.805 1.603l-3 2.237c-.709.529-1.682.529-2.391 0l-3-2.237c-.506-.377-.805-.972-.805-1.603v-8.327c0-1.657 1.343-3 3-3h4m-5 3v8.08c0 .158.075.306.201.401l2.5 1.864c.177.132.42.132.598 0l2.5-1.864c.127-.094.201-.243.201-.401v-8.08c0-.552-.448-1-1-1h-4c-.552 0-1 .448-1 1m2.778 7.778c-.552 0-1-.448-1-1s .448-1 1-1 1 .448 1 1-.448 1-1 1" transform="matrix(-.70711 .70711 -.70711 -.70711 17.05 9.767)"/></svg>
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1600 1405q0 120-73 189.5t-194 69.5h-874q-121 0-194-69.5t-73-189.5q0-53 3.5-103.5t14-109 26.5-108.5 43-97.5 62-81 85.5-53.5 111.5-20q9 0 42 21.5t74.5 48 108 48 133.5 21.5 133.5-21.5 108-48 74.5-48 42-21.5q61 0 111.5 20t85.5 53.5 62 81 43 97.5 26.5 108.5 14 109 3.5 103.5zm-320-893q0 159-112.5 271.5t-271.5 112.5-271.5-112.5-112.5-271.5 112.5-271.5 271.5-112.5 271.5 112.5 112.5 271.5z"/></svg> <svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 7C6.343 7 5 5.657 5 4s1.343-3 3-3 3 1.343 3 3-1.343 3-3 3zm0 8c-6.888 0-6.976-.78-6.976-2.52S2.144 8 8 8s6.976 2.692 6.976 4.48S14.888 15 8 15z" fill-rule="evenodd"/></svg>
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%li.filter-dropdown-item{ class: ('js-current-user' if user == current_user) } %li.filter-dropdown-item{ class: ('js-current-user' if user == current_user) }
%button.btn.btn-link.dropdown-user{ type: :button } %button.btn.btn-link.dropdown-user{ type: :button }
.avatar-container.s40 .avatar-container.s40
= user_avatar_without_link(user: user, lazy: avatar[:lazy], url: avatar[:url], size: 40).gsub('/images/{{avatar_url}}','{{avatar_url}}').html_safe = user_avatar_without_link(user: user, lazy: avatar[:lazy], url: avatar[:url], size: 40, has_tooltip: false).gsub('/images/{{avatar_url}}','{{avatar_url}}').html_safe
.dropdown-user-details .dropdown-user-details
%span %span
= user.name = user.name
......
...@@ -4,18 +4,25 @@ class AuthorizedProjectsWorker ...@@ -4,18 +4,25 @@ class AuthorizedProjectsWorker
# Schedules multiple jobs and waits for them to be completed. # Schedules multiple jobs and waits for them to be completed.
def self.bulk_perform_and_wait(args_list) def self.bulk_perform_and_wait(args_list)
job_ids = bulk_perform_async(args_list) waiter = Gitlab::JobWaiter.new(args_list.size)
Gitlab::JobWaiter.new(job_ids).wait # Point all the bulk jobs at the same JobWaiter. Converts, [[1], [2], [3]]
# into [[1, "key"], [2, "key"], [3, "key"]]
waiting_args_list = args_list.map { |args| args << waiter.key }
bulk_perform_async(waiting_args_list)
waiter.wait
end end
def self.bulk_perform_async(args_list) def self.bulk_perform_async(args_list)
Sidekiq::Client.push_bulk('class' => self, 'queue' => sidekiq_options['queue'], 'args' => args_list) Sidekiq::Client.push_bulk('class' => self, 'queue' => sidekiq_options['queue'], 'args' => args_list)
end end
def perform(user_id) def perform(user_id, notify_key = nil)
user = User.find_by(id: user_id) user = User.find_by(id: user_id)
user&.refresh_authorized_projects user&.refresh_authorized_projects
ensure
Gitlab::JobWaiter.notify(notify_key, jid) if notify_key
end end
end end
...@@ -18,7 +18,8 @@ class NamespacelessProjectDestroyWorker ...@@ -18,7 +18,8 @@ class NamespacelessProjectDestroyWorker
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
return return
end end
return unless project.namespace_id.nil? # Reject doing anything for projects that *do* have a namespace
return if project.namespace # Reject doing anything for projects that *do* have a namespace
project.team.truncate project.team.truncate
......
---
title: "Insert user name directly without encoding"
merge_request: 10085
author: Nathan Neulinger <nneul@neulinger.org>
---
title: Expose target_iid in Events API
merge_request: 13247
author: sue445
---
title: Expose noteable_iid in Note
merge_request: 13265
author: sue445
---
title: Alert the user if a Wiki page changed while they were editing it in order to prevent overwriting changes.
merge_request: 9707
author: Hiroyuki Sato
---
title: Remove Inactive Personal Access Tokens list from Access Tokens page
merge_request: 12866
author:
---
title: Add coordinator url to admin area runner page
merge_request: 11603
author:
---
title: Declare related resources into V4 API entities
merge_request:
author:
---
title: Replaces dashboard/event_filters.feature spinach with rspec
merge_request: 12651
author: Alexander Randa (@randaalex)
---
title: Replaces dashboard/dashboard.feature spinach with rspec
merge_request: 12876
author: Alexander Randa (@randaalex)
---
title: Added /duplicate quick action to close a duplicate issue
merge_request: 12845
author: Ryan Scott
---
title: Fix timezone inconsistencies in user contribution graph
merge_request: 13208
author:
---
title: Decrease ABC threshold to 56.96
merge_request: 11227
author: Maxim Rydkin
---
title: Hashed Storage support for Repositories (EXPERIMENTAL)
merge_request: 13246
author:
---
title: Don't send rejection mails for all auto-generated mails
merge_request: 13254
author:
---
title: Handle errors while a project is being deleted asynchronously.
merge_request: 11088
author:
---
title: Add /shrug and /tableflip commands
merge_request: 10068
author: Alex Ives
---
title: refactor initializations in dropzone_input.js
merge_request: 12768
author: Brandon Everett
---
title: Disallow running the pipeline if ref is protected and user cannot merge the
branch or create the tag
merge_request: 11910
author:
---
title: Remove project_key from the Jira configuration
merge_request: 12050
author:
---
title: Unlock stuck merge request and set the proper state
merge_request: 13207
author:
---
title: Add Slack and JIRA services counts to Usage Data
merge_request:
author:
---
title: Prevent web hook and project service background jobs from going to the dead
jobs queue
merge_request:
author:
---
title: Display specific error message when JIRA test fails
merge_request:
author:
---
title: Move some code from services to workers in order to improve performance
merge_request: 13326
author:
---
title: clean up merge request widget UI
merge_request:
author:
---
title: Associate Issues tab only with internal issues tracker
merge_request:
author:
---
title: Add CSRF token verification to API
merge_request: 12154
author: Vitaliy @blackst0ne Klachkov
---
title: Remove events column from notification settings table
merge_request:
author:
---
title: Clarifies and rearranges the input variables on the kubernetes integration
page and adjusts the docs slightly to meet the same order
merge_request: !12188
author:
---
title: Respect blockquote line breaks in markdown
merge_request:
author:
---
title: Update confidential issue UI - add confidential visibility and settings to
sidebar
merge_request:
author:
---
title: Add icons to contextual sidebars
merge_request:
author:
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment