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
lib/gitlab/background_migration/*
app/models/project_services/kubernetes_service.rb
lib/gitlab/workhorse.rb
<<<<<<< HEAD
ee/db/**/*
ee/app/serializers/ee/merge_request_widget_entity.rb
ee/lib/ee/gitlab/ldap/sync/admin_users.rb
ee/spec/**/*
=======
>>>>>>> upstream/master
......@@ -4,8 +4,14 @@ entry.
## 10.6.2 (2018-03-29)
<<<<<<< HEAD
### 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)
......
......@@ -28,7 +28,7 @@ gem 'default_value_for', gem_versions['default_value_for']
gem 'mysql2', '~> 0.4.10', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres
gem 'rugged', '~> 0.26.0'
gem 'rugged', '~> 0.27'
gem 'grape-route-helpers', '~> 2.1.0'
gem 'faraday', '~> 0.12'
......@@ -44,7 +44,7 @@ gem 'omniauth-cas3', '~> 1.1.4'
gem 'omniauth-facebook', '~> 4.0.0'
gem 'omniauth-github', '~> 1.1.1'
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-oauth2-generic', '~> 0.2.2'
gem 'omniauth-saml', '~> 1.10'
......@@ -388,6 +388,8 @@ group :development, :test do
gem 'stackprof', '~> 0.2.10', require: false
gem 'simple_po_parser', '~> 1.1.2', require: false
gem 'timecop', '~> 0.8.0'
end
group :test do
......@@ -397,7 +399,6 @@ group :test do
gem 'webmock', '~> 2.3.2'
gem 'test_after_commit', '~> 1.1'
gem 'sham_rack', '~> 1.3.6'
gem 'timecop', '~> 0.8.0'
gem 'concurrent-ruby', '~> 1.0.5'
gem 'test-prof', '~> 0.2.5'
end
......
......@@ -579,11 +579,10 @@ GEM
omniauth-gitlab (1.0.2)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.0)
omniauth-google-oauth2 (0.5.2)
jwt (~> 1.5)
multi_json (~> 1.3)
omniauth-google-oauth2 (0.5.3)
jwt (>= 1.5)
omniauth (>= 1.1.1)
omniauth-oauth2 (>= 1.3.1)
omniauth-oauth2 (>= 1.5)
omniauth-jwt (0.0.2)
jwt
omniauth (~> 1.1)
......@@ -595,8 +594,8 @@ GEM
omniauth-oauth (1.1.0)
oauth
omniauth (~> 1.0)
omniauth-oauth2 (1.4.0)
oauth2 (~> 1.0)
omniauth-oauth2 (1.5.0)
oauth2 (~> 1.1)
omniauth (~> 1.2)
omniauth-oauth2-generic (0.2.2)
omniauth-oauth2 (~> 1.0)
......@@ -843,7 +842,7 @@ GEM
rubyzip (1.2.1)
rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
rugged (0.26.0)
rugged (0.27.0)
safe_yaml (1.0.4)
sanitize (2.1.0)
nokogiri (>= 1.4.4)
......@@ -1155,7 +1154,7 @@ DEPENDENCIES
omniauth-facebook (~> 4.0.0)
omniauth-github (~> 1.1.1)
omniauth-gitlab (~> 1.0.2)
omniauth-google-oauth2 (~> 0.5.2)
omniauth-google-oauth2 (~> 0.5.3)
omniauth-jwt (~> 0.0.2)
omniauth-kerberos (~> 0.3.0)
omniauth-oauth2-generic (~> 0.2.2)
......@@ -1211,7 +1210,7 @@ DEPENDENCIES
ruby-prof (~> 0.17.0)
ruby_parser (~> 3.8)
rufus-scheduler (~> 3.4)
rugged (~> 0.26.0)
rugged (~> 0.27)
sanitize (~> 2.0)
sass-rails (~> 5.0.6)
scss_lint (~> 0.56.0)
......
......@@ -31,7 +31,7 @@ export default function renderMath($els) {
if (!$els.length) return;
Promise.all([
import(/* webpackChunkName: 'katex' */ 'katex'),
import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.css'),
import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.min.css'),
]).then(([katex]) => {
renderWithKaTeX($els, katex);
}).catch(() => flash(__('An error occurred while rendering KaTeX')));
......
......@@ -292,10 +292,12 @@ Please check your network connection and try again.`;
</button>
</div>
<div
v-if="note.resolvable"
class="btn-group discussion-actions"
role="group">
role="group"
>
<div
v-if="note.resolvable && !discussionResolved"
v-if="!discussionResolved"
class="btn-group"
role="group">
<a
......
......@@ -15,6 +15,7 @@ export default {
type: String,
required: true,
},
<<<<<<< HEAD
url: {
type: String,
required: true,
......@@ -22,6 +23,31 @@ export default {
groupName: {
type: String,
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: {
......
......@@ -199,6 +199,10 @@
.branch-header-title {
color: $color-700;
}
.ide-file-list .file.file-active {
color: $color-700;
}
}
body {
......
......@@ -20,7 +20,6 @@
display: flex;
height: calc(100vh - #{$header-height});
margin-top: 40px;
color: $almost-black;
border-top: 1px solid $white-dark;
border-bottom: 1px solid $white-dark;
......@@ -43,7 +42,11 @@
cursor: pointer;
&.file-open {
background: $white-normal;
background: $link-active-background;
}
&.file-active {
font-weight: $gl-font-weight-bold;
}
.ide-file-name {
......@@ -72,7 +75,10 @@
margin-right: -8px;
}
&:hover {
&:hover,
&:focus {
background: $link-active-background;
.ide-new-btn {
display: block;
}
......@@ -720,9 +726,7 @@
}
.ide-view {
height: calc(
100vh - #{$header-height + $performance-bar-height + $flash-height}
);
height: calc(100vh - #{$header-height + $performance-bar-height + $flash-height});
}
}
}
......
......@@ -21,10 +21,7 @@ class Projects::BranchesController < Projects::ApplicationController
fetch_branches_by_mode
@refs_pipelines = @project.pipelines.latest_successful_for_refs(@branches.map(&:name))
@merged_branch_names =
repository.merged_branch_names(@branches.map(&:name))
# n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37429
Gitlab::GitalyClient.allow_n_plus_1_calls do
@merged_branch_names = repository.merged_branch_names(@branches.map(&:name))
@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
......@@ -32,7 +29,6 @@ class Projects::BranchesController < Projects::ApplicationController
render
end
end
format.json do
branches = BranchesFinder.new(@repository, params).execute
branches = Kaminari.paginate_array(branches).page(params[:page])
......
......@@ -43,9 +43,14 @@ class Projects::MilestonesController < Projects::ApplicationController
def show
@project_namespace = @project.namespace.becomes(Namespace)
<<<<<<< HEAD
if @project.feature_available?(:burndown_charts, current_user) &&
@project.feature_available?(:issue_weights, current_user)
@burndown = Burndown.new(@milestone)
=======
respond_to do |format|
format.html
>>>>>>> upstream/master
end
end
......
......@@ -33,7 +33,7 @@ module NamespacesHelper
def namespace_icon(namespace, size = 40)
if namespace.is_a?(Group)
group_icon(namespace)
group_icon_url(namespace)
else
avatar_icon_for_user(namespace.owner, size)
end
......
......@@ -40,7 +40,11 @@ module PageLayoutHelper
def favicon
return 'favicon-yellow.ico' if Gitlab::Utils.to_boolean(ENV['CANARY'])
<<<<<<< HEAD
return 'favicon-green.ico' if Rails.env.development?
=======
return 'favicon-blue.ico' if Rails.env.development?
>>>>>>> upstream/master
'favicon.ico'
end
......
......@@ -4,7 +4,10 @@ module Ci
include Gitlab::SQL::Pattern
include RedisCacheable
include ChronicDurationAttribute
<<<<<<< HEAD
prepend EE::Ci::Runner
=======
>>>>>>> upstream/master
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
ONLINE_CONTACT_TIMEOUT = 1.hour
......
......@@ -27,6 +27,10 @@ class DeployKey < Key
self.private?
end
def user
super || User.ghost
end
def has_access_to?(project)
deploy_keys_project_for(project).present?
end
......
......@@ -17,32 +17,4 @@ class RedirectRoute < ActiveRecord::Base
where(wheres, path, "#{sanitize_sql_like(path)}/%")
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
......@@ -256,13 +256,13 @@ class Repository
end
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
# Rugged seems to throw a `ReferenceError` when given branch_names rather
# than SHA-1 hashes
number_commits_behind, number_commits_ahead =
raw_repository.count_commits_between(
root_ref_hash,
@root_ref_hash,
branch.dereferenced_target.sha,
left_right: true,
max_count: MAX_DIVERGING_COUNT)
......
......@@ -10,8 +10,6 @@ class Route < ActiveRecord::Base
presence: true,
uniqueness: { case_sensitive: false }
validate :ensure_permanent_paths, if: :path_changed?
before_validation :delete_conflicting_orphaned_routes
after_create :delete_conflicting_redirects
after_update :delete_conflicting_redirects, if: :path_changed?
......@@ -45,7 +43,7 @@ class Route < ActiveRecord::Base
# We are not calling route.delete_conflicting_redirects here, in hopes
# of avoiding deadlocks. The parent (self, in this method) already
# 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
......@@ -55,31 +53,17 @@ class Route < ActiveRecord::Base
end
def conflicting_redirects
RedirectRoute.temporary.matching_path_and_descendants(path)
RedirectRoute.matching_path_and_descendants(path)
end
def create_redirect(path, permanent: false)
RedirectRoute.create(source: source, path: path, permanent: permanent)
def create_redirect(path)
RedirectRoute.create(source: source, path: path)
end
private
def create_redirect_for_old_path
create_redirect(path_was, permanent: permanent_redirect?) 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?
create_redirect(path_was) if path_changed?
end
def delete_conflicting_orphaned_routes
......
......@@ -278,6 +278,7 @@ class Service < ActiveRecord::Base
def self.build_from_template(project_id, template)
service = template.dup
service.active = false unless service.valid?
service.template = false
service.project_id = project_id
service
......
......@@ -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
# Profile
has_many :keys, -> do
type = Key.arel_table[:type]
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 :keys, -> { where(type: ['Key', nil]) }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :nullify # rubocop:disable Cop/ActiveRecordDependent
has_many :gpg_keys
has_many :emails, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
......
......@@ -92,9 +92,6 @@ module Projects
unless @project.gitlab_project_import?
@project.write_repository_config
@project.create_wiki unless skip_wiki?
create_services_from_active_templates(@project)
@project.create_labels
end
event_service.create_project(@project, current_user)
......@@ -123,21 +120,29 @@ module Projects
Project.transaction do
@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
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
def fail(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.mark_import_as_failed(message)
@project.mark_import_as_failed(message) if @project.import?
end
@project
......
......@@ -43,8 +43,11 @@
%span.toggle-icon
= 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')
<<<<<<< HEAD
-# EE-specific start
= render 'ci/variables/environment_scope', form_field: form_field, variable: variable
-# EE-specific end
=======
>>>>>>> upstream/master
%button.js-row-remove-button.ci-variable-row-remove-button{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
= icon('minus-circle')
......@@ -22,10 +22,13 @@
= 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'
<<<<<<< HEAD
-# EE-specific start
= hidden_field_tag :ci_cd_only, params[:ci_cd_only]
-# EE-specific end
=======
>>>>>>> upstream/master
- unless github_import_configured?
%hr
%p
......
......@@ -80,11 +80,14 @@
= link_to charts_project_graph_path(@project, current_ref) do
#{ _('Charts') }
<<<<<<< HEAD
- if @project.feature_available?(:file_locks)
= nav_link(controller: [:path_locks]) do
= link_to project_path_locks_path(@project) do
#{ _('Locked Files') }
=======
>>>>>>> upstream/master
- if project_nav_tab? :issues
= nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do
= link_to project_issues_path(@project), class: 'shortcuts-issues' do
......@@ -239,11 +242,14 @@
= sprite_icon('disk')
%span.nav-item-name
Registry
<<<<<<< HEAD
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: %w[projects/registry/repositories], html_options: { class: "fly-out-top-item" } ) do
= link_to project_container_registry_index_path(@project) do
%strong.fly-out-top-item-name
#{ _('Registry') }
=======
>>>>>>> upstream/master
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
......
......@@ -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 }
%p
= _('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
- if ci_cd_projects_available?
%p
= _('To only use CI/CD features for an external repository, choose <strong>CI/CD for external repo</strong>.').html_safe
-# EE-specific end
=======
>>>>>>> upstream/master
.md
= brand_new_project_guidelines
%p
......@@ -46,9 +49,12 @@
%a{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab' }, role: 'tab' }
%span.hidden-xs Import project
%span.visible-xs Import
<<<<<<< HEAD
-# EE-specific start
= render 'new_ci_cd_only_project_tab', active_tab: active_tab
-# EE-specific end
=======
>>>>>>> upstream/master
.tab-content.gitlab-tab-content
.tab-pane{ id: 'blank-project-pane', class: active_when(active_tab == 'blank'), role: 'tabpanel' }
......@@ -114,11 +120,14 @@
= render "shared/import_form", f: f
= render 'new_project_fields', f: f, project_name_id: "import-url-name"
<<<<<<< HEAD
-# EE-specific start
= render 'new_ci_cd_only_project_pane', active_tab: active_tab
-# EE-specific end
=======
>>>>>>> upstream/master
.save-project-loader.hide
.center
%h2
......
......@@ -17,6 +17,7 @@
= import_will_timeout_message(ci_cd_only)
%li
= import_svn_message(ci_cd_only)
<<<<<<< HEAD
%li
The Git LFS objects will be ignored.
- unless ci_cd_only
......@@ -27,3 +28,5 @@
-# EE-specific start
= render 'shared/ee/import_form', f: f unless ci_cd_only
-# 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 = {
},
},
{
test: /katex.css$/,
test: /katex.min.css$/,
include: /node_modules\/katex\/dist/,
use: [
{ 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
t.string "path", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "permanent"
end
add_index "redirect_routes", ["path"], name: "index_redirect_routes_on_path", unique: true, using: :btree
......
......@@ -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-8047]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8047
[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
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
>**Note:** This endpoint was introduced in GitLab 9.3.
......
......@@ -15,13 +15,8 @@ codequality:
services:
- docker:dind
script:
- docker pull codeclimate/codeclimate
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- 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
- 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
artifacts:
paths: [codeclimate.json]
```
......
......@@ -111,7 +111,7 @@ We also use two secure variables:
## Storing API keys
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.
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
......
......@@ -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
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/`
When you're testing EE-only features, avoid adding examples to the
......
# 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
- [Install on AWS](https://about.gitlab.com/aws/)
- _Testing only!_ [DigitalOcean and Docker Machine](digitaloceandocker.md) -
Quickly test any version of GitLab on DigitalOcean using Docker Machine.
<<<<<<< HEAD
- [GitLab Pivotal Tile](pivotal/index.md) - Install and configure GitLab
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](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.
## Database
......
......@@ -142,11 +142,9 @@ and Git push/pull redirects.
Depending on the situation, different things apply.
When [renaming a user](../profile/index.md#changing-your-username) or
[changing a group path](../group/index.md#changing-a-group-s-path):
When [renaming a user](../profile/index.md#changing-your-username),
[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
redirect to the new URLs.
- 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
your remote will be displayed instead of rejecting your action.
This means that any automation scripts, or Git clients will continue to
work after a rename, making any transition a lot smoother.
To avoid pulling from or pushing to an entirely incorrect repository, the old
path will be reserved.
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.
- The redirects will be available as long as the original path is not claimed by
another group, user or project.
......@@ -236,6 +236,20 @@ module API
render_api_error!("Failed to save note #{note.errors.messages}", 400)
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
......@@ -54,7 +54,7 @@ module API
present key, with: Entities::DeployKeysProject
end
desc 'Add new deploy key to currently authenticated user' do
desc 'Add new deploy key to a project' do
success Entities::DeployKeysProject
end
params do
......@@ -66,33 +66,32 @@ module API
params[:key].strip!
# 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)
.find_by(keys: { key: params[:key] })
if key
present key, with: Entities::DeployKeysProject
if deploy_key_project
present deploy_key_project, with: Entities::DeployKeysProject
break
end
# Check for available deploy keys in other projects
key = current_user.accessible_deploy_keys.find_by(key: params[: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
end
# Create a new deploy key
key_attributes = { can_push: !!params[:can_push],
deploy_key_attributes: declared_params.except(:can_push) }
key = add_deploy_keys_project(user_project, key_attributes)
deploy_key_attributes = declared_params.except(:can_push).merge(user: current_user)
deploy_key_project = add_deploy_keys_project(user_project, deploy_key_attributes: deploy_key_attributes, can_push: !!params[:can_push])
if key.valid?
present key, with: Entities::DeployKeysProject
if deploy_key_project.valid?
present deploy_key_project, with: Entities::DeployKeysProject
else
render_validation_error!(key)
render_validation_error!(deploy_key_project)
end
end
......
......@@ -105,8 +105,12 @@ module Banzai
end
end
options = link_options.merge(href: match)
content_tag(:a, match.html_safe, options) + dropped
# match has come from node.to_html above, so we know it's encoded
# correctly.
html_safe_match = match.html_safe
options = link_options.merge(href: html_safe_match)
content_tag(:a, html_safe_match, options) + dropped
end
def autolink_filter(text)
......
......@@ -12,6 +12,7 @@ module Gitlab
class Build < ActiveRecord::Base
self.table_name = 'ci_builds'
self.inheritance_column = :_type_disabled
def ensure_stage!(attempts: 2)
find_stage || create_stage!
......
......@@ -9,20 +9,16 @@ module Gitlab
super(project, user, protocol)
end
def message(rejected: false)
def message
<<~MESSAGE
Project '#{redirected_path}' was moved to '#{project.full_path}'.
Please update your Git remote:
#{remote_url_message(rejected)}
git remote set-url origin #{url_to_repo}
MESSAGE
end
def permanent_redirect?
RedirectRoute.permanent.exists?(path: redirected_path)
end
private
attr_reader :redirected_path
......@@ -30,18 +26,6 @@ module Gitlab
def self.message_key(user_id, project_id)
"#{REDIRECT_NAMESPACE}:#{user_id}:#{project_id}"
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
......@@ -900,11 +900,42 @@ into similar problems in the future (e.g. when new tables are created).
end
end
# Rails' index_exists? doesn't work when you only give it a table and index
# name. As such we have to use some extra code to check if an index exists for
# a given name.
# Fetches indexes on a column by name for postgres.
#
# 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)
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
......
......@@ -56,7 +56,7 @@ module Gitlab
ensure_project_on_push!(cmd, changes)
check_project_accessibility!
check_project_moved!
add_project_moved_message!
check_repository_existence!
case cmd
......@@ -102,8 +102,6 @@ module Gitlab
end
def check_active_user!
return if deploy_key?
if user && !user_access.allowed?
raise UnauthorizedError, ERROR_MESSAGES[:account_blocked]
end
......@@ -128,16 +126,12 @@ module Gitlab
end
end
def check_project_moved!
def add_project_moved_message!
return if redirected_path.nil?
project_moved = Checks::ProjectMoved.new(project, user, protocol, redirected_path)
if project_moved.permanent_redirect?
project_moved.add_message
else
raise ProjectMovedError, project_moved.message(rejected: true)
end
end
def check_command_disabled!(cmd)
......@@ -223,7 +217,7 @@ module Gitlab
raise UnauthorizedError, ERROR_MESSAGES[:read_only]
end
if deploy_key
if deploy_key?
unless deploy_key.can_push_to?(project)
raise UnauthorizedError, ERROR_MESSAGES[:deploy_key_upload]
end
......@@ -332,8 +326,10 @@ module Gitlab
case actor
when User
actor
when DeployKey
nil
when Key
actor.user unless actor.is_a?(DeployKey)
actor.user
when :ci
nil
end
......
......@@ -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/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/20180113220114_rework_redirect_routes_indexes.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
AddUsersLowerUsernameEmailIndexes.new.up
......@@ -17,8 +17,8 @@ task setup_postgresql: :environment do
AddLowerPathIndexToRedirectRoutes.new.up
IndexRedirectRoutesPathForLike.new.up
AddIndexOnNamespacesLowerName.new.up
ReworkRedirectRoutesIndexes.new.up
UsersNameLowerIndex.new.up
AddPathIndexToRedirectRoutes.new.up
end
desc 'GitLab | Generate PostgreSQL Password Hash'
......
......@@ -20,14 +20,23 @@ describe Projects::MilestonesController do
describe "#show" do
render_views
def view_milestone
get :show, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid
def view_milestone(options = {})
params = { namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid }
get :show, params.merge(options)
end
it 'shows milestone page' do
view_milestone
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
......
......@@ -2,14 +2,5 @@ FactoryBot.define do
factory :redirect_route do
sequence(:path) { |n| "redirect#{n}" }
source factory: :group
permanent false
trait :permanent do
permanent true
end
trait :temporary do
permanent false
end
end
end
......@@ -19,7 +19,11 @@ feature 'Group activity page' do
it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
end
<<<<<<< HEAD
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) }
before do
......
......@@ -68,7 +68,11 @@ feature 'Group issues page' do
it 'does not render issue' do
visit path
<<<<<<< HEAD
expect(page).not_to have_content issue.title
=======
expect(page).not_to have_content issue.title[0..80]
>>>>>>> upstream/master
end
end
end
......
......@@ -30,6 +30,7 @@ feature 'Group show page' do
end
it_behaves_like "an autodiscoverable RSS feed without an RSS token"
<<<<<<< HEAD
end
context 'when group has a public project', :js do
......@@ -40,6 +41,29 @@ feature 'Group show page' do
expect(page).to have_link group.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
......
......@@ -55,6 +55,11 @@ describe PageLayoutHelper do
stub_env('CANARY', 'true')
expect(helper.favicon).to eq 'favicon-yellow.ico'
end
it 'has yellow favicon for canary' do
stub_env('CANARY', 'true')
expect(helper.favicon).to eq 'favicon-yellow.ico'
end
end
describe 'page_image' do
......
......@@ -17,9 +17,12 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
end
before do
<<<<<<< HEAD
# EE-specific start
stub_licensed_features(variable_environment_scope: true)
# EE specific end
=======
>>>>>>> upstream/master
project.add_master(admin)
sign_in(admin)
end
......
......@@ -25,26 +25,34 @@ describe('issue_discussion component', () => {
});
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', () => {
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);
});
describe('actions', () => {
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();
Vue.nextTick(() => {
expect(vm.$refs.noteForm).toBeDefined();
expect(vm.$refs.noteForm).not.toBeNull();
expect(vm.isReplying).toEqual(true);
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
expect(actual).to eq(expected_complicated_link)
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
doc = filter("See &lt;&lt;&lt;#{link}&gt;&gt;&gt;")
......
......@@ -51,4 +51,20 @@ describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 201
expect { described_class.new.perform(1, 6) }
.to raise_error ActiveRecord::RecordNotUnique
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
......@@ -44,44 +44,17 @@ describe Gitlab::Checks::ProjectMoved, :clean_gitlab_redis_shared_state do
end
describe '#message' do
context 'when the push is rejected' do
it 'returns a redirect message telling the user to try again' do
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} and try again.\n"
expect(project_moved.message(rejected: true)).to eq(message)
end
end
context 'when the push is not rejected' do
it 'returns a redirect message' do
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"
message = <<~MSG
Project 'foo/bar' was moved to '#{project.full_path}'.
expect(project_moved.message).to eq(message)
end
end
end
Please update your Git remote:
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
git remote set-url origin #{project.http_url_to_repo}
MSG
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
expect(project_moved.message).to eq(message)
end
end
end
......@@ -1211,4 +1211,33 @@ describe Gitlab::Database::MigrationHelpers do
expect(model.perform_background_migration_inline?).to eq(false)
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
......@@ -240,19 +240,12 @@ describe Gitlab::GitAccess do
end
shared_examples 'check_project_moved' do
it 'enqueues a redirected message' do
it 'enqueues a redirected message for pushing' do
push_access_check
expect(Gitlab::Checks::ProjectMoved.fetch_message(user.id, project.id)).not_to be_nil
end
end
describe '#check_project_moved!', :clean_gitlab_redis_shared_state do
before do
project.add_master(user)
end
context 'when a redirect was not followed to find the project' do
it 'allows push and pull access' do
aggregate_failures do
expect { push_access_check }.not_to raise_error
......@@ -261,61 +254,31 @@ describe Gitlab::GitAccess do
end
end
context 'when a permanent redirect and ssh protocol' do
let(:redirected_path) { 'some/other-path' }
describe '#add_project_moved_message!', :clean_gitlab_redis_shared_state do
before do
allow_any_instance_of(Gitlab::Checks::ProjectMoved).to receive(:permanent_redirect?).and_return(true)
project.add_master(user)
end
context 'when a redirect was not followed to find the project' do
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
it_behaves_like 'check_project_moved'
end
context 'with a permanent redirect and http protocol' do
context 'with a redirect and ssh protocol' do
let(:redirected_path) { 'some/other-path' }
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'
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
context 'with a 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
it_behaves_like 'check_project_moved'
end
end
......
......@@ -17,4 +17,25 @@ describe DeployKey, :mailer do
should_not_email(user)
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
......@@ -16,66 +16,6 @@ describe Route do
it { is_expected.to validate_presence_of(:source) }
it { is_expected.to validate_presence_of(:path) }
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
describe 'callbacks' do
......@@ -211,34 +151,23 @@ describe Route do
end
context 'when the source is a Project' do
it 'creates a temporal RedirectRoute' do
it 'creates a RedirectRoute' do
project = create(:project)
route = project.route
redirect_route = route.create_redirect('foo')
expect(redirect_route.permanent?).to be_falsy
expect(redirect_route).not_to be_nil
end
end
context 'when the source is not a project' do
it 'creates a permanent RedirectRoute' do
redirect_route = route.create_redirect('foo', permanent: true)
expect(redirect_route.permanent?).to be_truthy
it 'creates a RedirectRoute' do
redirect_route = route.create_redirect('foo')
expect(redirect_route).not_to be_nil
end
end
end
describe '#delete_conflicting_redirects' do
context 'with permanent redirect' do
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
......@@ -248,7 +177,6 @@ describe Route do
route.delete_conflicting_redirects
end.to change { RedirectRoute.count }.by(-1)
end
end
context 'when a redirect route with the same path exists' do
context 'when the redirect route has matching case' do
......@@ -289,32 +217,19 @@ describe Route do
end
describe '#conflicting_redirects' do
let(:route) { create(:project).route }
it 'returns an ActiveRecord::Relation' do
expect(route.conflicting_redirects).to be_an(ActiveRecord::Relation)
end
context 'with permanent redirects' do
it 'does not return anything' do
route.create_redirect("#{route.path}/foo", permanent: true)
route.create_redirect("#{route.path}/foo/bar", permanent: true)
route.create_redirect("#{route.path}/baz/quz", permanent: true)
expect(route.conflicting_redirects).to be_empty
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
context 'when a redirect route with the same path exists' do
let(:route) { create(:project).route }
......@@ -348,44 +263,6 @@ describe Route do
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
context 'when there is a conflicting route' do
let!(:conflicting_group) { create(:group, path: 'foo') }
......
......@@ -62,6 +62,21 @@ describe Service do
end
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
let!(:service_template) do
PushoverService.create(
......
......@@ -29,7 +29,7 @@ describe User do
it { is_expected.to have_many(:group_members) }
it { is_expected.to have_many(:groups) }
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(:issues).dependent(:destroy) }
it { is_expected.to have_many(:notes).dependent(:destroy) }
......@@ -135,23 +135,6 @@ describe User do
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
let(:username) { 'foo' }
let!(:other_user) { create(:user, username: username) }
......@@ -2802,27 +2785,19 @@ describe User do
end
end
describe "#username_previously_taken?" do
let(:user1) { create(:user, username: 'foo') }
context 'changing a username' do
let(:user) { create(:user, username: 'foo') }
context 'when the username has been taken before' do
before do
user1.username = 'bar'
user1.save!
it 'creates a redirect route' do
expect { user.update!(username: 'bar') }
.to change { RedirectRoute.where(path: 'foo').count }.by(1)
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
it 'deletes the redirect when a user with the old username was created' do
user.update!(username: 'bar')
context 'when the username has not been taken before' do
it 'should be valid' do
expect(RedirectRoute.count).to eq(0)
user2 = build(:user, username: 'baz')
expect(user2).to be_valid
end
expect { create(:user, username: 'foo') }
.to change { RedirectRoute.where(path: 'foo').count }.by(-1)
end
end
end
......@@ -1149,4 +1149,33 @@ describe API::Commits do
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
......@@ -91,6 +91,10 @@ describe API::DeployKeys do
expect do
post api("/projects/#{project.id}/deploy_keys", admin), key_attrs
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
it 'returns an existing ssh key when attempting to add a duplicate' do
......
......@@ -344,20 +344,11 @@ describe 'Git HTTP requests' do
context 'and the user requests a redirected path' do
let!(:redirect) { project.route.create_redirect('foo/bar') }
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:
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
it 'downloads get status 200 for redirects' do
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
......@@ -559,20 +550,19 @@ describe 'Git HTTP requests' do
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
end
it 'downloads get status 404 with "project was moved" message' do
it 'downloads get status 200' do
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
it 'uploads get status 404 with "project was moved" message' do
upload(path, env) do |response|
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
......
......@@ -29,8 +29,13 @@ describe Ci::RetryBuildService do
commit_id deployments erased_by_id last_deployment project_id
runner_id tag_taggings taggings tags trigger_request_id
user_id auto_canceled_by_id retried failure_reason
<<<<<<< HEAD
sourced_pipelines artifacts_file_store artifacts_metadata_store
metadata].freeze # EE
=======
artifacts_file_store artifacts_metadata_store
metadata].freeze
>>>>>>> upstream/master
shared_examples 'build duplication' do
let(:another_pipeline) { create(:ci_empty_pipeline, project: project) }
......
......@@ -222,8 +222,8 @@ describe Groups::TransferService, :postgresql do
expect(new_parent_group.children.first).to eq(group)
end
it 'should create a permanent redirect for the group' do
expect(group.redirect_routes.permanent.count).to eq(1)
it 'should create a redirect for the group' do
expect(group.redirect_routes.count).to eq(1)
end
end
......@@ -243,10 +243,10 @@ describe Groups::TransferService, :postgresql do
end
end
it 'should create permanent redirects for the subgroups' do
expect(group.redirect_routes.permanent.count).to eq(1)
expect(subgroup1.redirect_routes.permanent.count).to eq(1)
expect(subgroup2.redirect_routes.permanent.count).to eq(1)
it 'should create redirects for the subgroups' do
expect(group.redirect_routes.count).to eq(1)
expect(subgroup1.redirect_routes.count).to eq(1)
expect(subgroup2.redirect_routes.count).to eq(1)
end
context 'when the new parent has a higher visibility than the children' do
......@@ -287,9 +287,9 @@ describe Groups::TransferService, :postgresql do
end
it 'should create permanent redirects for the projects' do
expect(group.redirect_routes.permanent.count).to eq(1)
expect(project1.redirect_routes.permanent.count).to eq(1)
expect(project2.redirect_routes.permanent.count).to eq(1)
expect(group.redirect_routes.count).to eq(1)
expect(project1.redirect_routes.count).to eq(1)
expect(project2.redirect_routes.count).to eq(1)
end
context 'when the new parent has a higher visibility than the projects' do
......@@ -338,12 +338,12 @@ describe Groups::TransferService, :postgresql do
end
end
it 'should create permanent redirect for the subgroups and projects' do
expect(group.redirect_routes.permanent.count).to eq(1)
expect(subgroup1.redirect_routes.permanent.count).to eq(1)
expect(subgroup2.redirect_routes.permanent.count).to eq(1)
expect(project1.redirect_routes.permanent.count).to eq(1)
expect(project2.redirect_routes.permanent.count).to eq(1)
it 'should create redirect for the subgroups and projects' do
expect(group.redirect_routes.count).to eq(1)
expect(subgroup1.redirect_routes.count).to eq(1)
expect(subgroup2.redirect_routes.count).to eq(1)
expect(project1.redirect_routes.count).to eq(1)
expect(project2.redirect_routes.count).to eq(1)
end
end
......@@ -380,12 +380,12 @@ describe Groups::TransferService, :postgresql do
end
end
it 'should create permanent redirect for the subgroups and projects' do
expect(group.redirect_routes.permanent.count).to eq(1)
expect(project1.redirect_routes.permanent.count).to eq(1)
expect(subgroup1.redirect_routes.permanent.count).to eq(1)
expect(nested_subgroup.redirect_routes.permanent.count).to eq(1)
expect(nested_project.redirect_routes.permanent.count).to eq(1)
it 'should create redirect for the subgroups and projects' do
expect(group.redirect_routes.count).to eq(1)
expect(project1.redirect_routes.count).to eq(1)
expect(subgroup1.redirect_routes.count).to eq(1)
expect(nested_subgroup.redirect_routes.count).to eq(1)
expect(nested_project.redirect_routes.count).to eq(1)
end
end
......
......@@ -70,6 +70,16 @@ describe Projects::CreateService, '#execute' do
opts[:default_branch] = 'master'
expect(create_project(user, opts)).to eq(nil)
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
context 'wiki_enabled creates repository directory' do
......@@ -232,14 +242,15 @@ describe Projects::CreateService, '#execute' do
end
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'
create(:service, type: 'DroneCiService', project: nil, template: true, active: true)
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.services.count).to eq 0
expect(project).to be_persisted
expect(service.active).to be false
end
end
......
......@@ -2,7 +2,10 @@ module MigrationsHelpers
prepend EE::MigrationsHelpers
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
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