Commit 3c3def3c authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-03-29

# Conflicts:
#	.flayignore
#	CHANGELOG.md
#	app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
#	app/controllers/projects/milestones_controller.rb
#	app/helpers/page_layout_helper.rb
#	app/models/ci/runner.rb
#	app/views/ci/variables/_variable_row.html.haml
#	app/views/import/github/new.html.haml
#	app/views/layouts/nav/sidebar/_project.html.haml
#	app/views/projects/new.html.haml
#	app/views/shared/_import_form.html.haml
#	doc/install/README.md
#	spec/features/groups/activity_spec.rb
#	spec/features/groups/issues_spec.rb
#	spec/features/groups/show_spec.rb
#	spec/javascripts/fixtures/projects.rb
#	spec/services/ci/retry_build_service_spec.rb

[ci skip]
parents 31eb5747 22501582
...@@ -16,8 +16,11 @@ lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb ...@@ -16,8 +16,11 @@ lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb
lib/gitlab/background_migration/* lib/gitlab/background_migration/*
app/models/project_services/kubernetes_service.rb app/models/project_services/kubernetes_service.rb
lib/gitlab/workhorse.rb lib/gitlab/workhorse.rb
<<<<<<< HEAD
ee/db/**/* ee/db/**/*
ee/app/serializers/ee/merge_request_widget_entity.rb ee/app/serializers/ee/merge_request_widget_entity.rb
ee/lib/ee/gitlab/ldap/sync/admin_users.rb ee/lib/ee/gitlab/ldap/sync/admin_users.rb
ee/spec/**/* ee/spec/**/*
=======
>>>>>>> upstream/master
...@@ -4,8 +4,14 @@ entry. ...@@ -4,8 +4,14 @@ entry.
## 10.6.2 (2018-03-29) ## 10.6.2 (2018-03-29)
<<<<<<< HEAD
### Fixed (1 change, 1 of them is from the community) ### Fixed (1 change, 1 of them is from the community)
=======
### Fixed (2 changes, 1 of them is from the community)
- Don't capture trailing punctuation when autolinking. !17965
>>>>>>> upstream/master
- Cloning a repository over HTTPS with LDAP credentials causes a HTTP 401 Access denied. (Horatiu Eugen Vlad) - Cloning a repository over HTTPS with LDAP credentials causes a HTTP 401 Access denied. (Horatiu Eugen Vlad)
......
...@@ -28,7 +28,7 @@ gem 'default_value_for', gem_versions['default_value_for'] ...@@ -28,7 +28,7 @@ gem 'default_value_for', gem_versions['default_value_for']
gem 'mysql2', '~> 0.4.10', group: :mysql gem 'mysql2', '~> 0.4.10', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres gem 'pg', '~> 0.18.2', group: :postgres
gem 'rugged', '~> 0.26.0' gem 'rugged', '~> 0.27'
gem 'grape-route-helpers', '~> 2.1.0' gem 'grape-route-helpers', '~> 2.1.0'
gem 'faraday', '~> 0.12' gem 'faraday', '~> 0.12'
...@@ -44,7 +44,7 @@ gem 'omniauth-cas3', '~> 1.1.4' ...@@ -44,7 +44,7 @@ gem 'omniauth-cas3', '~> 1.1.4'
gem 'omniauth-facebook', '~> 4.0.0' gem 'omniauth-facebook', '~> 4.0.0'
gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.2' gem 'omniauth-gitlab', '~> 1.0.2'
gem 'omniauth-google-oauth2', '~> 0.5.2' gem 'omniauth-google-oauth2', '~> 0.5.3'
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-oauth2-generic', '~> 0.2.2' gem 'omniauth-oauth2-generic', '~> 0.2.2'
gem 'omniauth-saml', '~> 1.10' gem 'omniauth-saml', '~> 1.10'
...@@ -388,6 +388,8 @@ group :development, :test do ...@@ -388,6 +388,8 @@ group :development, :test do
gem 'stackprof', '~> 0.2.10', require: false gem 'stackprof', '~> 0.2.10', require: false
gem 'simple_po_parser', '~> 1.1.2', require: false gem 'simple_po_parser', '~> 1.1.2', require: false
gem 'timecop', '~> 0.8.0'
end end
group :test do group :test do
...@@ -397,7 +399,6 @@ group :test do ...@@ -397,7 +399,6 @@ group :test do
gem 'webmock', '~> 2.3.2' gem 'webmock', '~> 2.3.2'
gem 'test_after_commit', '~> 1.1' gem 'test_after_commit', '~> 1.1'
gem 'sham_rack', '~> 1.3.6' gem 'sham_rack', '~> 1.3.6'
gem 'timecop', '~> 0.8.0'
gem 'concurrent-ruby', '~> 1.0.5' gem 'concurrent-ruby', '~> 1.0.5'
gem 'test-prof', '~> 0.2.5' gem 'test-prof', '~> 0.2.5'
end end
......
...@@ -579,11 +579,10 @@ GEM ...@@ -579,11 +579,10 @@ GEM
omniauth-gitlab (1.0.2) omniauth-gitlab (1.0.2)
omniauth (~> 1.0) omniauth (~> 1.0)
omniauth-oauth2 (~> 1.0) omniauth-oauth2 (~> 1.0)
omniauth-google-oauth2 (0.5.2) omniauth-google-oauth2 (0.5.3)
jwt (~> 1.5) jwt (>= 1.5)
multi_json (~> 1.3)
omniauth (>= 1.1.1) omniauth (>= 1.1.1)
omniauth-oauth2 (>= 1.3.1) omniauth-oauth2 (>= 1.5)
omniauth-jwt (0.0.2) omniauth-jwt (0.0.2)
jwt jwt
omniauth (~> 1.1) omniauth (~> 1.1)
...@@ -595,8 +594,8 @@ GEM ...@@ -595,8 +594,8 @@ GEM
omniauth-oauth (1.1.0) omniauth-oauth (1.1.0)
oauth oauth
omniauth (~> 1.0) omniauth (~> 1.0)
omniauth-oauth2 (1.4.0) omniauth-oauth2 (1.5.0)
oauth2 (~> 1.0) oauth2 (~> 1.1)
omniauth (~> 1.2) omniauth (~> 1.2)
omniauth-oauth2-generic (0.2.2) omniauth-oauth2-generic (0.2.2)
omniauth-oauth2 (~> 1.0) omniauth-oauth2 (~> 1.0)
...@@ -843,7 +842,7 @@ GEM ...@@ -843,7 +842,7 @@ GEM
rubyzip (1.2.1) rubyzip (1.2.1)
rufus-scheduler (3.4.0) rufus-scheduler (3.4.0)
et-orbi (~> 1.0) et-orbi (~> 1.0)
rugged (0.26.0) rugged (0.27.0)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
...@@ -1155,7 +1154,7 @@ DEPENDENCIES ...@@ -1155,7 +1154,7 @@ DEPENDENCIES
omniauth-facebook (~> 4.0.0) omniauth-facebook (~> 4.0.0)
omniauth-github (~> 1.1.1) omniauth-github (~> 1.1.1)
omniauth-gitlab (~> 1.0.2) omniauth-gitlab (~> 1.0.2)
omniauth-google-oauth2 (~> 0.5.2) omniauth-google-oauth2 (~> 0.5.3)
omniauth-jwt (~> 0.0.2) omniauth-jwt (~> 0.0.2)
omniauth-kerberos (~> 0.3.0) omniauth-kerberos (~> 0.3.0)
omniauth-oauth2-generic (~> 0.2.2) omniauth-oauth2-generic (~> 0.2.2)
...@@ -1211,7 +1210,7 @@ DEPENDENCIES ...@@ -1211,7 +1210,7 @@ DEPENDENCIES
ruby-prof (~> 0.17.0) ruby-prof (~> 0.17.0)
ruby_parser (~> 3.8) ruby_parser (~> 3.8)
rufus-scheduler (~> 3.4) rufus-scheduler (~> 3.4)
rugged (~> 0.26.0) rugged (~> 0.27)
sanitize (~> 2.0) sanitize (~> 2.0)
sass-rails (~> 5.0.6) sass-rails (~> 5.0.6)
scss_lint (~> 0.56.0) scss_lint (~> 0.56.0)
......
...@@ -31,7 +31,7 @@ export default function renderMath($els) { ...@@ -31,7 +31,7 @@ export default function renderMath($els) {
if (!$els.length) return; if (!$els.length) return;
Promise.all([ Promise.all([
import(/* webpackChunkName: 'katex' */ 'katex'), import(/* webpackChunkName: 'katex' */ 'katex'),
import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.css'), import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.min.css'),
]).then(([katex]) => { ]).then(([katex]) => {
renderWithKaTeX($els, katex); renderWithKaTeX($els, katex);
}).catch(() => flash(__('An error occurred while rendering KaTeX'))); }).catch(() => flash(__('An error occurred while rendering KaTeX')));
......
...@@ -292,10 +292,12 @@ Please check your network connection and try again.`; ...@@ -292,10 +292,12 @@ Please check your network connection and try again.`;
</button> </button>
</div> </div>
<div <div
v-if="note.resolvable"
class="btn-group discussion-actions" class="btn-group discussion-actions"
role="group"> role="group"
>
<div <div
v-if="note.resolvable && !discussionResolved" v-if="!discussionResolved"
class="btn-group" class="btn-group"
role="group"> role="group">
<a <a
......
...@@ -15,6 +15,7 @@ export default { ...@@ -15,6 +15,7 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
<<<<<<< HEAD
url: { url: {
type: String, type: String,
required: true, required: true,
...@@ -22,6 +23,31 @@ export default { ...@@ -22,6 +23,31 @@ export default {
groupName: { groupName: {
type: String, type: String,
required: true, required: true,
=======
props: {
milestoneTitle: {
type: String,
required: true,
},
url: {
type: String,
required: true,
},
groupName: {
type: String,
required: true,
},
},
computed: {
title() {
return sprintf(s__('Milestones|Promote %{milestoneTitle} to group milestone?'), { milestoneTitle: this.milestoneTitle });
},
text() {
return sprintf(s__(`Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}.
Existing project milestones with the same title will be merged.
This action cannot be reversed.`), { milestoneTitle: this.milestoneTitle, groupName: this.groupName });
},
>>>>>>> upstream/master
}, },
}, },
computed: { computed: {
......
...@@ -199,6 +199,10 @@ ...@@ -199,6 +199,10 @@
.branch-header-title { .branch-header-title {
color: $color-700; color: $color-700;
} }
.ide-file-list .file.file-active {
color: $color-700;
}
} }
body { body {
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
display: flex; display: flex;
height: calc(100vh - #{$header-height}); height: calc(100vh - #{$header-height});
margin-top: 40px; margin-top: 40px;
color: $almost-black;
border-top: 1px solid $white-dark; border-top: 1px solid $white-dark;
border-bottom: 1px solid $white-dark; border-bottom: 1px solid $white-dark;
...@@ -43,7 +42,11 @@ ...@@ -43,7 +42,11 @@
cursor: pointer; cursor: pointer;
&.file-open { &.file-open {
background: $white-normal; background: $link-active-background;
}
&.file-active {
font-weight: $gl-font-weight-bold;
} }
.ide-file-name { .ide-file-name {
...@@ -72,7 +75,10 @@ ...@@ -72,7 +75,10 @@
margin-right: -8px; margin-right: -8px;
} }
&:hover { &:hover,
&:focus {
background: $link-active-background;
.ide-new-btn { .ide-new-btn {
display: block; display: block;
} }
...@@ -720,9 +726,7 @@ ...@@ -720,9 +726,7 @@
} }
.ide-view { .ide-view {
height: calc( height: calc(100vh - #{$header-height + $performance-bar-height + $flash-height});
100vh - #{$header-height + $performance-bar-height + $flash-height}
);
} }
} }
} }
......
...@@ -21,17 +21,13 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -21,17 +21,13 @@ class Projects::BranchesController < Projects::ApplicationController
fetch_branches_by_mode fetch_branches_by_mode
@refs_pipelines = @project.pipelines.latest_successful_for_refs(@branches.map(&:name)) @refs_pipelines = @project.pipelines.latest_successful_for_refs(@branches.map(&:name))
@merged_branch_names = @merged_branch_names = repository.merged_branch_names(@branches.map(&:name))
repository.merged_branch_names(@branches.map(&:name)) @max_commits = @branches.reduce(0) do |memo, branch|
# n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37429 diverging_commit_counts = repository.diverging_commit_counts(branch)
Gitlab::GitalyClient.allow_n_plus_1_calls do [memo, diverging_commit_counts[:behind], diverging_commit_counts[:ahead]].max
@max_commits = @branches.reduce(0) do |memo, branch|
diverging_commit_counts = repository.diverging_commit_counts(branch)
[memo, diverging_commit_counts[:behind], diverging_commit_counts[:ahead]].max
end
render
end end
render
end end
format.json do format.json do
branches = BranchesFinder.new(@repository, params).execute branches = BranchesFinder.new(@repository, params).execute
......
...@@ -43,9 +43,14 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -43,9 +43,14 @@ class Projects::MilestonesController < Projects::ApplicationController
def show def show
@project_namespace = @project.namespace.becomes(Namespace) @project_namespace = @project.namespace.becomes(Namespace)
<<<<<<< HEAD
if @project.feature_available?(:burndown_charts, current_user) && if @project.feature_available?(:burndown_charts, current_user) &&
@project.feature_available?(:issue_weights, current_user) @project.feature_available?(:issue_weights, current_user)
@burndown = Burndown.new(@milestone) @burndown = Burndown.new(@milestone)
=======
respond_to do |format|
format.html
>>>>>>> upstream/master
end end
end end
......
...@@ -33,7 +33,7 @@ module NamespacesHelper ...@@ -33,7 +33,7 @@ module NamespacesHelper
def namespace_icon(namespace, size = 40) def namespace_icon(namespace, size = 40)
if namespace.is_a?(Group) if namespace.is_a?(Group)
group_icon(namespace) group_icon_url(namespace)
else else
avatar_icon_for_user(namespace.owner, size) avatar_icon_for_user(namespace.owner, size)
end end
......
...@@ -40,7 +40,11 @@ module PageLayoutHelper ...@@ -40,7 +40,11 @@ module PageLayoutHelper
def favicon def favicon
return 'favicon-yellow.ico' if Gitlab::Utils.to_boolean(ENV['CANARY']) return 'favicon-yellow.ico' if Gitlab::Utils.to_boolean(ENV['CANARY'])
<<<<<<< HEAD
return 'favicon-green.ico' if Rails.env.development? return 'favicon-green.ico' if Rails.env.development?
=======
return 'favicon-blue.ico' if Rails.env.development?
>>>>>>> upstream/master
'favicon.ico' 'favicon.ico'
end end
......
...@@ -4,7 +4,10 @@ module Ci ...@@ -4,7 +4,10 @@ module Ci
include Gitlab::SQL::Pattern include Gitlab::SQL::Pattern
include RedisCacheable include RedisCacheable
include ChronicDurationAttribute include ChronicDurationAttribute
<<<<<<< HEAD
prepend EE::Ci::Runner prepend EE::Ci::Runner
=======
>>>>>>> upstream/master
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
ONLINE_CONTACT_TIMEOUT = 1.hour ONLINE_CONTACT_TIMEOUT = 1.hour
......
...@@ -27,6 +27,10 @@ class DeployKey < Key ...@@ -27,6 +27,10 @@ class DeployKey < Key
self.private? self.private?
end end
def user
super || User.ghost
end
def has_access_to?(project) def has_access_to?(project)
deploy_keys_project_for(project).present? deploy_keys_project_for(project).present?
end end
......
...@@ -17,32 +17,4 @@ class RedirectRoute < ActiveRecord::Base ...@@ -17,32 +17,4 @@ class RedirectRoute < ActiveRecord::Base
where(wheres, path, "#{sanitize_sql_like(path)}/%") where(wheres, path, "#{sanitize_sql_like(path)}/%")
end end
scope :permanent, -> do
if column_permanent_exists?
where(permanent: true)
else
none
end
end
scope :temporary, -> do
if column_permanent_exists?
where(permanent: [false, nil])
else
all
end
end
default_value_for :permanent, false
def permanent=(value)
if self.class.column_permanent_exists?
super
end
end
def self.column_permanent_exists?
ActiveRecord::Base.connection.column_exists?(:redirect_routes, :permanent)
end
end end
...@@ -256,13 +256,13 @@ class Repository ...@@ -256,13 +256,13 @@ class Repository
end end
def diverging_commit_counts(branch) def diverging_commit_counts(branch)
root_ref_hash = raw_repository.commit(root_ref).id @root_ref_hash ||= raw_repository.commit(root_ref).id
cache.fetch(:"diverging_commit_counts_#{branch.name}") do cache.fetch(:"diverging_commit_counts_#{branch.name}") do
# Rugged seems to throw a `ReferenceError` when given branch_names rather # Rugged seems to throw a `ReferenceError` when given branch_names rather
# than SHA-1 hashes # than SHA-1 hashes
number_commits_behind, number_commits_ahead = number_commits_behind, number_commits_ahead =
raw_repository.count_commits_between( raw_repository.count_commits_between(
root_ref_hash, @root_ref_hash,
branch.dereferenced_target.sha, branch.dereferenced_target.sha,
left_right: true, left_right: true,
max_count: MAX_DIVERGING_COUNT) max_count: MAX_DIVERGING_COUNT)
......
...@@ -10,8 +10,6 @@ class Route < ActiveRecord::Base ...@@ -10,8 +10,6 @@ class Route < ActiveRecord::Base
presence: true, presence: true,
uniqueness: { case_sensitive: false } uniqueness: { case_sensitive: false }
validate :ensure_permanent_paths, if: :path_changed?
before_validation :delete_conflicting_orphaned_routes before_validation :delete_conflicting_orphaned_routes
after_create :delete_conflicting_redirects after_create :delete_conflicting_redirects
after_update :delete_conflicting_redirects, if: :path_changed? after_update :delete_conflicting_redirects, if: :path_changed?
...@@ -45,7 +43,7 @@ class Route < ActiveRecord::Base ...@@ -45,7 +43,7 @@ class Route < ActiveRecord::Base
# We are not calling route.delete_conflicting_redirects here, in hopes # We are not calling route.delete_conflicting_redirects here, in hopes
# of avoiding deadlocks. The parent (self, in this method) already # of avoiding deadlocks. The parent (self, in this method) already
# called it, which deletes conflicts for all descendants. # called it, which deletes conflicts for all descendants.
route.create_redirect(old_path, permanent: permanent_redirect?) if attributes[:path] route.create_redirect(old_path) if attributes[:path]
end end
end end
end end
...@@ -55,31 +53,17 @@ class Route < ActiveRecord::Base ...@@ -55,31 +53,17 @@ class Route < ActiveRecord::Base
end end
def conflicting_redirects def conflicting_redirects
RedirectRoute.temporary.matching_path_and_descendants(path) RedirectRoute.matching_path_and_descendants(path)
end end
def create_redirect(path, permanent: false) def create_redirect(path)
RedirectRoute.create(source: source, path: path, permanent: permanent) RedirectRoute.create(source: source, path: path)
end end
private private
def create_redirect_for_old_path def create_redirect_for_old_path
create_redirect(path_was, permanent: permanent_redirect?) if path_changed? create_redirect(path_was) if path_changed?
end
def permanent_redirect?
source_type != "Project"
end
def ensure_permanent_paths
return if path.nil?
errors.add(:path, "has been taken before") if conflicting_redirect_exists?
end
def conflicting_redirect_exists?
RedirectRoute.permanent.matching_path_and_descendants(path).exists?
end end
def delete_conflicting_orphaned_routes def delete_conflicting_orphaned_routes
......
...@@ -278,6 +278,7 @@ class Service < ActiveRecord::Base ...@@ -278,6 +278,7 @@ class Service < ActiveRecord::Base
def self.build_from_template(project_id, template) def self.build_from_template(project_id, template)
service = template.dup service = template.dup
service.active = false unless service.valid?
service.template = false service.template = false
service.project_id = project_id service.project_id = project_id
service service
......
...@@ -84,11 +84,8 @@ class User < ActiveRecord::Base ...@@ -84,11 +84,8 @@ class User < ActiveRecord::Base
has_one :namespace, -> { where(type: nil) }, dependent: :destroy, foreign_key: :owner_id, inverse_of: :owner, autosave: true # rubocop:disable Cop/ActiveRecordDependent has_one :namespace, -> { where(type: nil) }, dependent: :destroy, foreign_key: :owner_id, inverse_of: :owner, autosave: true # rubocop:disable Cop/ActiveRecordDependent
# Profile # Profile
has_many :keys, -> do has_many :keys, -> { where(type: ['Key', nil]) }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
type = Key.arel_table[:type] has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :nullify # rubocop:disable Cop/ActiveRecordDependent
where(type.not_eq('DeployKey').or(type.eq(nil)))
end, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :gpg_keys has_many :gpg_keys
has_many :emails, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :emails, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
......
...@@ -92,9 +92,6 @@ module Projects ...@@ -92,9 +92,6 @@ module Projects
unless @project.gitlab_project_import? unless @project.gitlab_project_import?
@project.write_repository_config @project.write_repository_config
@project.create_wiki unless skip_wiki? @project.create_wiki unless skip_wiki?
create_services_from_active_templates(@project)
@project.create_labels
end end
event_service.create_project(@project, current_user) event_service.create_project(@project, current_user)
...@@ -123,21 +120,29 @@ module Projects ...@@ -123,21 +120,29 @@ module Projects
Project.transaction do Project.transaction do
@project.create_or_update_import_data(data: import_data[:data], credentials: import_data[:credentials]) if import_data @project.create_or_update_import_data(data: import_data[:data], credentials: import_data[:credentials]) if import_data
if @project.save && !@project.import? if @project.save
raise 'Failed to create repository' unless @project.create_repository unless @project.gitlab_project_import?
create_services_from_active_templates(@project)
@project.create_labels
end
unless @project.import?
raise 'Failed to create repository' unless @project.create_repository
end
end end
end end
end end
def fail(error:) def fail(error:)
message = "Unable to save project. Error: #{error}" message = "Unable to save project. Error: #{error}"
message << "Project ID: #{@project.id}" if @project && @project.id log_message = message.dup
Rails.logger.error(message) log_message << " Project ID: #{@project.id}" if @project&.id
Rails.logger.error(log_message)
if @project && @project.import? if @project
@project.errors.add(:base, message) @project.errors.add(:base, message)
@project.mark_import_as_failed(message) @project.mark_import_as_failed(message) if @project.import?
end end
@project @project
......
...@@ -43,8 +43,11 @@ ...@@ -43,8 +43,11 @@
%span.toggle-icon %span.toggle-icon
= sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked') = sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
= sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked') = sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
<<<<<<< HEAD
-# EE-specific start -# EE-specific start
= render 'ci/variables/environment_scope', form_field: form_field, variable: variable = render 'ci/variables/environment_scope', form_field: form_field, variable: variable
-# EE-specific end -# EE-specific end
=======
>>>>>>> upstream/master
%button.js-row-remove-button.ci-variable-row-remove-button{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') } %button.js-row-remove-button.ci-variable-row-remove-button{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
= icon('minus-circle') = icon('minus-circle')
...@@ -22,10 +22,13 @@ ...@@ -22,10 +22,13 @@
= text_field_tag :personal_access_token, '', class: 'form-control', placeholder: _('Personal Access Token'), size: 40 = text_field_tag :personal_access_token, '', class: 'form-control', placeholder: _('Personal Access Token'), size: 40
= submit_tag _('List your GitHub repositories'), class: 'btn btn-success' = submit_tag _('List your GitHub repositories'), class: 'btn btn-success'
<<<<<<< HEAD
-# EE-specific start -# EE-specific start
= hidden_field_tag :ci_cd_only, params[:ci_cd_only] = hidden_field_tag :ci_cd_only, params[:ci_cd_only]
-# EE-specific end -# EE-specific end
=======
>>>>>>> upstream/master
- unless github_import_configured? - unless github_import_configured?
%hr %hr
%p %p
......
...@@ -80,11 +80,14 @@ ...@@ -80,11 +80,14 @@
= link_to charts_project_graph_path(@project, current_ref) do = link_to charts_project_graph_path(@project, current_ref) do
#{ _('Charts') } #{ _('Charts') }
<<<<<<< HEAD
- if @project.feature_available?(:file_locks) - if @project.feature_available?(:file_locks)
= nav_link(controller: [:path_locks]) do = nav_link(controller: [:path_locks]) do
= link_to project_path_locks_path(@project) do = link_to project_path_locks_path(@project) do
#{ _('Locked Files') } #{ _('Locked Files') }
=======
>>>>>>> upstream/master
- if project_nav_tab? :issues - if project_nav_tab? :issues
= nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do = nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do
= link_to project_issues_path(@project), class: 'shortcuts-issues' do = link_to project_issues_path(@project), class: 'shortcuts-issues' do
...@@ -239,11 +242,14 @@ ...@@ -239,11 +242,14 @@
= sprite_icon('disk') = sprite_icon('disk')
%span.nav-item-name %span.nav-item-name
Registry Registry
<<<<<<< HEAD
%ul.sidebar-sub-level-items.is-fly-out-only %ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: %w[projects/registry/repositories], html_options: { class: "fly-out-top-item" } ) do = nav_link(controller: %w[projects/registry/repositories], html_options: { class: "fly-out-top-item" } ) do
= link_to project_container_registry_index_path(@project) do = link_to project_container_registry_index_path(@project) do
%strong.fly-out-top-item-name %strong.fly-out-top-item-name
#{ _('Registry') } #{ _('Registry') }
=======
>>>>>>> upstream/master
- if project_nav_tab? :wiki - if project_nav_tab? :wiki
= nav_link(controller: :wikis) do = nav_link(controller: :wikis) do
......
...@@ -18,11 +18,14 @@ ...@@ -18,11 +18,14 @@
= _('A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}.').html_safe % { among_other_things_link: among_other_things_link } = _('A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}.').html_safe % { among_other_things_link: among_other_things_link }
%p %p
= _('All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings.') = _('All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings.')
<<<<<<< HEAD
-# EE-specific start -# EE-specific start
- if ci_cd_projects_available? - if ci_cd_projects_available?
%p %p
= _('To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>.').html_safe = _('To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>.').html_safe
-# EE-specific end -# EE-specific end
=======
>>>>>>> upstream/master
.md .md
= brand_new_project_guidelines = brand_new_project_guidelines
%p %p
...@@ -46,9 +49,12 @@ ...@@ -46,9 +49,12 @@
%a{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab' }, role: 'tab' } %a{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab' }, role: 'tab' }
%span.hidden-xs Import project %span.hidden-xs Import project
%span.visible-xs Import %span.visible-xs Import
<<<<<<< HEAD
-# EE-specific start -# EE-specific start
= render 'new_ci_cd_only_project_tab', active_tab: active_tab = render 'new_ci_cd_only_project_tab', active_tab: active_tab
-# EE-specific end -# EE-specific end
=======
>>>>>>> upstream/master
.tab-content.gitlab-tab-content .tab-content.gitlab-tab-content
.tab-pane{ id: 'blank-project-pane', class: active_when(active_tab == 'blank'), role: 'tabpanel' } .tab-pane{ id: 'blank-project-pane', class: active_when(active_tab == 'blank'), role: 'tabpanel' }
...@@ -114,11 +120,14 @@ ...@@ -114,11 +120,14 @@
= render "shared/import_form", f: f = render "shared/import_form", f: f
= render 'new_project_fields', f: f, project_name_id: "import-url-name" = render 'new_project_fields', f: f, project_name_id: "import-url-name"
<<<<<<< HEAD
-# EE-specific start -# EE-specific start
= render 'new_ci_cd_only_project_pane', active_tab: active_tab = render 'new_ci_cd_only_project_pane', active_tab: active_tab
-# EE-specific end -# EE-specific end
=======
>>>>>>> upstream/master
.save-project-loader.hide .save-project-loader.hide
.center .center
%h2 %h2
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
= import_will_timeout_message(ci_cd_only) = import_will_timeout_message(ci_cd_only)
%li %li
= import_svn_message(ci_cd_only) = import_svn_message(ci_cd_only)
<<<<<<< HEAD
%li %li
The Git LFS objects will be ignored. The Git LFS objects will be ignored.
- unless ci_cd_only - unless ci_cd_only
...@@ -27,3 +28,5 @@ ...@@ -27,3 +28,5 @@
-# EE-specific start -# EE-specific start
= render 'shared/ee/import_form', f: f unless ci_cd_only = render 'shared/ee/import_form', f: f unless ci_cd_only
-# EE-specific end -# EE-specific end
=======
>>>>>>> upstream/master
---
title: Drop JSON response in Project Milestone along with avoiding error
merge_request: 17977
author: Takuya Noguchi
type: fixed
---
title: Project creation will now raise an error if a service template is invalid
merge_request: 18013
author:
type: fixed
---
title: Fix bug rendering group icons when forking
merge_request:
author:
type: fixed
---
title: Fix autolinking URLs containing ampersands
merge_request: 18045
author:
type: fixed
---
title: Reuse root_ref_hash for performance on Branches
merge_request: 17998
author: Takuya Noguchi
type: performance
---
title: Don't show Jump to Discussion button on Issues
merge_request:
author:
type: fixed
---
title: Don't create permanent redirect routes
merge_request: 17521
author:
type: changed
---
title: Ensure hooks run when a deploy key without a user pushes
merge_request:
author:
type: fixed
---
title: Allow merge requests related to a commit to be found via API
merge_request:
author:
type: added
---
title: Fix exceptions raised when migrating pipeline stages in the background
merge_request: 18076
author:
type: fixed
---
title: Added hover background color to IDE file list rows
merge_request:
author:
type: changed
...@@ -111,7 +111,7 @@ const config = { ...@@ -111,7 +111,7 @@ const config = {
}, },
}, },
{ {
test: /katex.css$/, test: /katex.min.css$/,
include: /node_modules\/katex\/dist/, include: /node_modules\/katex\/dist/,
use: [ use: [
{ loader: 'style-loader' }, { loader: 'style-loader' },
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RemovePermanentFromRedirectRoutes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
INDEX_NAME_PERM = "index_redirect_routes_on_path_text_pattern_ops_where_permanent"
INDEX_NAME_TEMP = "index_redirect_routes_on_path_text_pattern_ops_where_temporary"
def up
# These indexes were created on Postgres only in:
# ReworkRedirectRoutesIndexes:
# https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/16211
if Gitlab::Database.postgresql?
disable_statement_timeout
execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_PERM};"
execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_TEMP};"
end
remove_column(:redirect_routes, :permanent)
end
def down
add_column(:redirect_routes, :permanent, :boolean)
if Gitlab::Database.postgresql?
disable_statement_timeout
execute("CREATE INDEX CONCURRENTLY #{INDEX_NAME_PERM} ON redirect_routes (lower(path) varchar_pattern_ops) where (permanent);")
execute("CREATE INDEX CONCURRENTLY #{INDEX_NAME_TEMP} ON redirect_routes (lower(path) varchar_pattern_ops) where (not permanent or permanent is null) ;")
end
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddPathIndexToRedirectRoutes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
INDEX_NAME = 'index_redirect_routes_on_path_unique_text_pattern_ops'
# Indexing on LOWER(path) varchar_pattern_ops speeds up the LIKE query in
# RedirectRoute.matching_path_and_descendants
#
# This same index is also added in the `ReworkRedirectRoutesIndexes` so this
# is a no-op in most cases. But this migration is also called from the
# `setup_postgresql.rake` task when setting up a new database, in which case
# we want to create the index.
def up
return unless Gitlab::Database.postgresql?
disable_statement_timeout
unless index_exists_by_name?(:redirect_routes, INDEX_NAME)
execute("CREATE UNIQUE INDEX CONCURRENTLY #{INDEX_NAME} ON redirect_routes (lower(path) varchar_pattern_ops);")
end
end
def down
# Do nothing in the DOWN. Since the index above is originally created in the
# `ReworkRedirectRoutesIndexes`. This migration wouldn't have actually
# created any new index.
#
# This migration is only here to be called form `setup_postgresql.rake` so
# any newly created database would have this index.
end
end
...@@ -2143,7 +2143,6 @@ ActiveRecord::Schema.define(version: 20180327101207) do ...@@ -2143,7 +2143,6 @@ ActiveRecord::Schema.define(version: 20180327101207) do
t.string "path", null: false t.string "path", null: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.boolean "permanent"
end end
add_index "redirect_routes", ["path"], name: "index_redirect_routes_on_path", unique: true, using: :btree add_index "redirect_routes", ["path"], name: "index_redirect_routes_on_path", unique: true, using: :btree
......
...@@ -536,6 +536,74 @@ Example response: ...@@ -536,6 +536,74 @@ Example response:
} }
``` ```
## List Merge Requests associated with a commit
Get a list of Merge Requests related to the specified commit.
```
GET /projects/:id/repository/commits/:sha/merge_requests
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
| `sha` | string | yes | The commit SHA
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/commits/af5b13261899fb2c0db30abdd0af8b07cb44fdc5/merge_requests"
```
Example response:
```json
[
{
"id":45,
"iid":1,
"project_id":35,
"title":"Add new file",
"description":"",
"state":"opened",
"created_at":"2018-03-26T17:26:30.916Z",
"updated_at":"2018-03-26T17:26:30.916Z",
"target_branch":"master",
"source_branch":"test-branch",
"upvotes":0,
"downvotes":0,
"author" : {
"web_url" : "https://gitlab.example.com/thedude",
"name" : "Jeff Lebowski",
"avatar_url" : "https://gitlab.example.com/uploads/user/avatar/28/The-Big-Lebowski-400-400.png",
"username" : "thedude",
"state" : "active",
"id" : 28
},
"assignee":null,
"source_project_id":35,
"target_project_id":35,
"labels":[ ],
"work_in_progress":false,
"milestone":null,
"merge_when_pipeline_succeeds":false,
"merge_status":"can_be_merged",
"sha":"af5b13261899fb2c0db30abdd0af8b07cb44fdc5",
"merge_commit_sha":null,
"user_notes_count":0,
"discussion_locked":null,
"should_remove_source_branch":null,
"force_remove_source_branch":false,
"web_url":"http://https://gitlab.example.com/root/test-project/merge_requests/1",
"time_stats":{
"time_estimate":0,
"total_time_spent":0,
"human_time_estimate":null,
"human_total_time_spent":null
}
}
]
```
[ce-6096]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6096 "Multi-file commit" [ce-6096]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6096 "Multi-file commit"
[ce-8047]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8047 [ce-8047]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8047
[ce-15026]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15026 [ce-15026]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15026
...@@ -42,6 +42,10 @@ Dates for the `before` and `after` parameters should be supplied in the followin ...@@ -42,6 +42,10 @@ Dates for the `before` and `after` parameters should be supplied in the followin
YYYY-MM-DD YYYY-MM-DD
``` ```
### Event Time Period Limit
GitLab removes events older than 1 year from the events table for performance reasons. The range of 1 year was chosen because user contribution calendars only show contributions of the past year.
## List currently authenticated user's events ## List currently authenticated user's events
>**Note:** This endpoint was introduced in GitLab 9.3. >**Note:** This endpoint was introduced in GitLab 9.3.
......
...@@ -15,13 +15,8 @@ codequality: ...@@ -15,13 +15,8 @@ codequality:
services: services:
- docker:dind - docker:dind
script: script:
- docker pull codeclimate/codeclimate
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- docker run - docker run --env SOURCE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
--env SOURCE_CODE="$PWD" \
--volume "$PWD":/code \
--volume /var/run/docker.sock:/var/run/docker.sock \
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
artifacts: artifacts:
paths: [codeclimate.json] paths: [codeclimate.json]
``` ```
......
...@@ -111,7 +111,7 @@ We also use two secure variables: ...@@ -111,7 +111,7 @@ We also use two secure variables:
## Storing API keys ## Storing API keys
Secure Variables can added by going to your project's Secure Variables can added by going to your project's
**Settings ➔ Pipelines ➔ Secret variables**. The variables that are defined **Settings ➔ CI / CD ➔ Secret variables**. The variables that are defined
in the project settings are sent along with the build script to the Runner. in the project settings are sent along with the build script to the Runner.
The secure variables are stored out of the repository. Never store secrets in The secure variables are stored out of the repository. Never store secrets in
your project's `.gitlab-ci.yml`. It is also important that the secret's value your project's `.gitlab-ci.yml`. It is also important that the secret's value
......
...@@ -370,6 +370,255 @@ class beneath the `EE` module just as you would normally. ...@@ -370,6 +370,255 @@ class beneath the `EE` module just as you would normally.
For example, if CE has LDAP classes in `lib/gitlab/ldap/` then you would place For example, if CE has LDAP classes in `lib/gitlab/ldap/` then you would place
EE-specific LDAP classes in `ee/lib/ee/gitlab/ldap`. EE-specific LDAP classes in `ee/lib/ee/gitlab/ldap`.
### Code in `lib/api/`
It can be very tricky to extend EE features by a single line of `prepend`,
and for each different [Grape](https://github.com/ruby-grape/grape) feature,
we might need different strategies to extend it. To apply different strategies
easily, we would use `extend ActiveSupport::Concern` in the EE module.
Put the EE module files following
[EE features based on CE features](#ee-features-based-on-ce-features).
#### EE API routes
For EE API routes, we put them in a `prepended` block:
``` ruby
module EE
module API
module MergeRequests
extend ActiveSupport::Concern
prepended do
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects, requirements: ::API::API::PROJECT_ENDPOINT_REQUIREMENTS do
# ...
end
end
end
end
end
```
Note that due to namespace differences, we need to use the full qualifier for some
constants.
#### EE params
We can define `params` and utilize `use` in another `params` definition to
include params defined in EE. However, we need to define the "interface" first
in CE in order for EE to override it. We don't have to do this in other places
due to `prepend`, but Grape is complex internally and we couldn't easily do
that, so we'll follow regular object-oriented practices that we define the
interface first here.
For example, suppose we have a few more optional params for EE, given this CE
API code:
``` ruby
module API
class MergeRequests < Grape::API
# EE::API::MergeRequests would override the following helpers
helpers do
params :optional_params_ee do
end
end
prepend EE::API::MergeRequests
params :optional_params do
# CE specific params go here...
use :optional_params_ee
end
end
end
```
And then we could override it in EE module:
``` ruby
module EE
module API
module MergeRequests
extend ActiveSupport::Concern
prepended do
helpers do
params :optional_params_ee do
# EE specific params go here...
end
end
end
end
end
end
```
This way, the only difference between CE and EE for that API file would be
`prepend EE::API::MergeRequests`.
#### EE helpers
To make it easy for an EE module to override the CE helpers, we need to define
those helpers we want to extend first. Try to do that immediately after the
class definition to make it easy and clear:
``` ruby
module API
class JobArtifacts < Grape::API
# EE::API::JobArtifacts would override the following helpers
helpers do
def authorize_download_artifacts!
authorize_read_builds!
end
end
prepend EE::API::JobArtifacts
end
end
```
And then we can follow regular object-oriented practices to override it:
``` ruby
module EE
module API
module JobArtifacts
extend ActiveSupport::Concern
prepended do
helpers do
def authorize_download_artifacts!
super
check_cross_project_pipelines_feature!
end
end
end
end
end
end
```
#### EE-specific behaviour
Sometimes we need EE-specific behaviour in some of the APIs. Normally we could
use EE methods to override CE methods, however API routes are not methods and
therefore can't be simply overridden. We need to extract them into a standalone
method, or introduce some "hooks" where we could inject behavior in the CE
route. Something like this:
``` ruby
module API
class MergeRequests < Grape::API
helpers do
# EE::API::MergeRequests would override the following helpers
def update_merge_request_ee(merge_request)
end
end
prepend EE::API::MergeRequests
put ':id/merge_requests/:merge_request_iid/merge' do
merge_request = find_project_merge_request(params[:merge_request_iid])
# ...
update_merge_request_ee(merge_request)
# ...
end
end
end
```
Note that `update_merge_request_ee` doesn't do anything in CE, but
then we could override it in EE:
``` ruby
module EE
module API
module MergeRequests
extend ActiveSupport::Concern
prepended do
helpers do
def update_merge_request_ee(merge_request)
# ...
end
end
end
end
end
end
```
#### EE `route_setting`
It's very hard to extend this in an EE module, and this is simply storing
some meta-data for a particular route. Given that, we could simply leave the
EE `route_setting` in CE as it won't hurt and we are just not going to use
those meta-data in CE.
We could revisit this policy when we're using `route_setting` more and whether
or not we really need to extend it from EE. For now we're not using it much.
#### Utilizing class methods for setting up EE-specific data
Sometimes we need to use different arguments for a particular API route, and we
can't easily extend it with an EE module because Grape has different context in
different blocks. In order to overcome this, we could use class methods from the
API class.
For example, in one place we need to pass an extra argument to
`at_least_one_of` so that the API could consider an EE-only argument as the
least argument. This is not quite beautiful but it's working:
``` ruby
module API
class MergeRequests < Grape::API
def self.update_params_at_least_one_of
%i[
assignee_id
description
]
end
prepend EE::API::MergeRequests
params do
at_least_one_of(*::API::MergeRequests.update_params_at_least_one_of)
end
end
end
```
And then we could easily extend that argument in the EE class method:
``` ruby
module EE
module API
module MergeRequests
extend ActiveSupport::Concern
class_methods do
def update_params_at_least_one_of
super.push(*%i[
squash
])
end
end
end
end
end
```
It could be annoying if we need this for a lot of routes, but it might be the
simplest solution right now.
### Code in `spec/` ### Code in `spec/`
When you're testing EE-only features, avoid adding examples to the When you're testing EE-only features, avoid adding examples to the
......
# JavaScript style guide # JavaScript style guide
> TODO: Add content We use [Airbnb's JavaScript Style Guide][airbnb-style-guide] and it's accompanying linter to manage most of our JavaScript style guidelines.
In addition to the style guidelines set by Airbnb, we also have a few specific rules listed below.
> **Tip:**
You can run eslint locally by running `yarn eslint`
## Arrays
<a name="avoid-foreach"></a><a name="1.1"></a>
- [1.1](#avoid-foreach) **Avoid ForEach when mutating data** Use `map`, `reduce` or `filter` instead of `forEach` when mutating data. This will minimize mutations in functions ([which is aligned with Airbnb's style guide][airbnb-minimize-mutations])
```
// bad
users.forEach((user, index) => {
user.id = index;
});
// good
const usersWithId = users.map((user, index) => {
return Object.assign({}, user, { id: index });
});
```
## Functions
<a name="limit-params"></a><a name="2.1"></a>
- [2.1](#limit-params) **Limit number of parameters** If your function or method has more than 3 parameters, use an object as a parameter instead.
```
// bad
function a(p1, p2, p3) {
// ...
};
// good
function a(p) {
// ...
};
```
## Classes & constructors
<a name="avoid-constructor-side-effects"></a><a name="3.1"></a>
- [3.1](#avoid-constructor-side-effects) **Avoid side effects in constructors** Avoid making some operations in the `constructor`, such as asynchronous calls, API requests and DOM manipulations. Prefer moving them into separate functions. This will make tests easier to write and code easier to maintain.
```javascript
// bad
class myClass {
constructor(config) {
this.config = config;
axios.get(this.config.endpoint)
}
}
// good
class myClass {
constructor(config) {
this.config = config;
}
makeRequest() {
axios.get(this.config.endpoint)
}
}
const instance = new myClass();
instance.makeRequest();
```
<a name="avoid-classes-to-handle-dom-events"></a><a name="3.2"></a>
- [3.2](#avoid-classes-to-handle-dom-events) **Avoid classes to handle DOM events** If the only purpose of the class is to bind a DOM event and handle the callback, prefer using a function.
```
// bad
class myClass {
constructor(config) {
this.config = config;
}
init() {
document.addEventListener('click', () => {});
}
}
// good
const myFunction = () => {
document.addEventListener('click', () => {
// handle callback here
});
}
```
<a name="element-container"></a><a name="3.3"></a>
- [3.3](#element-container) **Pass element container to constructor** When your class manipulates the DOM, receive the element container as a parameter.
This is more maintainable and performant.
```
// bad
class a {
constructor() {
document.querySelector('.b');
}
}
// good
class a {
constructor(options) {
options.container.querySelector('.b');
}
}
```
## Type Casting & Coercion
<a name="use-parseint"></a><a name="4.1"></a>
- [4.1](#use-parseint) **Use ParseInt** Use `ParseInt` when converting a numeric string into a number.
```
// bad
Number('10')
// good
parseInt('10', 10);
```
## CSS Selectors
<a name="use-js-prefix"></a><a name="5.1"></a>
- [5.1](#use-js-prefix) **Use js prefix** If a CSS class is only being used in JavaScript as a reference to the element, prefix the class name with `js-`
```
// bad
<button class="add-user"></button>
// good
<button class="js-add-user"></button>
```
## Modules
<a name="use-absolute-paths"></a><a name="6.1"></a>
- [6.1](#use-absolute-paths) **Use absolute paths for nearby modules** Use absolute paths if the module you are importing is less than two levels up.
```
// bad
import GitLabStyleGuide from '~/guides/GitLabStyleGuide';
// good
import GitLabStyleGuide from '../GitLabStyleGuide';
```
<a name="use-relative-paths"></a><a name="6.2"></a>
- [6.2](#use-relative-paths) **Use relative paths for distant modules** If the module you are importing is two or more levels up, use a relative path instead of an absolute path.
```
// bad
import GitLabStyleGuide from '../../../guides/GitLabStyleGuide';
// good
import GitLabStyleGuide from '~/GitLabStyleGuide';
```
<a name="global-namespace"></a><a name="6.3"></a>
- [6.3](#global-namespace) **Do not add to global namespace**
<a name="domcontentloaded"></a><a name="6.4"></a>
- [6.4](#domcontentloaded) **Do not use DOMContentLoaded in non-page modules** Imported modules should act the same each time they are loaded. `DOMContentLoaded` events are only allowed on modules loaded in the `/pages/*` directory because those are loaded dynamically with webpack.
## Security
<a name="avoid-xss"></a><a name="7.1"></a>
- [7.1](#avoid-xss) **Avoid XSS** Do not use `innerHTML`, `append()` or `html()` to set content. It opens up too many vulnerabilities.
## ESLint
<a name="disable-eslint-file"></a><a name="8.1"></a>
- [8.1](#disable-eslint-file) **Disabling ESLint in new files** Do not disable ESLint when creating new files. Existing files may have existing rules disabled due to legacy compatibility reasons but they are in the process of being refactored.
<a name="disable-eslint-rule"></a><a name="8.2"></a>
- [8.2](#disable-eslint-rule) **Disabling ESLint rule** Do not disable specific ESLint rules. Due to technical debt, you may disable the following rules only if you are invoking/instantiating existing code modules
- [no-new][no-new]
- [class-method-use-this][class-method-use-this]
> Note: Disable these rules on a per line basis. This makes it easier to refactor in the future. E.g. use `eslint-disable-next-line` or `eslint-disable-line`
[airbnb-style-guide]: https://github.com/airbnb/javascript
[airbnb-minimize-mutations]: https://github.com/airbnb/javascript#testing--for-real
[no-new]: http://eslint.org/docs/rules/no-new
[class-method-use-this]: http://eslint.org/docs/rules/class-methods-use-this
...@@ -36,9 +36,13 @@ the full process of installing GitLab on Google Container Engine (GKE), pushing ...@@ -36,9 +36,13 @@ the full process of installing GitLab on Google Container Engine (GKE), pushing
- [Install on AWS](https://about.gitlab.com/aws/) - [Install on AWS](https://about.gitlab.com/aws/)
- _Testing only!_ [DigitalOcean and Docker Machine](digitaloceandocker.md) - - _Testing only!_ [DigitalOcean and Docker Machine](digitaloceandocker.md) -
Quickly test any version of GitLab on DigitalOcean using Docker Machine. Quickly test any version of GitLab on DigitalOcean using Docker Machine.
<<<<<<< HEAD
- [GitLab Pivotal Tile](pivotal/index.md) - Install and configure GitLab - [GitLab Pivotal Tile](pivotal/index.md) - Install and configure GitLab
Premium on Pivotal Cloud Foundry. Premium on Pivotal Cloud Foundry.
- [Getting started with GitLab and DigitalOcean](ttps://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/): requirements, installation process, updates. - [Getting started with GitLab and DigitalOcean](ttps://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/): requirements, installation process, updates.
=======
- [Getting started with GitLab and DigitalOcean](https://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/): requirements, installation process, updates.
>>>>>>> upstream/master
- [Demo: Cloud Native Development with GitLab](https://about.gitlab.com/2017/04/18/cloud-native-demo/): video demonstration on how to install GitLab on Kubernetes, build a project, create Review Apps, store Docker images in Container Registry, deploy to production on Kubernetes, and monitor with Prometheus. - [Demo: Cloud Native Development with GitLab](https://about.gitlab.com/2017/04/18/cloud-native-demo/): video demonstration on how to install GitLab on Kubernetes, build a project, create Review Apps, store Docker images in Container Registry, deploy to production on Kubernetes, and monitor with Prometheus.
## Database ## Database
......
...@@ -142,11 +142,9 @@ and Git push/pull redirects. ...@@ -142,11 +142,9 @@ and Git push/pull redirects.
Depending on the situation, different things apply. Depending on the situation, different things apply.
When [renaming a user](../profile/index.md#changing-your-username) or When [renaming a user](../profile/index.md#changing-your-username),
[changing a group path](../group/index.md#changing-a-group-s-path): [changing a group path](../group/index.md#changing-a-group-s-path) or [renaming a repository](settings/index.md#renaming-a-repository):
- **The redirect to the new URL is permanent**, which means that the original
namespace can't be claimed again by any group or user.
- Existing web URLs for the namespace and anything under it (e.g., projects) will - Existing web URLs for the namespace and anything under it (e.g., projects) will
redirect to the new URLs. redirect to the new URLs.
- Starting with GitLab 10.3, existing Git remote URLs for projects under the - Starting with GitLab 10.3, existing Git remote URLs for projects under the
...@@ -155,9 +153,5 @@ When [renaming a user](../profile/index.md#changing-your-username) or ...@@ -155,9 +153,5 @@ When [renaming a user](../profile/index.md#changing-your-username) or
your remote will be displayed instead of rejecting your action. your remote will be displayed instead of rejecting your action.
This means that any automation scripts, or Git clients will continue to This means that any automation scripts, or Git clients will continue to
work after a rename, making any transition a lot smoother. work after a rename, making any transition a lot smoother.
To avoid pulling from or pushing to an entirely incorrect repository, the old - The redirects will be available as long as the original path is not claimed by
path will be reserved. another group, user or project.
When [renaming-a-repository](settings/index.md#renaming-a-repository), the same
things apply, except for the Git push/pull actions which will be rejected with a
warning message to change to the new remote URL.
...@@ -236,6 +236,20 @@ module API ...@@ -236,6 +236,20 @@ module API
render_api_error!("Failed to save note #{note.errors.messages}", 400) render_api_error!("Failed to save note #{note.errors.messages}", 400)
end end
end end
desc 'Get Merge Requests associated with a commit' do
success Entities::MergeRequestBasic
end
params do
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag on which to find Merge Requests'
use :pagination
end
get ':id/repository/commits/:sha/merge_requests', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! 'Commit' unless commit
present paginate(commit.merge_requests), with: Entities::MergeRequestBasic
end
end end
end end
end end
...@@ -54,7 +54,7 @@ module API ...@@ -54,7 +54,7 @@ module API
present key, with: Entities::DeployKeysProject present key, with: Entities::DeployKeysProject
end end
desc 'Add new deploy key to currently authenticated user' do desc 'Add new deploy key to a project' do
success Entities::DeployKeysProject success Entities::DeployKeysProject
end end
params do params do
...@@ -66,33 +66,32 @@ module API ...@@ -66,33 +66,32 @@ module API
params[:key].strip! params[:key].strip!
# Check for an existing key joined to this project # Check for an existing key joined to this project
key = user_project.deploy_keys_projects deploy_key_project = user_project.deploy_keys_projects
.joins(:deploy_key) .joins(:deploy_key)
.find_by(keys: { key: params[:key] }) .find_by(keys: { key: params[:key] })
if key if deploy_key_project
present key, with: Entities::DeployKeysProject present deploy_key_project, with: Entities::DeployKeysProject
break break
end end
# Check for available deploy keys in other projects # Check for available deploy keys in other projects
key = current_user.accessible_deploy_keys.find_by(key: params[:key]) key = current_user.accessible_deploy_keys.find_by(key: params[:key])
if key if key
added_key = add_deploy_keys_project(user_project, deploy_key: key, can_push: !!params[:can_push]) deploy_key_project = add_deploy_keys_project(user_project, deploy_key: key, can_push: !!params[:can_push])
present added_key, with: Entities::DeployKeysProject present deploy_key_project, with: Entities::DeployKeysProject
break break
end end
# Create a new deploy key # Create a new deploy key
key_attributes = { can_push: !!params[:can_push], deploy_key_attributes = declared_params.except(:can_push).merge(user: current_user)
deploy_key_attributes: declared_params.except(:can_push) } deploy_key_project = add_deploy_keys_project(user_project, deploy_key_attributes: deploy_key_attributes, can_push: !!params[:can_push])
key = add_deploy_keys_project(user_project, key_attributes)
if key.valid? if deploy_key_project.valid?
present key, with: Entities::DeployKeysProject present deploy_key_project, with: Entities::DeployKeysProject
else else
render_validation_error!(key) render_validation_error!(deploy_key_project)
end end
end end
......
...@@ -105,8 +105,12 @@ module Banzai ...@@ -105,8 +105,12 @@ module Banzai
end end
end end
options = link_options.merge(href: match) # match has come from node.to_html above, so we know it's encoded
content_tag(:a, match.html_safe, options) + dropped # correctly.
html_safe_match = match.html_safe
options = link_options.merge(href: html_safe_match)
content_tag(:a, html_safe_match, options) + dropped
end end
def autolink_filter(text) def autolink_filter(text)
......
...@@ -12,6 +12,7 @@ module Gitlab ...@@ -12,6 +12,7 @@ module Gitlab
class Build < ActiveRecord::Base class Build < ActiveRecord::Base
self.table_name = 'ci_builds' self.table_name = 'ci_builds'
self.inheritance_column = :_type_disabled
def ensure_stage!(attempts: 2) def ensure_stage!(attempts: 2)
find_stage || create_stage! find_stage || create_stage!
......
...@@ -9,20 +9,16 @@ module Gitlab ...@@ -9,20 +9,16 @@ module Gitlab
super(project, user, protocol) super(project, user, protocol)
end end
def message(rejected: false) def message
<<~MESSAGE <<~MESSAGE
Project '#{redirected_path}' was moved to '#{project.full_path}'. Project '#{redirected_path}' was moved to '#{project.full_path}'.
Please update your Git remote: Please update your Git remote:
#{remote_url_message(rejected)} git remote set-url origin #{url_to_repo}
MESSAGE MESSAGE
end end
def permanent_redirect?
RedirectRoute.permanent.exists?(path: redirected_path)
end
private private
attr_reader :redirected_path attr_reader :redirected_path
...@@ -30,18 +26,6 @@ module Gitlab ...@@ -30,18 +26,6 @@ module Gitlab
def self.message_key(user_id, project_id) def self.message_key(user_id, project_id)
"#{REDIRECT_NAMESPACE}:#{user_id}:#{project_id}" "#{REDIRECT_NAMESPACE}:#{user_id}:#{project_id}"
end end
def remote_url_message(rejected)
if rejected
"git remote set-url origin #{url_to_repo} and try again."
else
"git remote set-url origin #{url_to_repo}"
end
end
def url
protocol == 'ssh' ? project.ssh_url_to_repo : project.http_url_to_repo
end
end end
end end
end end
...@@ -900,11 +900,42 @@ into similar problems in the future (e.g. when new tables are created). ...@@ -900,11 +900,42 @@ into similar problems in the future (e.g. when new tables are created).
end end
end end
# Rails' index_exists? doesn't work when you only give it a table and index # Fetches indexes on a column by name for postgres.
# name. As such we have to use some extra code to check if an index exists for #
# a given name. # This will include indexes using an expression on the column, for example:
# `CREATE INDEX CONCURRENTLY index_name ON table (LOWER(column));`
#
# For mysql, it falls back to the default ActiveRecord implementation that
# will not find custom indexes. But it will select by name without passing
# a column.
#
# We can remove this when upgrading to Rails 5 with an updated `index_exists?`:
# - https://github.com/rails/rails/commit/edc2b7718725016e988089b5fb6d6fb9d6e16882
#
# Or this can be removed when we no longer support postgres < 9.5, so we
# can use `CREATE INDEX IF NOT EXISTS`.
def index_exists_by_name?(table, index) def index_exists_by_name?(table, index)
indexes(table).map(&:name).include?(index) # We can't fall back to the normal `index_exists?` method because that
# does not find indexes without passing a column name.
if indexes(table).map(&:name).include?(index.to_s)
true
elsif Gitlab::Database.postgresql?
postgres_exists_by_name?(table, index)
else
false
end
end
def postgres_exists_by_name?(table, name)
index_sql = <<~SQL
SELECT COUNT(*)
FROM pg_index
JOIN pg_class i ON (indexrelid=i.oid)
JOIN pg_class t ON (indrelid=t.oid)
WHERE i.relname = '#{name}' AND t.relname = '#{table}'
SQL
connection.select_value(index_sql).to_i > 0
end end
end end
end end
......
...@@ -56,7 +56,7 @@ module Gitlab ...@@ -56,7 +56,7 @@ module Gitlab
ensure_project_on_push!(cmd, changes) ensure_project_on_push!(cmd, changes)
check_project_accessibility! check_project_accessibility!
check_project_moved! add_project_moved_message!
check_repository_existence! check_repository_existence!
case cmd case cmd
...@@ -102,8 +102,6 @@ module Gitlab ...@@ -102,8 +102,6 @@ module Gitlab
end end
def check_active_user! def check_active_user!
return if deploy_key?
if user && !user_access.allowed? if user && !user_access.allowed?
raise UnauthorizedError, ERROR_MESSAGES[:account_blocked] raise UnauthorizedError, ERROR_MESSAGES[:account_blocked]
end end
...@@ -128,16 +126,12 @@ module Gitlab ...@@ -128,16 +126,12 @@ module Gitlab
end end
end end
def check_project_moved! def add_project_moved_message!
return if redirected_path.nil? return if redirected_path.nil?
project_moved = Checks::ProjectMoved.new(project, user, protocol, redirected_path) project_moved = Checks::ProjectMoved.new(project, user, protocol, redirected_path)
if project_moved.permanent_redirect? project_moved.add_message
project_moved.add_message
else
raise ProjectMovedError, project_moved.message(rejected: true)
end
end end
def check_command_disabled!(cmd) def check_command_disabled!(cmd)
...@@ -223,7 +217,7 @@ module Gitlab ...@@ -223,7 +217,7 @@ module Gitlab
raise UnauthorizedError, ERROR_MESSAGES[:read_only] raise UnauthorizedError, ERROR_MESSAGES[:read_only]
end end
if deploy_key if deploy_key?
unless deploy_key.can_push_to?(project) unless deploy_key.can_push_to?(project)
raise UnauthorizedError, ERROR_MESSAGES[:deploy_key_upload] raise UnauthorizedError, ERROR_MESSAGES[:deploy_key_upload]
end end
...@@ -332,8 +326,10 @@ module Gitlab ...@@ -332,8 +326,10 @@ module Gitlab
case actor case actor
when User when User
actor actor
when DeployKey
nil
when Key when Key
actor.user unless actor.is_a?(DeployKey) actor.user
when :ci when :ci
nil nil
end end
......
...@@ -7,8 +7,8 @@ task setup_postgresql: :environment do ...@@ -7,8 +7,8 @@ task setup_postgresql: :environment do
require Rails.root.join('db/migrate/20170724214302_add_lower_path_index_to_redirect_routes') require Rails.root.join('db/migrate/20170724214302_add_lower_path_index_to_redirect_routes')
require Rails.root.join('db/migrate/20170503185032_index_redirect_routes_path_for_like') require Rails.root.join('db/migrate/20170503185032_index_redirect_routes_path_for_like')
require Rails.root.join('db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb') require Rails.root.join('db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb')
require Rails.root.join('db/migrate/20180113220114_rework_redirect_routes_indexes.rb')
require Rails.root.join('db/migrate/20180215181245_users_name_lower_index.rb') require Rails.root.join('db/migrate/20180215181245_users_name_lower_index.rb')
require Rails.root.join('db/post_migrate/20180306164012_add_path_index_to_redirect_routes.rb')
NamespacesProjectsPathLowerIndexes.new.up NamespacesProjectsPathLowerIndexes.new.up
AddUsersLowerUsernameEmailIndexes.new.up AddUsersLowerUsernameEmailIndexes.new.up
...@@ -17,8 +17,8 @@ task setup_postgresql: :environment do ...@@ -17,8 +17,8 @@ task setup_postgresql: :environment do
AddLowerPathIndexToRedirectRoutes.new.up AddLowerPathIndexToRedirectRoutes.new.up
IndexRedirectRoutesPathForLike.new.up IndexRedirectRoutesPathForLike.new.up
AddIndexOnNamespacesLowerName.new.up AddIndexOnNamespacesLowerName.new.up
ReworkRedirectRoutesIndexes.new.up
UsersNameLowerIndex.new.up UsersNameLowerIndex.new.up
AddPathIndexToRedirectRoutes.new.up
end end
desc 'GitLab | Generate PostgreSQL Password Hash' desc 'GitLab | Generate PostgreSQL Password Hash'
......
...@@ -20,14 +20,23 @@ describe Projects::MilestonesController do ...@@ -20,14 +20,23 @@ describe Projects::MilestonesController do
describe "#show" do describe "#show" do
render_views render_views
def view_milestone def view_milestone(options = {})
get :show, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid params = { namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid }
get :show, params.merge(options)
end end
it 'shows milestone page' do it 'shows milestone page' do
view_milestone view_milestone
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(response.content_type).to eq 'text/html'
end
it 'returns milestone json' do
view_milestone format: :json
expect(response).to have_http_status(404)
expect(response.content_type).to eq 'application/json'
end end
end end
......
...@@ -2,14 +2,5 @@ FactoryBot.define do ...@@ -2,14 +2,5 @@ FactoryBot.define do
factory :redirect_route do factory :redirect_route do
sequence(:path) { |n| "redirect#{n}" } sequence(:path) { |n| "redirect#{n}" }
source factory: :group source factory: :group
permanent false
trait :permanent do
permanent true
end
trait :temporary do
permanent false
end
end end
end end
...@@ -19,7 +19,11 @@ feature 'Group activity page' do ...@@ -19,7 +19,11 @@ feature 'Group activity page' do
it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token" it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
end end
<<<<<<< HEAD
context 'when project in the group', :js do context 'when project in the group', :js do
=======
context 'when project is in the group', :js do
>>>>>>> upstream/master
let(:project) { create(:project, :public, namespace: group) } let(:project) { create(:project, :public, namespace: group) }
before do before do
......
...@@ -68,7 +68,11 @@ feature 'Group issues page' do ...@@ -68,7 +68,11 @@ feature 'Group issues page' do
it 'does not render issue' do it 'does not render issue' do
visit path visit path
<<<<<<< HEAD
expect(page).not_to have_content issue.title expect(page).not_to have_content issue.title
=======
expect(page).not_to have_content issue.title[0..80]
>>>>>>> upstream/master
end end
end end
end end
......
...@@ -30,6 +30,7 @@ feature 'Group show page' do ...@@ -30,6 +30,7 @@ feature 'Group show page' do
end end
it_behaves_like "an autodiscoverable RSS feed without an RSS token" it_behaves_like "an autodiscoverable RSS feed without an RSS token"
<<<<<<< HEAD
end end
context 'when group has a public project', :js do context 'when group has a public project', :js do
...@@ -40,6 +41,29 @@ feature 'Group show page' do ...@@ -40,6 +41,29 @@ feature 'Group show page' do
expect(page).to have_link group.name expect(page).to have_link group.name
expect(page).to have_link project.name expect(page).to have_link project.name
=======
end
context 'when group has a public project', :js do
let!(:project) { create(:project, :public, namespace: group) }
it 'renders public project' do
visit path
expect(page).to have_link group.name
expect(page).to have_link project.name
end
end
context 'when group has a private project', :js do
let!(:project) { create(:project, :private, namespace: group) }
it 'does not render private project' do
visit path
expect(page).to have_link group.name
expect(page).not_to have_link project.name
>>>>>>> upstream/master
end end
end end
end end
......
...@@ -55,6 +55,11 @@ describe PageLayoutHelper do ...@@ -55,6 +55,11 @@ describe PageLayoutHelper do
stub_env('CANARY', 'true') stub_env('CANARY', 'true')
expect(helper.favicon).to eq 'favicon-yellow.ico' expect(helper.favicon).to eq 'favicon-yellow.ico'
end end
it 'has yellow favicon for canary' do
stub_env('CANARY', 'true')
expect(helper.favicon).to eq 'favicon-yellow.ico'
end
end end
describe 'page_image' do describe 'page_image' do
......
...@@ -17,9 +17,12 @@ describe 'Projects (JavaScript fixtures)', type: :controller do ...@@ -17,9 +17,12 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
end end
before do before do
<<<<<<< HEAD
# EE-specific start # EE-specific start
stub_licensed_features(variable_environment_scope: true) stub_licensed_features(variable_environment_scope: true)
# EE specific end # EE specific end
=======
>>>>>>> upstream/master
project.add_master(admin) project.add_master(admin)
sign_in(admin) sign_in(admin)
end end
......
...@@ -25,26 +25,34 @@ describe('issue_discussion component', () => { ...@@ -25,26 +25,34 @@ describe('issue_discussion component', () => {
}); });
it('should render user avatar', () => { it('should render user avatar', () => {
expect(vm.$el.querySelector('.user-avatar-link')).toBeDefined(); expect(vm.$el.querySelector('.user-avatar-link')).not.toBeNull();
}); });
it('should render discussion header', () => { it('should render discussion header', () => {
expect(vm.$el.querySelector('.discussion-header')).toBeDefined(); expect(vm.$el.querySelector('.discussion-header')).not.toBeNull();
expect(vm.$el.querySelector('.notes').children.length).toEqual(discussionMock.notes.length); expect(vm.$el.querySelector('.notes').children.length).toEqual(discussionMock.notes.length);
}); });
describe('actions', () => { describe('actions', () => {
it('should render reply button', () => { it('should render reply button', () => {
expect(vm.$el.querySelector('.js-vue-discussion-reply').textContent.trim()).toEqual('Reply...'); expect(vm.$el.querySelector('.js-vue-discussion-reply').textContent.trim()).toEqual(
'Reply...',
);
}); });
it('should toggle reply form', (done) => { it('should toggle reply form', done => {
vm.$el.querySelector('.js-vue-discussion-reply').click(); vm.$el.querySelector('.js-vue-discussion-reply').click();
Vue.nextTick(() => { Vue.nextTick(() => {
expect(vm.$refs.noteForm).toBeDefined(); expect(vm.$refs.noteForm).not.toBeNull();
expect(vm.isReplying).toEqual(true); expect(vm.isReplying).toEqual(true);
done(); done();
}); });
}); });
it('does not render jump to discussion button', () => {
expect(
vm.$el.querySelector('*[data-original-title="Jump to next unresolved discussion"]'),
).toBeNull();
});
}); });
}); });
...@@ -167,6 +167,15 @@ describe Banzai::Filter::AutolinkFilter do ...@@ -167,6 +167,15 @@ describe Banzai::Filter::AutolinkFilter do
expect(actual).to eq(expected_complicated_link) expect(actual).to eq(expected_complicated_link)
end end
it 'does not double-encode HTML entities' do
encoded_link = "#{link}?foo=bar&amp;baz=quux"
expected_encoded_link = %Q{<a href="#{encoded_link}">#{encoded_link}</a>}
actual = unescape(filter(encoded_link).to_html)
expect(actual).to eq(Rinku.auto_link(encoded_link))
expect(actual).to eq(expected_encoded_link)
end
it 'does not include trailing HTML entities' do it 'does not include trailing HTML entities' do
doc = filter("See &lt;&lt;&lt;#{link}&gt;&gt;&gt;") doc = filter("See &lt;&lt;&lt;#{link}&gt;&gt;&gt;")
......
...@@ -51,4 +51,20 @@ describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 201 ...@@ -51,4 +51,20 @@ describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 201
expect { described_class.new.perform(1, 6) } expect { described_class.new.perform(1, 6) }
.to raise_error ActiveRecord::RecordNotUnique .to raise_error ActiveRecord::RecordNotUnique
end end
context 'when invalid class can be loaded due to single table inheritance' do
let(:commit_status) do
jobs.create!(id: 7, commit_id: 1, project_id: 123, stage_idx: 4,
stage: 'post-deploy', status: :failed)
end
before do
commit_status.update_column(:type, 'SomeClass')
end
it 'does ignore single table inheritance type' do
expect { described_class.new.perform(1, 7) }.not_to raise_error
expect(jobs.find(7)).to have_attributes(stage_id: (a_value > 0))
end
end
end end
...@@ -44,44 +44,17 @@ describe Gitlab::Checks::ProjectMoved, :clean_gitlab_redis_shared_state do ...@@ -44,44 +44,17 @@ describe Gitlab::Checks::ProjectMoved, :clean_gitlab_redis_shared_state do
end end
describe '#message' do describe '#message' do
context 'when the push is rejected' do it 'returns a redirect message' do
it 'returns a redirect message telling the user to try again' do project_moved = described_class.new(project, user, 'http', 'foo/bar')
project_moved = described_class.new(project, user, 'http', 'foo/bar') message = <<~MSG
message = "Project 'foo/bar' was moved to '#{project.full_path}'." + Project 'foo/bar' was moved to '#{project.full_path}'.
"\n\nPlease update your Git remote:" +
"\n\n git remote set-url origin #{project.http_url_to_repo} and try again.\n"
expect(project_moved.message(rejected: true)).to eq(message) Please update your Git remote:
end
end
context 'when the push is not rejected' do git remote set-url origin #{project.http_url_to_repo}
it 'returns a redirect message' do MSG
project_moved = described_class.new(project, user, 'http', 'foo/bar')
message = "Project 'foo/bar' was moved to '#{project.full_path}'." +
"\n\nPlease update your Git remote:" +
"\n\n git remote set-url origin #{project.http_url_to_repo}\n"
expect(project_moved.message).to eq(message) expect(project_moved.message).to eq(message)
end
end
end
describe '#permanent_redirect?' do
context 'with a permanent RedirectRoute' do
it 'returns true' do
project.route.create_redirect('foo/bar', permanent: true)
project_moved = described_class.new(project, user, 'http', 'foo/bar')
expect(project_moved.permanent_redirect?).to be_truthy
end
end
context 'without a permanent RedirectRoute' do
it 'returns false' do
project.route.create_redirect('foo/bar')
project_moved = described_class.new(project, user, 'http', 'foo/bar')
expect(project_moved.permanent_redirect?).to be_falsy
end
end end
end end
end end
...@@ -1211,4 +1211,33 @@ describe Gitlab::Database::MigrationHelpers do ...@@ -1211,4 +1211,33 @@ describe Gitlab::Database::MigrationHelpers do
expect(model.perform_background_migration_inline?).to eq(false) expect(model.perform_background_migration_inline?).to eq(false)
end end
end end
describe '#index_exists_by_name?' do
it 'returns true if an index exists' do
expect(model.index_exists_by_name?(:projects, 'index_projects_on_path'))
.to be_truthy
end
it 'returns false if the index does not exist' do
expect(model.index_exists_by_name?(:projects, 'this_does_not_exist'))
.to be_falsy
end
context 'when an index with a function exists', :postgresql do
before do
ActiveRecord::Base.connection.execute(
'CREATE INDEX test_index ON projects (LOWER(path));'
)
end
after do
'DROP INDEX IF EXISTS test_index;'
end
it 'returns true if an index exists' do
expect(model.index_exists_by_name?(:projects, 'test_index'))
.to be_truthy
end
end
end
end end
...@@ -240,14 +240,21 @@ describe Gitlab::GitAccess do ...@@ -240,14 +240,21 @@ describe Gitlab::GitAccess do
end end
shared_examples 'check_project_moved' do shared_examples 'check_project_moved' do
it 'enqueues a redirected message' do it 'enqueues a redirected message for pushing' do
push_access_check push_access_check
expect(Gitlab::Checks::ProjectMoved.fetch_message(user.id, project.id)).not_to be_nil expect(Gitlab::Checks::ProjectMoved.fetch_message(user.id, project.id)).not_to be_nil
end end
it 'allows push and pull access' do
aggregate_failures do
expect { push_access_check }.not_to raise_error
expect { pull_access_check }.not_to raise_error
end
end
end end
describe '#check_project_moved!', :clean_gitlab_redis_shared_state do describe '#add_project_moved_message!', :clean_gitlab_redis_shared_state do
before do before do
project.add_master(user) project.add_master(user)
end end
...@@ -261,62 +268,18 @@ describe Gitlab::GitAccess do ...@@ -261,62 +268,18 @@ describe Gitlab::GitAccess do
end end
end end
context 'when a permanent redirect and ssh protocol' do context 'with a redirect and ssh protocol' do
let(:redirected_path) { 'some/other-path' } let(:redirected_path) { 'some/other-path' }
before do
allow_any_instance_of(Gitlab::Checks::ProjectMoved).to receive(:permanent_redirect?).and_return(true)
end
it 'allows push and pull access' do
aggregate_failures do
expect { push_access_check }.not_to raise_error
end
end
it_behaves_like 'check_project_moved' it_behaves_like 'check_project_moved'
end end
context 'with a permanent redirect and http protocol' do context 'with a redirect and http protocol' do
let(:redirected_path) { 'some/other-path' } let(:redirected_path) { 'some/other-path' }
let(:protocol) { 'http' } let(:protocol) { 'http' }
before do
allow_any_instance_of(Gitlab::Checks::ProjectMoved).to receive(:permanent_redirect?).and_return(true)
end
it 'allows_push and pull access' do
aggregate_failures do
expect { push_access_check }.not_to raise_error
end
end
it_behaves_like 'check_project_moved' it_behaves_like 'check_project_moved'
end end
context 'with a temporal redirect and ssh protocol' do
let(:redirected_path) { 'some/other-path' }
it 'blocks push and pull access' do
aggregate_failures do
expect { push_access_check }.to raise_error(described_class::ProjectMovedError, /Project '#{redirected_path}' was moved to '#{project.full_path}'/)
expect { push_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.ssh_url_to_repo}/)
expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /Project '#{redirected_path}' was moved to '#{project.full_path}'/)
expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.ssh_url_to_repo}/)
end
end
end
context 'with a temporal redirect and http protocol' do
let(:redirected_path) { 'some/other-path' }
let(:protocol) { 'http' }
it 'does not allow to push and pull access' do
expect { push_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/)
expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/)
end
end
end end
describe '#check_authentication_abilities!' do describe '#check_authentication_abilities!' do
......
...@@ -17,4 +17,25 @@ describe DeployKey, :mailer do ...@@ -17,4 +17,25 @@ describe DeployKey, :mailer do
should_not_email(user) should_not_email(user)
end end
end end
describe '#user' do
let(:deploy_key) { create(:deploy_key) }
let(:user) { create(:user) }
context 'when user is set' do
before do
deploy_key.user = user
end
it 'returns the user' do
expect(deploy_key.user).to be(user)
end
end
context 'when user is not set' do
it 'returns the ghost user' do
expect(deploy_key.user).to eq(User.ghost)
end
end
end
end end
...@@ -16,66 +16,6 @@ describe Route do ...@@ -16,66 +16,6 @@ describe Route do
it { is_expected.to validate_presence_of(:source) } it { is_expected.to validate_presence_of(:source) }
it { is_expected.to validate_presence_of(:path) } it { is_expected.to validate_presence_of(:path) }
it { is_expected.to validate_uniqueness_of(:path).case_insensitive } it { is_expected.to validate_uniqueness_of(:path).case_insensitive }
describe '#ensure_permanent_paths' do
context 'when the route is not yet persisted' do
let(:new_route) { described_class.new(path: 'foo', source: build(:group)) }
context 'when permanent conflicting redirects exist' do
it 'is invalid' do
redirect = build(:redirect_route, :permanent, path: 'foo/bar/baz')
redirect.save!(validate: false)
expect(new_route.valid?).to be_falsey
expect(new_route.errors.first[1]).to eq('has been taken before')
end
end
context 'when no permanent conflicting redirects exist' do
it 'is valid' do
expect(new_route.valid?).to be_truthy
end
end
end
context 'when path has changed' do
before do
route.path = 'foo'
end
context 'when permanent conflicting redirects exist' do
it 'is invalid' do
redirect = build(:redirect_route, :permanent, path: 'foo/bar/baz')
redirect.save!(validate: false)
expect(route.valid?).to be_falsey
expect(route.errors.first[1]).to eq('has been taken before')
end
end
context 'when no permanent conflicting redirects exist' do
it 'is valid' do
expect(route.valid?).to be_truthy
end
end
end
context 'when path has not changed' do
context 'when permanent conflicting redirects exist' do
it 'is valid' do
redirect = build(:redirect_route, :permanent, path: 'git_lab/foo/bar')
redirect.save!(validate: false)
expect(route.valid?).to be_truthy
end
end
context 'when no permanent conflicting redirects exist' do
it 'is valid' do
expect(route.valid?).to be_truthy
end
end
end
end
end end
describe 'callbacks' do describe 'callbacks' do
...@@ -211,43 +151,31 @@ describe Route do ...@@ -211,43 +151,31 @@ describe Route do
end end
context 'when the source is a Project' do context 'when the source is a Project' do
it 'creates a temporal RedirectRoute' do it 'creates a RedirectRoute' do
project = create(:project) project = create(:project)
route = project.route route = project.route
redirect_route = route.create_redirect('foo') redirect_route = route.create_redirect('foo')
expect(redirect_route.permanent?).to be_falsy expect(redirect_route).not_to be_nil
end end
end end
context 'when the source is not a project' do context 'when the source is not a project' do
it 'creates a permanent RedirectRoute' do it 'creates a RedirectRoute' do
redirect_route = route.create_redirect('foo', permanent: true) redirect_route = route.create_redirect('foo')
expect(redirect_route.permanent?).to be_truthy expect(redirect_route).not_to be_nil
end end
end end
end end
describe '#delete_conflicting_redirects' do describe '#delete_conflicting_redirects' do
context 'with permanent redirect' do let(:route) { create(:project).route }
it 'does not delete the redirect' do
route.create_redirect("#{route.path}/foo", permanent: true)
expect do
route.delete_conflicting_redirects
end.not_to change { RedirectRoute.count }
end
end
context 'with temporal redirect' do
let(:route) { create(:project).route }
it 'deletes the redirect' do it 'deletes the redirect' do
route.create_redirect("#{route.path}/foo") route.create_redirect("#{route.path}/foo")
expect do expect do
route.delete_conflicting_redirects route.delete_conflicting_redirects
end.to change { RedirectRoute.count }.by(-1) end.to change { RedirectRoute.count }.by(-1)
end
end end
context 'when a redirect route with the same path exists' do context 'when a redirect route with the same path exists' do
...@@ -289,31 +217,18 @@ describe Route do ...@@ -289,31 +217,18 @@ describe Route do
end end
describe '#conflicting_redirects' do describe '#conflicting_redirects' do
let(:route) { create(:project).route }
it 'returns an ActiveRecord::Relation' do it 'returns an ActiveRecord::Relation' do
expect(route.conflicting_redirects).to be_an(ActiveRecord::Relation) expect(route.conflicting_redirects).to be_an(ActiveRecord::Relation)
end end
context 'with permanent redirects' do it 'returns the redirect routes' do
it 'does not return anything' do redirect1 = route.create_redirect("#{route.path}/foo")
route.create_redirect("#{route.path}/foo", permanent: true) redirect2 = route.create_redirect("#{route.path}/foo/bar")
route.create_redirect("#{route.path}/foo/bar", permanent: true) redirect3 = route.create_redirect("#{route.path}/baz/quz")
route.create_redirect("#{route.path}/baz/quz", permanent: true)
expect(route.conflicting_redirects).to be_empty expect(route.conflicting_redirects).to match_array([redirect1, redirect2, redirect3])
end
end
context 'with temporal redirects' do
let(:route) { create(:project).route }
it 'returns the redirect routes' do
route = create(:project).route
redirect1 = route.create_redirect("#{route.path}/foo")
redirect2 = route.create_redirect("#{route.path}/foo/bar")
redirect3 = route.create_redirect("#{route.path}/baz/quz")
expect(route.conflicting_redirects).to match_array([redirect1, redirect2, redirect3])
end
end end
context 'when a redirect route with the same path exists' do context 'when a redirect route with the same path exists' do
...@@ -348,44 +263,6 @@ describe Route do ...@@ -348,44 +263,6 @@ describe Route do
end end
end end
describe "#conflicting_redirect_exists?" do
context 'when a conflicting redirect exists' do
let(:group1) { create(:group, path: 'foo') }
let(:group2) { create(:group, path: 'baz') }
it 'should not be saved' do
group1.path = 'bar'
group1.save
group2.path = 'foo'
expect(group2.save).to be_falsy
end
it 'should return an error on path' do
group1.path = 'bar'
group1.save
group2.path = 'foo'
group2.valid?
expect(group2.errors[:path]).to eq(['has been taken before'])
end
end
context 'when a conflicting redirect does not exist' do
let(:project1) { create(:project, path: 'foo') }
let(:project2) { create(:project, path: 'baz') }
it 'should be saved' do
project1.path = 'bar'
project1.save
project2.path = 'foo'
expect(project2.save).to be_truthy
end
end
end
describe '#delete_conflicting_orphaned_routes' do describe '#delete_conflicting_orphaned_routes' do
context 'when there is a conflicting route' do context 'when there is a conflicting route' do
let!(:conflicting_group) { create(:group, path: 'foo') } let!(:conflicting_group) { create(:group, path: 'foo') }
......
...@@ -62,6 +62,21 @@ describe Service do ...@@ -62,6 +62,21 @@ describe Service do
end end
describe "Template" do describe "Template" do
describe '.build_from_template' do
context 'when template is invalid' do
it 'sets service template to inactive when template is invalid' do
project = create(:project)
template = JiraService.new(template: true, active: true)
template.save(validate: false)
service = described_class.build_from_template(project.id, template)
expect(service).to be_valid
expect(service.active).to be false
end
end
end
describe "for pushover service" do describe "for pushover service" do
let!(:service_template) do let!(:service_template) do
PushoverService.create( PushoverService.create(
......
...@@ -29,7 +29,7 @@ describe User do ...@@ -29,7 +29,7 @@ describe User do
it { is_expected.to have_many(:group_members) } it { is_expected.to have_many(:group_members) }
it { is_expected.to have_many(:groups) } it { is_expected.to have_many(:groups) }
it { is_expected.to have_many(:keys).dependent(:destroy) } it { is_expected.to have_many(:keys).dependent(:destroy) }
it { is_expected.to have_many(:deploy_keys).dependent(:destroy) } it { is_expected.to have_many(:deploy_keys).dependent(:nullify) }
it { is_expected.to have_many(:events).dependent(:destroy) } it { is_expected.to have_many(:events).dependent(:destroy) }
it { is_expected.to have_many(:issues).dependent(:destroy) } it { is_expected.to have_many(:issues).dependent(:destroy) }
it { is_expected.to have_many(:notes).dependent(:destroy) } it { is_expected.to have_many(:notes).dependent(:destroy) }
...@@ -135,23 +135,6 @@ describe User do ...@@ -135,23 +135,6 @@ describe User do
end end
end end
context 'when the username was used by another user before' do
let(:username) { 'foo' }
let!(:other_user) { create(:user, username: username) }
before do
other_user.username = 'bar'
other_user.save!
end
it 'is invalid' do
user = build(:user, username: username)
expect(user).not_to be_valid
expect(user.errors.full_messages).to eq(['Username has been taken before'])
end
end
context 'when the username is in use by another user' do context 'when the username is in use by another user' do
let(:username) { 'foo' } let(:username) { 'foo' }
let!(:other_user) { create(:user, username: username) } let!(:other_user) { create(:user, username: username) }
...@@ -2802,27 +2785,19 @@ describe User do ...@@ -2802,27 +2785,19 @@ describe User do
end end
end end
describe "#username_previously_taken?" do context 'changing a username' do
let(:user1) { create(:user, username: 'foo') } let(:user) { create(:user, username: 'foo') }
context 'when the username has been taken before' do it 'creates a redirect route' do
before do expect { user.update!(username: 'bar') }
user1.username = 'bar' .to change { RedirectRoute.where(path: 'foo').count }.by(1)
user1.save!
end
it 'should raise an ActiveRecord::RecordInvalid exception' do
user2 = build(:user, username: 'foo')
expect { user2.save! }.to raise_error(ActiveRecord::RecordInvalid, /Username has been taken before/)
end
end end
context 'when the username has not been taken before' do it 'deletes the redirect when a user with the old username was created' do
it 'should be valid' do user.update!(username: 'bar')
expect(RedirectRoute.count).to eq(0)
user2 = build(:user, username: 'baz') expect { create(:user, username: 'foo') }
expect(user2).to be_valid .to change { RedirectRoute.where(path: 'foo').count }.by(-1)
end
end end
end end
end end
...@@ -1149,4 +1149,33 @@ describe API::Commits do ...@@ -1149,4 +1149,33 @@ describe API::Commits do
end end
end end
end end
describe 'GET /projects/:id/repository/commits/:sha/merge_requests' do
let!(:project) { create(:project, :repository, :private) }
let!(:merged_mr) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'feature') }
let(:commit) { merged_mr.merge_request_diff.commits.last }
it 'returns the correct merge request' do
get api("/projects/#{project.id}/repository/commits/#{commit.id}/merge_requests", user)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response.length).to eq(1)
expect(json_response[0]['id']).to eq(merged_mr.id)
end
it 'returns 403 for an unauthorized user' do
project.add_guest(user)
get api("/projects/#{project.id}/repository/commits/#{commit.id}/merge_requests", user)
expect(response).to have_gitlab_http_status(403)
end
it 'responds 404 when the commit does not exist' do
get api("/projects/#{project.id}/repository/commits/a7d26f00c35b/merge_requests", user)
expect(response).to have_gitlab_http_status(404)
end
end
end end
...@@ -91,6 +91,10 @@ describe API::DeployKeys do ...@@ -91,6 +91,10 @@ describe API::DeployKeys do
expect do expect do
post api("/projects/#{project.id}/deploy_keys", admin), key_attrs post api("/projects/#{project.id}/deploy_keys", admin), key_attrs
end.to change { project.deploy_keys.count }.by(1) end.to change { project.deploy_keys.count }.by(1)
new_key = project.deploy_keys.last
expect(new_key.key).to eq(key_attrs[:key])
expect(new_key.user).to eq(admin)
end end
it 'returns an existing ssh key when attempting to add a duplicate' do it 'returns an existing ssh key when attempting to add a duplicate' do
......
...@@ -344,20 +344,11 @@ describe 'Git HTTP requests' do ...@@ -344,20 +344,11 @@ describe 'Git HTTP requests' do
context 'and the user requests a redirected path' do context 'and the user requests a redirected path' do
let!(:redirect) { project.route.create_redirect('foo/bar') } let!(:redirect) { project.route.create_redirect('foo/bar') }
let(:path) { "#{redirect.path}.git" } let(:path) { "#{redirect.path}.git" }
let(:project_moved_message) do
<<-MSG.strip_heredoc
Project '#{redirect.path}' was moved to '#{project.full_path}'.
Please update your Git remote: it 'downloads get status 200 for redirects' do
git remote set-url origin #{project.http_url_to_repo} and try again.
MSG
end
it 'downloads get status 404 with "project was moved" message' do
clone_get(path, {}) clone_get(path, {})
expect(response).to have_gitlab_http_status(:not_found)
expect(response.body).to match(project_moved_message) expect(response).to have_gitlab_http_status(:ok)
end end
end end
end end
...@@ -559,20 +550,19 @@ describe 'Git HTTP requests' do ...@@ -559,20 +550,19 @@ describe 'Git HTTP requests' do
Please update your Git remote: Please update your Git remote:
git remote set-url origin #{project.http_url_to_repo} and try again. git remote set-url origin #{project.http_url_to_repo}.
MSG MSG
end end
it 'downloads get status 404 with "project was moved" message' do it 'downloads get status 200' do
clone_get(path, env) clone_get(path, env)
expect(response).to have_gitlab_http_status(:not_found)
expect(response.body).to match(project_moved_message) expect(response).to have_gitlab_http_status(:ok)
end end
it 'uploads get status 404 with "project was moved" message' do it 'uploads get status 404 with "project was moved" message' do
upload(path, env) do |response| upload(path, env) do |response|
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:ok)
expect(response.body).to match(project_moved_message)
end end
end end
end end
......
...@@ -29,8 +29,13 @@ describe Ci::RetryBuildService do ...@@ -29,8 +29,13 @@ describe Ci::RetryBuildService do
commit_id deployments erased_by_id last_deployment project_id commit_id deployments erased_by_id last_deployment project_id
runner_id tag_taggings taggings tags trigger_request_id runner_id tag_taggings taggings tags trigger_request_id
user_id auto_canceled_by_id retried failure_reason user_id auto_canceled_by_id retried failure_reason
<<<<<<< HEAD
sourced_pipelines artifacts_file_store artifacts_metadata_store sourced_pipelines artifacts_file_store artifacts_metadata_store
metadata].freeze # EE metadata].freeze # EE
=======
artifacts_file_store artifacts_metadata_store
metadata].freeze
>>>>>>> upstream/master
shared_examples 'build duplication' do shared_examples 'build duplication' do
let(:another_pipeline) { create(:ci_empty_pipeline, project: project) } let(:another_pipeline) { create(:ci_empty_pipeline, project: project) }
......
...@@ -222,8 +222,8 @@ describe Groups::TransferService, :postgresql do ...@@ -222,8 +222,8 @@ describe Groups::TransferService, :postgresql do
expect(new_parent_group.children.first).to eq(group) expect(new_parent_group.children.first).to eq(group)
end end
it 'should create a permanent redirect for the group' do it 'should create a redirect for the group' do
expect(group.redirect_routes.permanent.count).to eq(1) expect(group.redirect_routes.count).to eq(1)
end end
end end
...@@ -243,10 +243,10 @@ describe Groups::TransferService, :postgresql do ...@@ -243,10 +243,10 @@ describe Groups::TransferService, :postgresql do
end end
end end
it 'should create permanent redirects for the subgroups' do it 'should create redirects for the subgroups' do
expect(group.redirect_routes.permanent.count).to eq(1) expect(group.redirect_routes.count).to eq(1)
expect(subgroup1.redirect_routes.permanent.count).to eq(1) expect(subgroup1.redirect_routes.count).to eq(1)
expect(subgroup2.redirect_routes.permanent.count).to eq(1) expect(subgroup2.redirect_routes.count).to eq(1)
end end
context 'when the new parent has a higher visibility than the children' do context 'when the new parent has a higher visibility than the children' do
...@@ -287,9 +287,9 @@ describe Groups::TransferService, :postgresql do ...@@ -287,9 +287,9 @@ describe Groups::TransferService, :postgresql do
end end
it 'should create permanent redirects for the projects' do it 'should create permanent redirects for the projects' do
expect(group.redirect_routes.permanent.count).to eq(1) expect(group.redirect_routes.count).to eq(1)
expect(project1.redirect_routes.permanent.count).to eq(1) expect(project1.redirect_routes.count).to eq(1)
expect(project2.redirect_routes.permanent.count).to eq(1) expect(project2.redirect_routes.count).to eq(1)
end end
context 'when the new parent has a higher visibility than the projects' do context 'when the new parent has a higher visibility than the projects' do
...@@ -338,12 +338,12 @@ describe Groups::TransferService, :postgresql do ...@@ -338,12 +338,12 @@ describe Groups::TransferService, :postgresql do
end end
end end
it 'should create permanent redirect for the subgroups and projects' do it 'should create redirect for the subgroups and projects' do
expect(group.redirect_routes.permanent.count).to eq(1) expect(group.redirect_routes.count).to eq(1)
expect(subgroup1.redirect_routes.permanent.count).to eq(1) expect(subgroup1.redirect_routes.count).to eq(1)
expect(subgroup2.redirect_routes.permanent.count).to eq(1) expect(subgroup2.redirect_routes.count).to eq(1)
expect(project1.redirect_routes.permanent.count).to eq(1) expect(project1.redirect_routes.count).to eq(1)
expect(project2.redirect_routes.permanent.count).to eq(1) expect(project2.redirect_routes.count).to eq(1)
end end
end end
...@@ -380,12 +380,12 @@ describe Groups::TransferService, :postgresql do ...@@ -380,12 +380,12 @@ describe Groups::TransferService, :postgresql do
end end
end end
it 'should create permanent redirect for the subgroups and projects' do it 'should create redirect for the subgroups and projects' do
expect(group.redirect_routes.permanent.count).to eq(1) expect(group.redirect_routes.count).to eq(1)
expect(project1.redirect_routes.permanent.count).to eq(1) expect(project1.redirect_routes.count).to eq(1)
expect(subgroup1.redirect_routes.permanent.count).to eq(1) expect(subgroup1.redirect_routes.count).to eq(1)
expect(nested_subgroup.redirect_routes.permanent.count).to eq(1) expect(nested_subgroup.redirect_routes.count).to eq(1)
expect(nested_project.redirect_routes.permanent.count).to eq(1) expect(nested_project.redirect_routes.count).to eq(1)
end end
end end
......
...@@ -70,6 +70,16 @@ describe Projects::CreateService, '#execute' do ...@@ -70,6 +70,16 @@ describe Projects::CreateService, '#execute' do
opts[:default_branch] = 'master' opts[:default_branch] = 'master'
expect(create_project(user, opts)).to eq(nil) expect(create_project(user, opts)).to eq(nil)
end end
it 'sets invalid service as inactive' do
create(:service, type: 'JiraService', project: nil, template: true, active: true)
project = create_project(user, opts)
service = project.services.first
expect(project).to be_persisted
expect(service.active).to be false
end
end end
context 'wiki_enabled creates repository directory' do context 'wiki_enabled creates repository directory' do
...@@ -232,14 +242,15 @@ describe Projects::CreateService, '#execute' do ...@@ -232,14 +242,15 @@ describe Projects::CreateService, '#execute' do
end end
context 'when a bad service template is created' do context 'when a bad service template is created' do
it 'reports an error in the imported project' do it 'sets service to be inactive' do
opts[:import_url] = 'http://www.gitlab.com/gitlab-org/gitlab-ce' opts[:import_url] = 'http://www.gitlab.com/gitlab-org/gitlab-ce'
create(:service, type: 'DroneCiService', project: nil, template: true, active: true) create(:service, type: 'DroneCiService', project: nil, template: true, active: true)
project = create_project(user, opts) project = create_project(user, opts)
service = project.services.first
expect(project.errors.full_messages_for(:base).first).to match(/Unable to save project. Error: Unable to save DroneCiService/) expect(project).to be_persisted
expect(project.services.count).to eq 0 expect(service.active).to be false
end end
end end
......
...@@ -2,7 +2,10 @@ module MigrationsHelpers ...@@ -2,7 +2,10 @@ module MigrationsHelpers
prepend EE::MigrationsHelpers prepend EE::MigrationsHelpers
def table(name) def table(name)
Class.new(ActiveRecord::Base) { self.table_name = name } Class.new(ActiveRecord::Base) do
self.table_name = name
self.inheritance_column = :_type_disabled
end
end end
def migrations_paths def migrations_paths
......
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