summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/dispatcher.js13
-rw-r--r--app/helpers/diff_helper.rb4
-rw-r--r--app/models/legacy_diff_note.rb2
-rw-r--r--app/models/user.rb29
-rw-r--r--app/services/users/refresh_authorized_projects_service.rb2
-rw-r--r--app/views/projects/_zen.html.haml7
-rw-r--r--app/views/projects/buttons/_fork.html.haml4
-rw-r--r--app/views/projects/snippets/show.html.haml2
-rw-r--r--app/views/shared/notes/_form.html.haml4
-rw-r--r--app/views/shared/notes/_notes_with_form.html.haml2
-rw-r--r--changelogs/unreleased/25164-disable-fork-on-project-limit.yml4
-rw-r--r--changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml4
-rw-r--r--changelogs/unreleased/moved-submodules.yml4
-rw-r--r--changelogs/unreleased/reduce-sidekiq-wait-timings.yml4
-rw-r--r--doc/administration/job_artifacts.md14
-rw-r--r--lib/gitlab/current_settings.rb46
-rw-r--r--lib/gitlab/database/migration_helpers.rb6
-rw-r--r--lib/gitlab/diff/line.rb20
-rw-r--r--lib/gitlab/diff/parallel_diff.rb20
-rw-r--r--lib/gitlab/fake_application_settings.rb27
-rw-r--r--lib/gitlab/git/gitmodules_parser.rb77
-rw-r--r--lib/gitlab/git/repository.rb52
-rw-r--r--lib/gitlab/job_waiter.rb2
-rw-r--r--locale/bg/gitlab.po3
-rw-r--r--locale/de/gitlab.po3
-rw-r--r--locale/en/gitlab.po3
-rw-r--r--locale/es/gitlab.po3
-rw-r--r--locale/gitlab.pot3
-rw-r--r--locale/pt_BR/gitlab.po3
-rw-r--r--locale/zh_CN/gitlab.po3
-rw-r--r--locale/zh_HK/gitlab.po3
-rw-r--r--locale/zh_TW/gitlab.po3
-rw-r--r--spec/factories/application_settings.rb4
-rw-r--r--spec/features/projects/snippets_spec.rb25
-rw-r--r--spec/features/snippets/notes_on_personal_snippets_spec.rb16
-rw-r--r--spec/helpers/diff_helper_spec.rb11
-rw-r--r--spec/lib/gitlab/current_settings_spec.rb31
-rw-r--r--spec/lib/gitlab/fake_application_settings_spec.rb32
-rw-r--r--spec/lib/gitlab/git/gitmodules_parser_spec.rb28
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb8
-rw-r--r--spec/models/user_spec.rb34
41 files changed, 458 insertions, 107 deletions
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index d6216b0c4cf..f4a30f7d393 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -79,7 +79,18 @@ import initSettingsPanels from './settings_panels';
path = page.split(':');
shortcut_handler = null;
- new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources).setup();
+ $('.js-gfm-input').each((i, el) => {
+ const gfm = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources);
+ const enableGFM = gl.utils.convertPermissionToBoolean(el.dataset.supportsAutocomplete);
+ gfm.setup($(el), {
+ emojis: true,
+ members: enableGFM,
+ issues: enableGFM,
+ milestones: enableGFM,
+ mergeRequests: enableGFM,
+ labels: enableGFM,
+ });
+ });
function initBlob() {
new LineHighlighter();
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index 06822747d11..16a99addd0b 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -66,12 +66,12 @@ module DiffHelper
discussions_left = discussions_right = nil
- if left && (left.unchanged? || left.discussable?)
+ if left && left.discussable? && (left.unchanged? || left.removed?)
line_code = diff_file.line_code(left)
discussions_left = @grouped_diff_discussions[line_code]
end
- if right&.discussable?
+ if right && right.discussable? && right.added?
line_code = diff_file.line_code(right)
discussions_right = @grouped_diff_discussions[line_code]
end
diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb
index 7126de2d488..2d5909ab25e 100644
--- a/app/models/legacy_diff_note.rb
+++ b/app/models/legacy_diff_note.rb
@@ -42,7 +42,7 @@ class LegacyDiffNote < Note
end
def for_line?(line)
- !line.meta? && diff_file.line_code(line) == self.line_code
+ line.discussable? && diff_file.line_code(line) == self.line_code
end
def original_line_code
diff --git a/app/models/user.rb b/app/models/user.rb
index 5d128e4b390..782c162e1f3 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -139,21 +139,21 @@ class User < ActiveRecord::Base
presence: true,
uniqueness: { case_sensitive: false }
- validate :namespace_uniq, if: ->(user) { user.username_changed? }
+ validate :namespace_uniq, if: :username_changed?
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
- validate :unique_email, if: ->(user) { user.email_changed? }
- validate :owns_notification_email, if: ->(user) { user.notification_email_changed? }
- validate :owns_public_email, if: ->(user) { user.public_email_changed? }
+ validate :unique_email, if: :email_changed?
+ validate :owns_notification_email, if: :notification_email_changed?
+ validate :owns_public_email, if: :public_email_changed?
validate :signup_domain_valid?, on: :create, if: ->(user) { !user.created_by_id }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
before_validation :sanitize_attrs
- before_validation :set_notification_email, if: ->(user) { user.email_changed? }
- before_validation :set_public_email, if: ->(user) { user.public_email_changed? }
+ before_validation :set_notification_email, if: :email_changed?
+ before_validation :set_public_email, if: :public_email_changed?
- after_update :update_emails_with_primary_email, if: ->(user) { user.email_changed? }
+ after_update :update_emails_with_primary_email, if: :email_changed?
before_save :ensure_authentication_token, :ensure_incoming_email_token
- before_save :ensure_external_user_rights
+ before_save :ensure_user_rights_and_limits, if: :external_changed?
after_save :ensure_namespace_correct
after_initialize :set_projects_limit
after_destroy :post_destroy_hook
@@ -1033,11 +1033,14 @@ class User < ActiveRecord::Base
super
end
- def ensure_external_user_rights
- return unless external?
-
- self.can_create_group = false
- self.projects_limit = 0
+ def ensure_user_rights_and_limits
+ if external?
+ self.can_create_group = false
+ self.projects_limit = 0
+ else
+ self.can_create_group = gitlab_config.default_can_create_group
+ self.projects_limit = current_application_settings.default_projects_limit
+ end
end
def signup_domain_valid?
diff --git a/app/services/users/refresh_authorized_projects_service.rb b/app/services/users/refresh_authorized_projects_service.rb
index 3e07b811027..f028f5eb0d4 100644
--- a/app/services/users/refresh_authorized_projects_service.rb
+++ b/app/services/users/refresh_authorized_projects_service.rb
@@ -34,7 +34,7 @@ module Users
# Keep trying until we obtain the lease. If we don't do so we may end up
# not updating the list of authorized projects properly. To prevent
# hammering Redis too much we'll wait for a bit between retries.
- sleep(1)
+ sleep(0.1)
end
begin
diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml
index bff11d888a7..afc40ca4eab 100644
--- a/app/views/projects/_zen.html.haml
+++ b/app/views/projects/_zen.html.haml
@@ -1,10 +1,15 @@
- @gfm_form = true
- current_text ||= nil
+- supports_autocomplete = local_assigns.fetch(:supports_autocomplete, true)
- supports_quick_actions = local_assigns.fetch(:supports_quick_actions, false)
.zen-backdrop
- classes << ' js-gfm-input js-autosize markdown-area'
- if defined?(f) && f
- = f.text_area attr, class: classes, placeholder: placeholder, data: { supports_quick_actions: supports_quick_actions }
+ = f.text_area attr,
+ class: classes,
+ placeholder: placeholder,
+ data: { supports_quick_actions: supports_quick_actions,
+ supports_autocomplete: supports_autocomplete }
- else
= text_area_tag attr, current_text, class: classes, placeholder: placeholder
%a.zen-control.zen-control-leave.js-zen-leave{ href: "#" }
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index d3d44c45cc7..42f8c75f57b 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -4,6 +4,10 @@
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: _('Go to your fork'), class: 'btn has-tooltip' do
= custom_icon('icon_fork')
%span= s_('GoToYourFork|Fork')
+ - elsif !current_user.can_create_project?
+ = link_to new_namespace_project_fork_path(@project.namespace, @project), title: _('You have reached your project limit'), class: 'btn has-tooltip disabled' do
+ = custom_icon('icon_fork')
+ %span= s_('CreateNewFork|Fork')
- else
= link_to new_namespace_project_fork_path(@project.namespace, @project), class: 'btn' do
= custom_icon('icon_fork')
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index 847f3c2f348..37b114e7a3a 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -9,4 +9,4 @@
.row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true
- #notes= render "shared/notes/notes_with_form", :autocomplete => true
+ #notes= render "shared/notes/notes_with_form", :autocomplete => false
diff --git a/app/views/shared/notes/_form.html.haml b/app/views/shared/notes/_form.html.haml
index 32ff505cbda..c6b5dcc3647 100644
--- a/app/views/shared/notes/_form.html.haml
+++ b/app/views/shared/notes/_form.html.haml
@@ -1,3 +1,4 @@
+- supports_autocomplete = local_assigns.fetch(:supports_autocomplete, true)
- supports_quick_actions = note_supports_quick_actions?(@note)
- if supports_quick_actions
- preview_url = preview_markdown_path(@project, quick_actions_target_type: @note.noteable_type, quick_actions_target_id: @note.noteable_id)
@@ -27,7 +28,8 @@
attr: :note,
classes: 'note-textarea js-note-text',
placeholder: "Write a comment or drag your files here...",
- supports_quick_actions: supports_quick_actions
+ supports_quick_actions: supports_quick_actions,
+ supports_autocomplete: supports_autocomplete
= render 'shared/notes/hints', supports_quick_actions: supports_quick_actions
.error-alert
diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml
index 5902798dfd0..0cca8d875d2 100644
--- a/app/views/shared/notes/_notes_with_form.html.haml
+++ b/app/views/shared/notes/_notes_with_form.html.haml
@@ -12,7 +12,7 @@
%a.author_link{ href: user_path(current_user) }
= image_tag avatar_icon(current_user), alt: current_user.to_reference, class: 'avatar s40'
.timeline-content.timeline-content-form
- = render "shared/notes/form", view: diff_view
+ = render "shared/notes/form", view: diff_view, supports_autocomplete: autocomplete
- elsif !current_user
.disabled-comment.text-center.prepend-top-default
Please
diff --git a/changelogs/unreleased/25164-disable-fork-on-project-limit.yml b/changelogs/unreleased/25164-disable-fork-on-project-limit.yml
new file mode 100644
index 00000000000..9fa824b161d
--- /dev/null
+++ b/changelogs/unreleased/25164-disable-fork-on-project-limit.yml
@@ -0,0 +1,4 @@
+---
+title: Disable fork button on project limit
+merge_request: 12145
+author: Ivan Chernov
diff --git a/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml b/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml
new file mode 100644
index 00000000000..3058404b3f8
--- /dev/null
+++ b/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml
@@ -0,0 +1,4 @@
+---
+title: Ensures default user limits when external user is unchecked
+merge_request: 12218
+author:
diff --git a/changelogs/unreleased/moved-submodules.yml b/changelogs/unreleased/moved-submodules.yml
new file mode 100644
index 00000000000..eee858717ed
--- /dev/null
+++ b/changelogs/unreleased/moved-submodules.yml
@@ -0,0 +1,4 @@
+---
+title: 'Handle renamed submodules in repository browser'
+merge_request: 10798
+author: David Turner
diff --git a/changelogs/unreleased/reduce-sidekiq-wait-timings.yml b/changelogs/unreleased/reduce-sidekiq-wait-timings.yml
new file mode 100644
index 00000000000..4d23accc82e
--- /dev/null
+++ b/changelogs/unreleased/reduce-sidekiq-wait-timings.yml
@@ -0,0 +1,4 @@
+---
+title: Reduce time spent waiting for certain Sidekiq jobs to complete
+merge_request:
+author:
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index 5599435564e..3587696225c 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -46,7 +46,10 @@ To disable artifacts site-wide, follow the steps below.
After a successful job, GitLab Runner uploads an archive containing the job
artifacts to GitLab.
-To change the location where the artifacts are stored, follow the steps below.
+### Using local storage
+
+To change the location where the artifacts are stored locally, follow the steps
+below.
---
@@ -82,6 +85,13 @@ _The artifacts are stored by default in
1. Save the file and [restart GitLab][] for the changes to take effect.
+### Using object storage
+
+In [GitLab Enterprise Edition Premium][eep] you can use an object storage like
+AWS S3 to store the artifacts.
+
+[Learn how to use the object storage option.][ee-os]
+
## Expiring artifacts
If an expiry date is used for the artifacts, they are marked for deletion
@@ -148,3 +158,5 @@ memory and disk I/O.
[reconfigure gitlab]: restart_gitlab.md "How to restart GitLab"
[restart gitlab]: restart_gitlab.md "How to restart GitLab"
[gitlab workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse "GitLab Workhorse repository"
+[ee-os]: https://docs.gitlab.com/ee/administration/job_artifacts.html#using-object-storage
+[eep]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition Premium"
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 2fd5452dd78..284e6ad55a5 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -10,43 +10,49 @@ module Gitlab
delegate :sidekiq_throttling_enabled?, to: :current_application_settings
- def fake_application_settings
- OpenStruct.new(::ApplicationSetting.defaults)
+ def fake_application_settings(defaults = ApplicationSetting.defaults)
+ FakeApplicationSettings.new(defaults)
end
private
def ensure_application_settings!
- unless ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
- settings = retrieve_settings_from_database?
- end
+ return in_memory_application_settings if ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
- settings || in_memory_application_settings
+ cached_application_settings || uncached_application_settings
end
- def retrieve_settings_from_database?
- settings = retrieve_settings_from_database_cache?
- return settings if settings.present?
-
- return fake_application_settings unless connect_to_db?
-
+ def cached_application_settings
begin
- db_settings = ::ApplicationSetting.current
- # In case Redis isn't running or the Redis UNIX socket file is not available
+ ApplicationSetting.cached
rescue ::Redis::BaseError, ::Errno::ENOENT
- db_settings = ::ApplicationSetting.last
+ # In case Redis isn't running or the Redis UNIX socket file is not available
end
- db_settings || ::ApplicationSetting.create_from_defaults
end
- def retrieve_settings_from_database_cache?
+ def uncached_application_settings
+ return fake_application_settings unless connect_to_db?
+
+ # This loads from the database into the cache, so handle Redis errors
begin
- settings = ApplicationSetting.cached
+ db_settings = ApplicationSetting.current
rescue ::Redis::BaseError, ::Errno::ENOENT
# In case Redis isn't running or the Redis UNIX socket file is not available
- settings = nil
end
- settings
+
+ # If there are pending migrations, it's possible there are columns that
+ # need to be added to the application settings. To prevent Rake tasks
+ # and other callers from failing, use any loaded settings and return
+ # defaults for missing columns.
+ if ActiveRecord::Migrator.needs_migration?
+ defaults = ApplicationSetting.defaults
+ defaults.merge!(db_settings.attributes.symbolize_keys) if db_settings.present?
+ return fake_application_settings(defaults)
+ end
+
+ return db_settings if db_settings.present?
+
+ ApplicationSetting.create_from_defaults || in_memory_application_settings
end
def in_memory_application_settings
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index cd85f961242..9181202a091 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -233,6 +233,12 @@ module Gitlab
# Update in batches of 5% until we run out of any rows to update.
batch_size = ((total / 100.0) * 5.0).ceil
+ max_size = 1000
+
+ # The upper limit is 1000 to ensure we don't lock too many rows. For
+ # example, for "merge_requests" even 1% of the table is around 35 000
+ # rows for GitLab.com.
+ batch_size = max_size if batch_size > max_size
start_arel = table.project(table[:id]).order(table[:id].asc).take(1)
start_arel = yield table, start_arel if block_given?
diff --git a/lib/gitlab/diff/line.rb b/lib/gitlab/diff/line.rb
index bd52ae47e9f..2d89ccfc354 100644
--- a/lib/gitlab/diff/line.rb
+++ b/lib/gitlab/diff/line.rb
@@ -42,25 +42,25 @@ module Gitlab
end
def added?
- type == 'new' || type == 'new-nonewline'
+ %w[new new-nonewline].include?(type)
end
def removed?
- type == 'old' || type == 'old-nonewline'
- end
-
- def rich_text
- @parent_file.highlight_lines! if @parent_file && !@rich_text
-
- @rich_text
+ %w[old old-nonewline].include?(type)
end
def meta?
- type == 'match'
+ %w[match new-nonewline old-nonewline].include?(type)
end
def discussable?
- !['match', 'new-nonewline', 'old-nonewline'].include?(type)
+ !meta?
+ end
+
+ def rich_text
+ @parent_file.highlight_lines! if @parent_file && !@rich_text
+
+ @rich_text
end
def as_json(opts = nil)
diff --git a/lib/gitlab/diff/parallel_diff.rb b/lib/gitlab/diff/parallel_diff.rb
index 481536a380b..0cb26fa45c8 100644
--- a/lib/gitlab/diff/parallel_diff.rb
+++ b/lib/gitlab/diff/parallel_diff.rb
@@ -14,16 +14,7 @@ module Gitlab
lines = []
highlighted_diff_lines = diff_file.highlighted_diff_lines
highlighted_diff_lines.each do |line|
- if line.meta? || line.unchanged?
- # line in the right panel is the same as in the left one
- lines << {
- left: line,
- right: line
- }
-
- free_right_index = nil
- i += 1
- elsif line.removed?
+ if line.removed?
lines << {
left: line,
right: nil
@@ -51,6 +42,15 @@ module Gitlab
free_right_index = nil
i += 1
end
+ elsif line.meta? || line.unchanged?
+ # line in the right panel is the same as in the left one
+ lines << {
+ left: line,
+ right: line
+ }
+
+ free_right_index = nil
+ i += 1
end
end
diff --git a/lib/gitlab/fake_application_settings.rb b/lib/gitlab/fake_application_settings.rb
new file mode 100644
index 00000000000..bb14a8cd9e7
--- /dev/null
+++ b/lib/gitlab/fake_application_settings.rb
@@ -0,0 +1,27 @@
+# This class extends an OpenStruct object by adding predicate methods to mimic
+# ActiveRecord access. We rely on the initial values being true or false to
+# determine whether to define a predicate method because for a newly-added
+# column that has not been migrated yet, there is no way to determine the
+# column type without parsing db/schema.rb.
+module Gitlab
+ class FakeApplicationSettings < OpenStruct
+ def initialize(options = {})
+ super
+
+ FakeApplicationSettings.define_predicate_methods(options)
+ end
+
+ # Mimic ActiveRecord predicate methods for boolean values
+ def self.define_predicate_methods(options)
+ options.each do |key, value|
+ next if key.to_s.end_with?('?')
+ next unless [true, false].include?(value)
+
+ define_method "#{key}?" do
+ actual_key = key.to_s.chomp('?')
+ self[actual_key]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/gitmodules_parser.rb b/lib/gitlab/git/gitmodules_parser.rb
new file mode 100644
index 00000000000..f4e3b5e5129
--- /dev/null
+++ b/lib/gitlab/git/gitmodules_parser.rb
@@ -0,0 +1,77 @@
+module Gitlab
+ module Git
+ class GitmodulesParser
+ def initialize(content)
+ @content = content
+ end
+
+ # Parses the contents of a .gitmodules file and returns a hash of
+ # submodule information, indexed by path.
+ def parse
+ reindex_by_path(get_submodules_by_name)
+ end
+
+ private
+
+ class State
+ def initialize
+ @result = {}
+ @current_submodule = nil
+ end
+
+ def start_section(section)
+ # In some .gitmodules files (e.g. nodegit's), a header
+ # with the same name appears multiple times; we want to
+ # accumulate the configs across these
+ @current_submodule = @result[section] || { 'name' => section }
+ @result[section] = @current_submodule
+ end
+
+ def set_attribute(attr, value)
+ @current_submodule[attr] = value
+ end
+
+ def section_started?
+ !@current_submodule.nil?
+ end
+
+ def submodules_by_name
+ @result
+ end
+ end
+
+ def get_submodules_by_name
+ iterator = State.new
+
+ @content.split("\n").each_with_object(iterator) do |text, iterator|
+ next if text =~ /^\s*#/
+
+ if text =~ /\A\[submodule "(?<name>[^"]+)"\]\z/
+ iterator.start_section($~[:name])
+ else
+ next unless iterator.section_started?
+
+ next unless text =~ /\A\s*(?<key>\w+)\s*=\s*(?<value>.*)\z/
+
+ value = $~[:value].chomp
+ iterator.set_attribute($~[:key], value)
+ end
+ end
+
+ iterator.submodules_by_name
+ end
+
+ def reindex_by_path(submodules_by_name)
+ # Convert from an indexed by name to an array indexed by path
+ # If a submodule doesn't have a path, it is considered bogus
+ # and is ignored
+ submodules_by_name.each_with_object({}) do |(name, data), results|
+ path = data.delete 'path'
+ next unless path
+
+ results[path] = data
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 85695d0a4df..c1f942f931a 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -617,9 +617,9 @@ module Gitlab
#
# Ex.
# {
- # "rack" => {
+ # "current_path/rack" => {
+ # "name" => "original_path/rack",
# "id" => "c67be4624545b4263184c4a0e8f887efd0a66320",
- # "path" => "rack",
# "url" => "git://github.com/chneukirchen/rack.git"
# },
# "encoding" => {
@@ -637,7 +637,8 @@ module Gitlab
return {}
end
- parse_gitmodules(commit, content)
+ parser = GitmodulesParser.new(content)
+ fill_submodule_ids(commit, parser.parse)
end
# Return total commits count accessible from passed ref
@@ -998,42 +999,19 @@ module Gitlab
end
end
- # Parses the contents of a .gitmodules file and returns a hash of
- # submodule information.
- def parse_gitmodules(commit, content)
- modules = {}
-
- name = nil
- content.each_line do |line|
- case line.strip
- when /\A\[submodule "(?<name>[^"]+)"\]\z/ # Submodule header
- name = $~[:name]
- modules[name] = {}
- when /\A(?<key>\w+)\s*=\s*(?<value>.*)\z/ # Key/value pair
- key = $~[:key]
- value = $~[:value].chomp
-
- next unless name && modules[name]
-
- modules[name][key] = value
-
- if key == 'path'
- begin
- modules[name]['id'] = blob_content(commit, value)
- rescue InvalidBlobName
- # The current entry is invalid
- modules.delete(name)
- name = nil
- end
- end
- when /\A#/ # Comment
- next
- else # Invalid line
- name = nil
+ # Fill in the 'id' field of a submodule hash from its values
+ # as-of +commit+. Return a Hash consisting only of entries
+ # from the submodule hash for which the 'id' field is filled.
+ def fill_submodule_ids(commit, submodule_data)
+ submodule_data.each do |path, data|
+ id = begin
+ blob_content(commit, path)
+ rescue InvalidBlobName
+ nil
end
+ data['id'] = id
end
-
- modules
+ submodule_data.select { |path, data| data['id'] }
end
# Returns true if +commit+ introduced changes to +path+, using commit
diff --git a/lib/gitlab/job_waiter.rb b/lib/gitlab/job_waiter.rb
index 8db91d25a4b..208f0e1bbea 100644
--- a/lib/gitlab/job_waiter.rb
+++ b/lib/gitlab/job_waiter.rb
@@ -14,7 +14,7 @@ module Gitlab
# timeout - The maximum amount of seconds to block the caller for. This
# ensures we don't indefinitely block a caller in case a job takes
# long to process, or is never processed.
- def wait(timeout = 60)
+ def wait(timeout = 10)
start = Time.current
while (Time.current - start) <= timeout
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
index e6caf83252d..43a5de65c43 100644
--- a/locale/bg/gitlab.po
+++ b/locale/bg/gitlab.po
@@ -250,6 +250,9 @@ msgstr "Искате ли да видите данните? Помолете а
msgid "We don't have enough data to show this stage."
msgstr "Няма достатъчно данни за този етап."
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You need permission."
msgstr "Нуждаете се от разрешение."
diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po
index 9a660571db9..ea864091b10 100644
--- a/locale/de/gitlab.po
+++ b/locale/de/gitlab.po
@@ -291,6 +291,9 @@ msgstr "Um diese Daten einsehen zu können, wenden Sie sich bitte an Ihren Admin
msgid "We don't have enough data to show this stage."
msgstr "Es liegen nicht genügend Daten vor, um diese Phase anzuzeigen."
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You need permission."
msgstr "Sie benötigen Zugriffsrechte."
diff --git a/locale/en/gitlab.po b/locale/en/gitlab.po
index 2f2075b131e..afb8fb3176f 100644
--- a/locale/en/gitlab.po
+++ b/locale/en/gitlab.po
@@ -984,6 +984,9 @@ msgstr ""
msgid "You can only add files when you are on a branch"
msgstr ""
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You must sign in to star a project"
msgstr ""
diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po
index 4ce9ce4b28a..6946a256308 100644
--- a/locale/es/gitlab.po
+++ b/locale/es/gitlab.po
@@ -987,6 +987,9 @@ msgstr "Solo puedes agregar archivos cuando estás en una rama"
msgid "You must sign in to star a project"
msgstr "Debes iniciar sesión para destacar un proyecto"
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You need permission."
msgstr "Necesitas permisos."
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f3f08cc4950..2d8076f5567 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -982,6 +982,9 @@ msgstr ""
msgid "You can only add files when you are on a branch"
msgstr ""
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You must sign in to star a project"
msgstr ""
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
index 5ad41f92b64..fe6d51c36ac 100644
--- a/locale/pt_BR/gitlab.po
+++ b/locale/pt_BR/gitlab.po
@@ -250,6 +250,9 @@ msgstr "Precisa visualizar os dados? Solicite acesso ao administrador."
msgid "We don't have enough data to show this stage."
msgstr "Não temos dados suficientes para mostrar esta fase."
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You need permission."
msgstr "Você precisa de permissão."
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index 11434460207..8d994ff3c4f 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -283,6 +283,9 @@ msgstr "权限不足。如需查看相关数据,请向管理员申请权限。
msgid "We don't have enough data to show this stage."
msgstr "该阶段的数据不足,无法显示。"
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You need permission."
msgstr "您需要相关的权限。"
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index 81b2ff863ea..f0a9e44daf3 100644
--- a/locale/zh_HK/gitlab.po
+++ b/locale/zh_HK/gitlab.po
@@ -283,6 +283,9 @@ msgstr "權限不足。如需查看相關數據,請向管理員申請權限。
msgid "We don't have enough data to show this stage."
msgstr "該階段的數據不足,無法顯示。"
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You need permission."
msgstr "您需要相關的權限。"
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index e40723a9d8d..5130572d7ed 100644
--- a/locale/zh_TW/gitlab.po
+++ b/locale/zh_TW/gitlab.po
@@ -283,6 +283,9 @@ msgstr "權限不足。如需查看相關資料,請向管理員申請權限。
msgid "We don't have enough data to show this stage."
msgstr "因該階段的資料不足而無法顯示相關資訊"
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You need permission."
msgstr "您需要相關的權限。"
diff --git a/spec/factories/application_settings.rb b/spec/factories/application_settings.rb
new file mode 100644
index 00000000000..aef65e724c2
--- /dev/null
+++ b/spec/factories/application_settings.rb
@@ -0,0 +1,4 @@
+FactoryGirl.define do
+ factory :application_setting do
+ end
+end
diff --git a/spec/features/projects/snippets_spec.rb b/spec/features/projects/snippets_spec.rb
index 18689c17fe9..da46b950440 100644
--- a/spec/features/projects/snippets_spec.rb
+++ b/spec/features/projects/snippets_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Project snippets', feature: true do
+describe 'Project snippets', :js, feature: true do
context 'when the project has snippets' do
let(:project) { create(:empty_project, :public) }
let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) }
@@ -26,5 +26,28 @@ describe 'Project snippets', feature: true do
expect(page).to have_content(snippets[1].title)
end
end
+
+ context 'when submitting a note' do
+ before do
+ login_as :admin
+ visit namespace_project_snippet_path(project.namespace, project, snippets[0])
+ end
+
+ it 'should not have autocomplete' do
+ wait_for_requests
+ request_count_before = page.driver.network_traffic.count
+
+ find('#note_note').native.send_keys('')
+ fill_in 'note[note]', with: '@'
+
+ wait_for_requests
+ request_count_after = page.driver.network_traffic.count
+
+ # This selector probably won't be in place even if autocomplete was enabled
+ # but we want to make sure
+ expect(page).not_to have_selector('.atwho-view')
+ expect(request_count_before).to eq(request_count_after)
+ end
+ end
end
end
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
index 44b0c89fac7..04b596d29a1 100644
--- a/spec/features/snippets/notes_on_personal_snippets_spec.rb
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -70,6 +70,22 @@ describe 'Comments on personal snippets', :js, feature: true do
expect(find('div#notes')).to have_content('This is awesome!')
end
+
+ it 'should not have autocomplete' do
+ wait_for_requests
+ request_count_before = page.driver.network_traffic.count
+
+ find('#note_note').native.send_keys('')
+ fill_in 'note[note]', with: '@'
+
+ wait_for_requests
+ request_count_after = page.driver.network_traffic.count
+
+ # This selector probably won't be in place even if autocomplete was enabled
+ # but we want to make sure
+ expect(page).not_to have_selector('.atwho-view')
+ expect(request_count_before).to eq(request_count_after)
+ end
end
context 'when editing a note' do
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index 0ac030d3171..0d909e6e140 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -148,12 +148,21 @@ describe DiffHelper do
it 'puts comments on added lines' do
left = Gitlab::Diff::Line.new('\\nonewline', 'old-nonewline', 3, 3, 3)
- right = Gitlab::Diff::Line.new('new line', 'add', 3, 3, 3)
+ right = Gitlab::Diff::Line.new('new line', 'new', 3, 3, 3)
result = helper.parallel_diff_discussions(left, right, diff_file)
expect(result).to eq([nil, 'comment'])
end
+
+ it 'puts comments on unchanged lines' do
+ left = Gitlab::Diff::Line.new('unchanged line', nil, 3, 3, 3)
+ right = Gitlab::Diff::Line.new('unchanged line', nil, 3, 3, 3)
+
+ result = helper.parallel_diff_discussions(left, right, diff_file)
+
+ expect(result).to eq(['comment', nil])
+ end
end
describe "#diff_match_line" do
diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb
index fda39d78610..a566f24f6a6 100644
--- a/spec/lib/gitlab/current_settings_spec.rb
+++ b/spec/lib/gitlab/current_settings_spec.rb
@@ -32,6 +32,37 @@ describe Gitlab::CurrentSettings do
expect(current_application_settings).to be_a(ApplicationSetting)
end
+
+ context 'with migrations pending' do
+ before do
+ expect(ActiveRecord::Migrator).to receive(:needs_migration?).and_return(true)
+ end
+
+ it 'returns an in-memory ApplicationSetting object' do
+ settings = current_application_settings
+
+ expect(settings).to be_a(OpenStruct)
+ expect(settings.sign_in_enabled?).to eq(settings.sign_in_enabled)
+ expect(settings.sign_up_enabled?).to eq(settings.sign_up_enabled)
+ end
+
+ it 'uses the existing database settings and falls back to defaults' do
+ db_settings = create(:application_setting,
+ home_page_url: 'http://mydomain.com',
+ signup_enabled: false)
+ settings = current_application_settings
+ app_defaults = ApplicationSetting.last
+
+ expect(settings).to be_a(OpenStruct)
+ expect(settings.home_page_url).to eq(db_settings.home_page_url)
+ expect(settings.signup_enabled?).to be_falsey
+ expect(settings.signup_enabled).to be_falsey
+
+ # Check that unspecified values use the defaults
+ settings.reject! { |key, _| [:home_page_url, :signup_enabled].include? key }
+ settings.each { |key, _| expect(settings[key]).to eq(app_defaults[key]) }
+ end
+ end
end
context 'with DB unavailable' do
diff --git a/spec/lib/gitlab/fake_application_settings_spec.rb b/spec/lib/gitlab/fake_application_settings_spec.rb
new file mode 100644
index 00000000000..b793176d84a
--- /dev/null
+++ b/spec/lib/gitlab/fake_application_settings_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe Gitlab::FakeApplicationSettings do
+ let(:defaults) { { signin_enabled: false, foobar: 'asdf', signup_enabled: true, 'test?' => 123 } }
+
+ subject { described_class.new(defaults) }
+
+ it 'wraps OpenStruct variables properly' do
+ expect(subject.signin_enabled).to be_falsey
+ expect(subject.signup_enabled).to be_truthy
+ expect(subject.foobar).to eq('asdf')
+ end
+
+ it 'defines predicate methods' do
+ expect(subject.signin_enabled?).to be_falsey
+ expect(subject.signup_enabled?).to be_truthy
+ end
+
+ it 'predicate method changes when value is updated' do
+ subject.signin_enabled = true
+
+ expect(subject.signin_enabled?).to be_truthy
+ end
+
+ it 'does not define a predicate method' do
+ expect(subject.foobar?).to be_nil
+ end
+
+ it 'does not override an existing predicate method' do
+ expect(subject.test?).to eq(123)
+ end
+end
diff --git a/spec/lib/gitlab/git/gitmodules_parser_spec.rb b/spec/lib/gitlab/git/gitmodules_parser_spec.rb
new file mode 100644
index 00000000000..143aa2218c9
--- /dev/null
+++ b/spec/lib/gitlab/git/gitmodules_parser_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Gitlab::Git::GitmodulesParser do
+ it 'should parse a .gitmodules file correctly' do
+ parser = described_class.new(<<-'GITMODULES'.strip_heredoc)
+ [submodule "vendor/libgit2"]
+ path = vendor/libgit2
+ [submodule "vendor/libgit2"]
+ url = https://github.com/nodegit/libgit2.git
+
+ # a comment
+ [submodule "moved"]
+ path = new/path
+ url = https://example.com/some/project
+ [submodule "bogus"]
+ url = https://example.com/another/project
+ GITMODULES
+
+ modules = parser.parse
+
+ expect(modules).to eq({
+ 'vendor/libgit2' => { 'name' => 'vendor/libgit2',
+ 'url' => 'https://github.com/nodegit/libgit2.git' },
+ 'new/path' => { 'name' => 'moved',
+ 'url' => 'https://example.com/some/project' }
+ })
+ end
+end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index eee4c9eab6d..02b3f167250 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -358,7 +358,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
expect(submodule).to eq([
"six", {
"id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
- "path" => "six",
+ "name" => "six",
"url" => "git://github.com/randx/six.git"
}
])
@@ -366,14 +366,14 @@ describe Gitlab::Git::Repository, seed_helper: true do
it 'should handle nested submodules correctly' do
nested = submodules['nested/six']
- expect(nested['path']).to eq('nested/six')
+ expect(nested['name']).to eq('nested/six')
expect(nested['url']).to eq('git://github.com/randx/six.git')
expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
end
it 'should handle deeply nested submodules correctly' do
nested = submodules['deeper/nested/six']
- expect(nested['path']).to eq('deeper/nested/six')
+ expect(nested['name']).to eq('deeper/nested/six')
expect(nested['url']).to eq('git://github.com/randx/six.git')
expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
end
@@ -393,7 +393,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
expect(submodules.first).to eq([
"six", {
"id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
- "path" => "six",
+ "name" => "six",
"url" => "git://github.com/randx/six.git"
}
])
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 1a1bbd60504..f6b7120e2c9 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -451,6 +451,40 @@ describe User, models: true do
end
end
+ describe '#ensure_user_rights_and_limits' do
+ describe 'with external user' do
+ let(:user) { create(:user, external: true) }
+
+ it 'receives callback when external changes' do
+ expect(user).to receive(:ensure_user_rights_and_limits)
+
+ user.update_attributes(external: false)
+ end
+
+ it 'ensures correct rights and limits for user' do
+ stub_config_setting(default_can_create_group: true)
+
+ expect { user.update_attributes(external: false) }.to change { user.can_create_group }.to(true)
+ .and change { user.projects_limit }.to(current_application_settings.default_projects_limit)
+ end
+ end
+
+ describe 'without external user' do
+ let(:user) { create(:user, external: false) }
+
+ it 'receives callback when external changes' do
+ expect(user).to receive(:ensure_user_rights_and_limits)
+
+ user.update_attributes(external: true)
+ end
+
+ it 'ensures correct rights and limits for user' do
+ expect { user.update_attributes(external: true) }.to change { user.can_create_group }.to(false)
+ .and change { user.projects_limit }.to(0)
+ end
+ end
+ end
+
describe 'rss token' do
it 'ensures an rss token on read' do
user = create(:user, rss_token: nil)