summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml4
-rw-r--r--CHANGELOG5
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock10
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss9
-rw-r--r--app/controllers/admin/groups_controller.rb2
-rw-r--r--app/controllers/concerns/filter_projects.rb15
-rw-r--r--app/controllers/dashboard/projects_controller.rb24
-rw-r--r--app/controllers/explore/projects_controller.rb11
-rw-r--r--app/controllers/groups_controller.rb6
-rw-r--r--app/finders/snippets_finder.rb6
-rw-r--r--app/helpers/commits_helper.rb11
-rw-r--r--app/helpers/explore_helper.rb12
-rw-r--r--app/helpers/snippets_helper.rb10
-rw-r--r--app/helpers/sorting_helper.rb10
-rw-r--r--app/models/merge_request_diff.rb2
-rw-r--r--app/models/personal_snippet.rb1
-rw-r--r--app/models/project_snippet.rb3
-rw-r--r--app/models/repository.rb55
-rw-r--r--app/models/snippet.rb7
-rw-r--r--app/services/git_push_service.rb18
-rw-r--r--app/services/git_tag_push_service.rb2
-rw-r--r--app/services/merge_requests/post_merge_service.rb4
-rw-r--r--app/views/dashboard/_projects_head.html.haml2
-rw-r--r--app/views/explore/projects/_dropdown.html.haml20
-rw-r--r--app/views/explore/projects/_filter.html.haml10
-rw-r--r--app/views/groups/_projects.html.haml9
-rw-r--r--app/views/projects/branches/destroy.js.haml2
-rw-r--r--app/views/projects/commits/_commit_list.html.haml11
-rw-r--r--app/views/projects/commits/_commits.html.haml8
-rw-r--r--app/views/projects/commits/_head.html.haml4
-rw-r--r--app/views/shared/projects/_dropdown.html.haml22
-rw-r--r--app/views/shared/projects/_project.html.haml3
-rw-r--r--config/environments/test.rb2
-rw-r--r--db/migrate/20160229193553_add_main_language_to_repository.rb5
-rw-r--r--db/migrate/20160305220806_remove_expires_at_from_snippets.rb5
-rw-r--r--db/schema.rb5
-rw-r--r--doc/api/commits.md3
-rw-r--r--doc/api/notes.md1
-rw-r--r--doc/api/project_snippets.md1
-rw-r--r--doc/api/users.md2
-rw-r--r--doc/ci/api/README.md88
-rw-r--r--doc/ci/api/builds.md118
-rw-r--r--doc/ci/api/commits.md108
-rw-r--r--doc/ci/api/projects.md149
-rw-r--r--doc/ci/api/runners.md71
-rw-r--r--doc/development/README.md1
-rw-r--r--doc/development/sql.md219
-rw-r--r--doc/hooks/custom_hooks.md2
-rw-r--r--doc/integration/slack.md12
-rw-r--r--doc/update/upgrader.md2
-rw-r--r--doc/web_hooks/web_hooks.md1
-rw-r--r--features/dashboard/archived_projects.feature5
-rw-r--r--features/explore/projects.feature2
-rw-r--r--features/steps/dashboard/archived_projects.rb4
-rw-r--r--features/support/env.rb1
-rw-r--r--features/support/rerun.rb14
-rw-r--r--lib/api/entities.rb4
-rw-r--r--lib/gitlab/bitbucket_import/importer.rb2
-rw-r--r--lib/tasks/spinach.rake62
-rwxr-xr-xscripts/prepare_build.sh26
-rw-r--r--spec/controllers/projects/forks_controller_spec.rb2
-rw-r--r--spec/factories/personal_snippets.rb11
-rw-r--r--spec/factories/project_snippets.rb6
-rw-r--r--spec/factories/snippets.rb12
-rw-r--r--spec/finders/snippets_finder_spec.rb25
-rw-r--r--spec/helpers/visibility_level_helper_spec.rb6
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb88
-rw-r--r--spec/models/project_snippet_spec.rb1
-rw-r--r--spec/models/project_spec.rb2
-rw-r--r--spec/models/repository_spec.rb98
-rw-r--r--spec/models/snippet_spec.rb1
-rw-r--r--spec/requests/api/users_spec.rb2
-rw-r--r--spec/services/delete_tag_service_spec.rb26
-rw-r--r--spec/services/git_push_service_spec.rb17
-rw-r--r--spec/services/projects/update_service_spec.rb4
-rw-r--r--spec/spec_helper.rb4
77 files changed, 874 insertions, 668 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c477721f9da..ffefeb6dfd8 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -12,6 +12,8 @@ cache:
variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1"
+ # retry tests only in CI environment
+ RSPEC_RETRY_RETRY_COUNT: "3"
before_script:
- source ./scripts/prepare_build.sh
@@ -21,7 +23,7 @@ before_script:
- cp config/gitlab.yml.example config/gitlab.yml
- touch log/application.log
- touch log/test.log
- - bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"
+ - bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"
- RAILS_ENV=test bundle exec rake db:drop db:create db:schema:load db:migrate
stages:
diff --git a/CHANGELOG b/CHANGELOG
index a9742d22015..10984ebd190 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -9,15 +9,20 @@ v 8.6.0 (unreleased)
- Fix issue when pushing to projects ending in .wiki
- Fix avatar stretching by providing a cropping feature (Johann Pardanaud)
- Don't load all of GitLab in mail_room
+ - Memoize @group in Admin::GroupsController (Yatish Mehta)
- Indicate how much an MR diverged from the target branch (Pierre de La Morinerie)
- Strip leading and trailing spaces in URL validator (evuez)
+ - Add "last_sign_in_at" and "confirmed_at" to GET /users/* API endpoints for admins (evuez)
- Return empty array instead of 404 when commit has no statuses in commit status API
- Add support for cross-project label references
- Update documentation to reflect Guest role not being enforced on internal projects
- Allow search for logged out users
+ - Fix bug where Bitbucket `closed` issues were imported as `opened` (Iuri de Silvio)
- Don't show Issues/MRs from archived projects in Groups view
- Increase the notes polling timeout over time (Roberto Dip)
- Show labels in dashboard and group milestone views
+ - Add main language of a project in the list of projects (Tiago Botelho)
+ - Add ability to show archived projects on dashboard, explore and group pages
v 8.5.4
- Do not cache requests for badges (including builds badge)
diff --git a/Gemfile b/Gemfile
index c66ef3cffad..7e70761a77a 100644
--- a/Gemfile
+++ b/Gemfile
@@ -263,7 +263,9 @@ group :development, :test do
gem 'database_cleaner', '~> 1.4.0'
gem 'factory_girl_rails', '~> 4.6.0'
gem 'rspec-rails', '~> 3.3.0'
+ gem 'rspec-retry'
gem 'spinach-rails', '~> 0.2.1'
+ gem 'spinach-rerun-reporter', '~> 0.0.2'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.7.0'
@@ -273,7 +275,7 @@ group :development, :test do
gem 'capybara', '~> 2.4.0'
gem 'capybara-screenshot', '~> 1.0.0'
- gem 'poltergeist', '~> 1.8.1'
+ gem 'poltergeist', '~> 1.9.0'
gem 'teaspoon', '~> 1.0.0'
gem 'teaspoon-jasmine', '~> 2.2.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 22c86e4ae8f..432bdc344ad 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -552,7 +552,7 @@ GEM
parser (2.2.3.0)
ast (>= 1.1, < 3.0)
pg (0.18.4)
- poltergeist (1.8.1)
+ poltergeist (1.9.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
multi_json (~> 1.0)
@@ -679,6 +679,8 @@ GEM
rspec-expectations (~> 3.3.0)
rspec-mocks (~> 3.3.0)
rspec-support (~> 3.3.0)
+ rspec-retry (0.4.5)
+ rspec-core
rspec-support (3.3.0)
rubocop (0.35.1)
astrolabe (~> 1.3)
@@ -764,6 +766,8 @@ GEM
capybara (>= 2.0.0)
railties (>= 3)
spinach (>= 0.4)
+ spinach-rerun-reporter (0.0.2)
+ spinach (~> 0.8)
spring (1.6.4)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
@@ -978,7 +982,7 @@ DEPENDENCIES
org-ruby (~> 0.9.12)
paranoia (~> 2.0)
pg (~> 0.18.2)
- poltergeist (~> 1.8.1)
+ poltergeist (~> 1.9.0)
pry-rails
quiet_assets (~> 1.0.2)
rack-attack (~> 4.3.1)
@@ -999,6 +1003,7 @@ DEPENDENCIES
rouge (~> 1.10.1)
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.3.0)
+ rspec-retry
rubocop (~> 0.35.0)
ruby-fogbugz (~> 0.2.1)
sanitize (~> 2.0)
@@ -1017,6 +1022,7 @@ DEPENDENCIES
six (~> 0.2.0)
slack-notifier (~> 1.2.0)
spinach-rails (~> 0.2.1)
+ spinach-rerun-reporter (~> 0.0.2)
spring (~> 1.6.4)
spring-commands-rspec (~> 1.0.4)
spring-commands-spinach (~> 1.0.0)
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index d4878b333f9..3dc524ccca4 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -81,8 +81,9 @@
&::before {
content: "\f00c";
position: absolute;
- left: 4px;
- top: 8px;
+ left: 5px;
+ top: 50%;
+ margin-top: -7px;
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
text-rendering: auto;
@@ -94,8 +95,8 @@
}
.dropdown-header {
- padding-left: 10px;
- padding-right: 10px;
+ padding-left: 5px;
+ padding-right: 5px;
color: $dropdown-header-color;
font-size: 13px;
line-height: 22px;
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 4d3e48f7f81..668396a0f20 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -55,7 +55,7 @@ class Admin::GroupsController < Admin::ApplicationController
private
def group
- @group = Group.find_by(path: params[:id])
+ @group ||= Group.find_by(path: params[:id])
end
def group_params
diff --git a/app/controllers/concerns/filter_projects.rb b/app/controllers/concerns/filter_projects.rb
new file mode 100644
index 00000000000..f63b703d101
--- /dev/null
+++ b/app/controllers/concerns/filter_projects.rb
@@ -0,0 +1,15 @@
+# == FilterProjects
+#
+# Controller concern to handle projects filtering
+# * by name
+# * by archived state
+#
+module FilterProjects
+ extend ActiveSupport::Concern
+
+ def filter_projects(projects)
+ projects = projects.search(params[:filter_projects]) if params[:filter_projects].present?
+ projects = projects.non_archived if params[:archived].blank?
+ projects
+ end
+end
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index dc880b634e5..fc51c3241af 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -1,18 +1,15 @@
class Dashboard::ProjectsController < Dashboard::ApplicationController
+ include FilterProjects
+
before_action :event_filter
def index
- @projects = current_user.authorized_projects.sorted_by_activity.non_archived
- @projects = @projects.sort(@sort = params[:sort])
+ @projects = current_user.authorized_projects.sorted_by_activity
+ @projects = filter_projects(@projects)
@projects = @projects.includes(:namespace)
+ @projects = @projects.sort(@sort = params[:sort])
+ @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
- terms = params[:filter_projects]
-
- if terms.present?
- @projects = @projects.search(terms)
- end
-
- @projects = @projects.page(params[:page]).per(PER_PAGE) if terms.blank?
@last_push = current_user.recent_push
respond_to do |format|
@@ -32,16 +29,11 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
def starred
@projects = current_user.starred_projects.sorted_by_activity
+ @projects = filter_projects(@projects)
@projects = @projects.includes(:namespace, :forked_from_project, :tags)
@projects = @projects.sort(@sort = params[:sort])
+ @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
- terms = params[:filter_projects]
-
- if terms.present?
- @projects = @projects.search(terms)
- end
-
- @projects = @projects.page(params[:page]).per(PER_PAGE) if terms.blank?
@last_push = current_user.recent_push
@groups = []
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index a384f3004db..5b811db3068 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -1,12 +1,12 @@
class Explore::ProjectsController < Explore::ApplicationController
+ include FilterProjects
+
def index
@projects = ProjectsFinder.new.execute(current_user)
@tags = @projects.tags_on(:tags)
@projects = @projects.tagged_with(params[:tag]) if params[:tag].present?
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
- @projects = @projects.non_archived
- @projects = @projects.search(params[:search]) if params[:search].present?
- @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
+ @projects = filter_projects(@projects)
@projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
@@ -22,8 +22,7 @@ class Explore::ProjectsController < Explore::ApplicationController
def trending
@projects = TrendingProjectsFinder.new.execute(current_user)
- @projects = @projects.non_archived
- @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
+ @projects = filter_projects(@projects)
@projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
respond_to do |format|
@@ -38,7 +37,7 @@ class Explore::ProjectsController < Explore::ApplicationController
def starred
@projects = ProjectsFinder.new.execute(current_user)
- @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
+ @projects = filter_projects(@projects)
@projects = @projects.reorder('star_count DESC')
@projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index ca5ce1e2046..f05c29e9974 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -1,4 +1,5 @@
class GroupsController < Groups::ApplicationController
+ include FilterProjects
include IssuesAction
include MergeRequestsAction
@@ -41,7 +42,8 @@ class GroupsController < Groups::ApplicationController
def show
@last_push = current_user.recent_push if current_user
@projects = @projects.includes(:namespace)
- @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
+ @projects = filter_projects(@projects)
+ @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
respond_to do |format|
@@ -98,7 +100,7 @@ class GroupsController < Groups::ApplicationController
end
def load_projects
- @projects ||= ProjectsFinder.new.execute(current_user, group: group).sorted_by_activity.non_archived
+ @projects ||= ProjectsFinder.new.execute(current_user, group: group).sorted_by_activity
end
# Dont allow unauthorized access to group
diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb
index 07b5759443b..a41172816b8 100644
--- a/app/finders/snippets_finder.rb
+++ b/app/finders/snippets_finder.rb
@@ -4,7 +4,7 @@ class SnippetsFinder
case filter
when :all then
- snippets(current_user).fresh.non_expired
+ snippets(current_user).fresh
when :by_user then
by_user(current_user, params[:user], params[:scope])
when :by_project
@@ -27,7 +27,7 @@ class SnippetsFinder
end
def by_user(current_user, user, scope)
- snippets = user.snippets.fresh.non_expired
+ snippets = user.snippets.fresh
return snippets.are_public unless current_user
@@ -48,7 +48,7 @@ class SnippetsFinder
end
def by_project(current_user, project)
- snippets = project.snippets.fresh.non_expired
+ snippets = project.snippets.fresh
if current_user
if project.team.member?(current_user.id)
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index a09e91578b6..f994c9e6170 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -211,4 +211,15 @@ module CommitsHelper
def clean(string)
Sanitize.clean(string, remove_contents: true)
end
+
+ def limited_commits(commits)
+ if commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
+ [
+ commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE),
+ commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE
+ ]
+ else
+ [commits, 0]
+ end
+ end
end
diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb
index 3648757428b..337b0aacbb5 100644
--- a/app/helpers/explore_helper.rb
+++ b/app/helpers/explore_helper.rb
@@ -1,5 +1,5 @@
module ExploreHelper
- def explore_projects_filter_path(options={})
+ def filter_projects_path(options={})
exist_opts = {
sort: params[:sort],
scope: params[:scope],
@@ -9,15 +9,7 @@ module ExploreHelper
}
options = exist_opts.merge(options)
-
- path = if explore_controller?
- explore_projects_path
- elsif current_action?(:starred)
- starred_dashboard_projects_path
- else
- dashboard_projects_path
- end
-
+ path = request.path
path << "?#{options.to_param}"
path
end
diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb
index 41ae4048992..0a5a8eb5aee 100644
--- a/app/helpers/snippets_helper.rb
+++ b/app/helpers/snippets_helper.rb
@@ -1,14 +1,4 @@
module SnippetsHelper
- def lifetime_select_options
- options = [
- ['forever', nil],
- ['1 day', "#{Date.current + 1.day}"],
- ['1 week', "#{Date.current + 1.week}"],
- ['1 month', "#{Date.current + 1.month}"]
- ]
- options_for_select(options)
- end
-
def reliable_snippet_path(snippet)
if snippet.project_id?
namespace_project_snippet_path(snippet.project.namespace,
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index f9026b887da..2f2d2721d6d 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -16,6 +16,16 @@ module SortingHelper
}
end
+ def projects_sort_options_hash
+ {
+ sort_value_name => sort_title_name,
+ sort_value_recently_updated => sort_title_recently_updated,
+ sort_value_oldest_updated => sort_title_oldest_updated,
+ sort_value_recently_created => sort_title_recently_created,
+ sort_value_oldest_created => sort_title_oldest_created,
+ }
+ end
+
def sort_title_oldest_updated
'Oldest updated'
end
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index df08d3a6dfb..33884118595 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -17,7 +17,7 @@ class MergeRequestDiff < ActiveRecord::Base
include Sortable
# Prevent store of diff if commits amount more then 500
- COMMITS_SAFE_SIZE = 500
+ COMMITS_SAFE_SIZE = 100
belongs_to :merge_request
diff --git a/app/models/personal_snippet.rb b/app/models/personal_snippet.rb
index 9cee3b70cb3..452f3913eef 100644
--- a/app/models/personal_snippet.rb
+++ b/app/models/personal_snippet.rb
@@ -10,7 +10,6 @@
# created_at :datetime
# updated_at :datetime
# file_name :string(255)
-# expires_at :datetime
# type :string(255)
# visibility_level :integer default(0), not null
#
diff --git a/app/models/project_snippet.rb b/app/models/project_snippet.rb
index 9e2c1b0e18e..1f7d85a5f3d 100644
--- a/app/models/project_snippet.rb
+++ b/app/models/project_snippet.rb
@@ -10,7 +10,6 @@
# created_at :datetime
# updated_at :datetime
# file_name :string(255)
-# expires_at :datetime
# type :string(255)
# visibility_level :integer default(0), not null
#
@@ -23,6 +22,4 @@ class ProjectSnippet < Snippet
# Scopes
scope :fresh, -> { order("created_at DESC") }
- scope :non_expired, -> { where(["expires_at IS NULL OR expires_at > ?", Time.current]) }
- scope :expired, -> { where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) }
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index c135ab61f6a..6441cd87e87 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -133,18 +133,18 @@ class Repository
rugged.branches.create(branch_name, target)
end
- expire_branches_cache
+ after_create_branch
find_branch(branch_name)
end
def add_tag(tag_name, ref, message = nil)
- expire_tags_cache
+ before_push_tag
gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message)
end
def rm_branch(user, branch_name)
- expire_branches_cache
+ before_remove_branch
branch = find_branch(branch_name)
oldrev = branch.try(:target)
@@ -155,12 +155,12 @@ class Repository
rugged.branches.delete(branch_name)
end
- expire_branches_cache
+ after_remove_branch
true
end
def rm_tag(tag_name)
- expire_tags_cache
+ before_remove_tag
gitlab_shell.rm_tag(path_with_namespace, tag_name)
end
@@ -183,6 +183,14 @@ class Repository
end
end
+ def branch_count
+ @branch_count ||= cache.fetch(:branch_count) { raw_repository.branch_count }
+ end
+
+ def tag_count
+ @tag_count ||= cache.fetch(:tag_count) { raw_repository.rugged.tags.count }
+ end
+
# Return repo size in megabytes
# Cached in redis
def size
@@ -278,6 +286,16 @@ class Repository
@has_visible_content = nil
end
+ def expire_branch_count_cache
+ cache.expire(:branch_count)
+ @branch_count = nil
+ end
+
+ def expire_tag_count_cache
+ cache.expire(:tag_count)
+ @tag_count = nil
+ end
+
def rebuild_cache
cache_keys.each do |key|
cache.expire(key)
@@ -313,9 +331,17 @@ class Repository
expire_root_ref_cache
end
- # Runs code before creating a new tag.
- def before_create_tag
+ # Runs code before pushing (= creating or removing) a tag.
+ def before_push_tag
expire_cache
+ expire_tags_cache
+ expire_tag_count_cache
+ end
+
+ # Runs code before removing a tag.
+ def before_remove_tag
+ expire_tags_cache
+ expire_tag_count_cache
end
# Runs code after a repository has been forked/imported.
@@ -330,12 +356,21 @@ class Repository
# Runs code after a new branch has been created.
def after_create_branch
+ expire_branches_cache
expire_has_visible_content_cache
+ expire_branch_count_cache
+ end
+
+ # Runs code before removing an existing branch.
+ def before_remove_branch
+ expire_branches_cache
end
# Runs code after an existing branch has been removed.
def after_remove_branch
expire_has_visible_content_cache
+ expire_branch_count_cache
+ expire_branches_cache
end
def method_missing(m, *args, &block)
@@ -812,6 +847,12 @@ class Repository
raw_repository.ls_files(actual_ref)
end
+ def main_language
+ unless empty?
+ Linguist::Repository.new(rugged, rugged.head.target_id).language
+ end
+ end
+
private
def cache
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index f876be7a4c8..dd3925c7a7d 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -10,7 +10,6 @@
# created_at :datetime
# updated_at :datetime
# file_name :string(255)
-# expires_at :datetime
# type :string(255)
# visibility_level :integer default(0), not null
#
@@ -46,8 +45,6 @@ class Snippet < ActiveRecord::Base
scope :are_public, -> { where(visibility_level: Snippet::PUBLIC) }
scope :public_and_internal, -> { where(visibility_level: [Snippet::PUBLIC, Snippet::INTERNAL]) }
scope :fresh, -> { order("created_at DESC") }
- scope :expired, -> { where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) }
- scope :non_expired, -> { where(["expires_at IS NULL OR expires_at > ?", Time.current]) }
participant :author, :notes
@@ -111,10 +108,6 @@ class Snippet < ActiveRecord::Base
nil
end
- def expired?
- expires_at && expires_at < Time.current
- end
-
def visibility_level_field
visibility_level
end
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 9ba200f7bde..93a16e88967 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -14,6 +14,7 @@ class GitPushService < BaseService
# 3. Recognizes cross-references from commit messages
# 4. Executes the project's web hooks
# 5. Executes the project's services
+ # 6. Checks if the project's main language has changed
#
def execute
@project.repository.after_push_commit(branch_name)
@@ -42,11 +43,24 @@ class GitPushService < BaseService
@push_commits = @project.repository.commits_between(params[:oldrev], params[:newrev])
process_commit_messages
end
+ # Checks if the main language has changed in the project and if so
+ # it updates it accordingly
+ update_main_language
# Update merge requests that may be affected by this push. A new branch
# could cause the last commit of a merge request to change.
update_merge_requests
end
+ def update_main_language
+ current_language = @project.repository.main_language
+
+ unless current_language == @project.main_language
+ return @project.update_attributes(main_language: current_language)
+ end
+
+ true
+ end
+
protected
def update_merge_requests
@@ -96,7 +110,9 @@ class GitPushService < BaseService
# a different branch.
closed_issues = commit.closes_issues(current_user)
closed_issues.each do |issue|
- Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit)
+ if can?(current_user, :update_issue, issue)
+ Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit)
+ end
end
end
diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb
index a62c5fc4fc4..c88c7672805 100644
--- a/app/services/git_tag_push_service.rb
+++ b/app/services/git_tag_push_service.rb
@@ -2,7 +2,7 @@ class GitTagPushService
attr_accessor :project, :user, :push_data
def execute(project, user, oldrev, newrev, ref)
- project.repository.before_create_tag
+ project.repository.before_push_tag
@project, @user = project, user
@push_data = build_push_data(oldrev, newrev, ref)
diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb
index 8f25c5e2496..ebb67c7db65 100644
--- a/app/services/merge_requests/post_merge_service.rb
+++ b/app/services/merge_requests/post_merge_service.rb
@@ -21,7 +21,9 @@ module MergeRequests
closed_issues = merge_request.closes_issues(current_user)
closed_issues.each do |issue|
- Issues::CloseService.new(project, current_user, {}).execute(issue, merge_request)
+ if can?(current_user, :update_issue, issue)
+ Issues::CloseService.new(project, current_user, {}).execute(issue, merge_request)
+ end
end
end
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index 40f88261c10..9da3fcbd986 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -15,7 +15,7 @@
.nav-controls
= form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
= search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short projects-list-filter', spellcheck: false, id: 'project-filter-form-field', tabindex: "2"
- = render 'explore/projects/dropdown'
+ = render 'shared/projects/dropdown'
- if current_user.can_create_project?
= link_to new_project_path, class: 'btn btn-new' do
= icon('plus')
diff --git a/app/views/explore/projects/_dropdown.html.haml b/app/views/explore/projects/_dropdown.html.haml
deleted file mode 100644
index a4b4cd8d6c7..00000000000
--- a/app/views/explore/projects/_dropdown.html.haml
+++ /dev/null
@@ -1,20 +0,0 @@
-.dropdown.inline
- %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
- %span.light
- - if @sort.present?
- = sort_options_hash[@sort]
- - else
- = sort_title_recently_updated
- %b.caret
- %ul.dropdown-menu.dropdown-menu-align-right
- %li
- = link_to explore_projects_filter_path(sort: sort_value_name) do
- = sort_title_name
- = link_to explore_projects_filter_path(sort: sort_value_recently_created) do
- = sort_title_recently_created
- = link_to explore_projects_filter_path(sort: sort_value_oldest_created) do
- = sort_title_oldest_created
- = link_to explore_projects_filter_path(sort: sort_value_recently_updated) do
- = sort_title_recently_updated
- = link_to explore_projects_filter_path(sort: sort_value_oldest_updated) do
- = sort_title_oldest_updated
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 39e3e8e2738..cd485da5104 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -10,11 +10,11 @@
%b.caret
%ul.dropdown-menu
%li
- = link_to explore_projects_filter_path(visibility_level: nil) do
+ = link_to filter_projects_path(visibility_level: nil) do
Any
- Gitlab::VisibilityLevel.values.each do |level|
%li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' }
- = link_to explore_projects_filter_path(visibility_level: level) do
+ = link_to filter_projects_path(visibility_level: level) do
= visibility_level_icon(level)
= visibility_level_label(level)
@@ -30,11 +30,11 @@
%b.caret
%ul.dropdown-menu
%li
- = link_to explore_projects_filter_path(tag: nil) do
+ = link_to filter_projects_path(tag: nil) do
Any
- @tags.each do |tag|
%li{ class: (tag.name == params[:tag]) ? 'active' : 'light' }
- = link_to explore_projects_filter_path(tag: tag.name) do
- %i.fa.fa-tag
+ = link_to filter_projects_path(tag: tag.name) do
+ = icon('tag')
= tag.name
diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml
index 794aa57b55a..7cd8e9bea46 100644
--- a/app/views/groups/_projects.html.haml
+++ b/app/views/groups/_projects.html.haml
@@ -3,9 +3,10 @@
= form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
- if @projects.present?
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
- - if can? current_user, :create_projects, @group
- = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do
- = icon('plus')
- New Project
+ = render 'shared/projects/dropdown'
+ - if can? current_user, :create_projects, @group
+ = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do
+ = icon('plus')
+ New Project
= render 'shared/projects/list', projects: @projects, stars: false, skip_namespace: true
diff --git a/app/views/projects/branches/destroy.js.haml b/app/views/projects/branches/destroy.js.haml
index 882a4d0c5e2..a21ddaf4930 100644
--- a/app/views/projects/branches/destroy.js.haml
+++ b/app/views/projects/branches/destroy.js.haml
@@ -1 +1 @@
-$('.js-totalbranch-count').html("#{@repository.branches.size}")
+$('.js-totalbranch-count').html("#{@repository.branch_count}")
diff --git a/app/views/projects/commits/_commit_list.html.haml b/app/views/projects/commits/_commit_list.html.haml
index ce60fbdf032..bac9e244d36 100644
--- a/app/views/projects/commits/_commit_list.html.haml
+++ b/app/views/projects/commits/_commit_list.html.haml
@@ -1,11 +1,14 @@
+- commits, hidden = limited_commits(@commits)
+- commits = Commit.decorate(commits, @project)
+
%div.panel.panel-default
.panel-heading
Commits (#{@commits.count})
- - if @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
+ - if hidden > 0
%ul.well-list
- - Commit.decorate(@commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE), @project).each do |commit|
+ - commits.each do |commit|
= render "projects/commits/inline_commit", commit: commit, project: @project
%li.warning-row.unstyled
- other #{@commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE} commits hidden to prevent performance issues.
+ #{number_with_delimiter(hidden)} additional commits have been omitted to prevent performance issues.
- else
- %ul.well-list= render Commit.decorate(@commits, @project), project: @project
+ %ul.well-list= render commits, project: @project
diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml
index 6c631228002..a7e3c2478c2 100644
--- a/app/views/projects/commits/_commits.html.haml
+++ b/app/views/projects/commits/_commits.html.haml
@@ -1,7 +1,9 @@
- unless defined?(project)
- project = @project
-- @commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits|
+- commits, hidden = limited_commits(@commits)
+
+- commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits|
.row.commits-row
.col-md-2.hidden-xs.hidden-sm
%h5.commits-row-date
@@ -13,3 +15,7 @@
%ul.bordered-list
= render commits, project: project
%hr.lists-separator
+
+- if hidden > 0
+ .alert.alert-warning
+ #{number_with_delimiter(hidden)} additional commits have been omitted to prevent performance issues.
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index 498c5e05b32..7a5b0d993db 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -15,9 +15,9 @@
= nav_link(html_options: {class: branches_tab_class}) do
= link_to namespace_project_branches_path(@project.namespace, @project) do
Branches
- %span.badge.js-totalbranch-count= @repository.branches.size
+ %span.badge.js-totalbranch-count= @repository.branch_count
= nav_link(controller: [:tags, :releases]) do
= link_to namespace_project_tags_path(@project.namespace, @project) do
Tags
- %span.badge.js-totaltags-count= @repository.tags.length
+ %span.badge.js-totaltags-count= @repository.tag_count
diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml
new file mode 100644
index 00000000000..e7e04621ff4
--- /dev/null
+++ b/app/views/shared/projects/_dropdown.html.haml
@@ -0,0 +1,22 @@
+- @sort ||= sort_value_recently_updated
+- archived = params[:archived]
+.dropdown.inline
+ %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ %span.light
+ = projects_sort_options_hash[@sort]
+ %b.caret
+ %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable
+ %li.dropdown-header
+ Sort by
+ - projects_sort_options_hash.each do |value, title|
+ %li
+ = link_to filter_projects_path(sort: value, archived: archived), class: ("is-active" if @sort == value) do
+ = title
+
+ %li.divider
+ %li
+ = link_to filter_projects_path(sort: @sort, archived: nil), class: ("is-active" unless params[:archived].present?) do
+ Hide archived projects
+ %li
+ = link_to filter_projects_path(sort: @sort, archived: true), class: ("is-active" if params[:archived].present?) do
+ Show archived projects
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index 99e48e86e38..97cfb76cdb0 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -28,6 +28,9 @@
= project.name
.controls
+ - if project.main_language
+ %span
+ = project.main_language
- if ci_commit
%span
= render_ci_status(ci_commit)
diff --git a/config/environments/test.rb b/config/environments/test.rb
index d6842affa6c..f96ac6f9753 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -7,8 +7,6 @@ Rails.application.configure do
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = false
- config.cache_store = :null_store
-
# Configure static asset server for tests with Cache-Control for performance
config.serve_static_files = true
config.static_cache_control = "public, max-age=3600"
diff --git a/db/migrate/20160229193553_add_main_language_to_repository.rb b/db/migrate/20160229193553_add_main_language_to_repository.rb
new file mode 100644
index 00000000000..b5446c6a447
--- /dev/null
+++ b/db/migrate/20160229193553_add_main_language_to_repository.rb
@@ -0,0 +1,5 @@
+class AddMainLanguageToRepository < ActiveRecord::Migration
+ def change
+ add_column :projects, :main_language, :string
+ end
+end
diff --git a/db/migrate/20160305220806_remove_expires_at_from_snippets.rb b/db/migrate/20160305220806_remove_expires_at_from_snippets.rb
new file mode 100644
index 00000000000..fc12b5b09e6
--- /dev/null
+++ b/db/migrate/20160305220806_remove_expires_at_from_snippets.rb
@@ -0,0 +1,5 @@
+class RemoveExpiresAtFromSnippets < ActiveRecord::Migration
+ def change
+ remove_column :snippets, :expires_at, :datetime
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 71d9257a31e..4f56f3970db 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20160222153918) do
+ActiveRecord::Schema.define(version: 20160305220806) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -697,6 +697,7 @@ ActiveRecord::Schema.define(version: 20160222153918) do
t.integer "build_timeout", default: 3600, null: false
t.boolean "pending_delete", default: false
t.boolean "public_builds", default: true, null: false
+ t.string "main_language"
end
add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree
@@ -777,7 +778,6 @@ ActiveRecord::Schema.define(version: 20160222153918) do
t.datetime "created_at"
t.datetime "updated_at"
t.string "file_name"
- t.datetime "expires_at"
t.string "type"
t.integer "visibility_level", default: 0, null: false
end
@@ -785,7 +785,6 @@ ActiveRecord::Schema.define(version: 20160222153918) do
add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree
add_index "snippets", ["created_at", "id"], name: "index_snippets_on_created_at_and_id", using: :btree
add_index "snippets", ["created_at"], name: "index_snippets_on_created_at", using: :btree
- add_index "snippets", ["expires_at"], name: "index_snippets_on_expires_at", using: :btree
add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree
add_index "snippets", ["updated_at"], name: "index_snippets_on_updated_at", using: :btree
add_index "snippets", ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree
diff --git a/doc/api/commits.md b/doc/api/commits.md
index e4d436b8e52..6341440c58b 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -213,8 +213,7 @@ Example response:
## Commit status
-Since GitLab 8.1, this is the new commit status API. The documentation in
-[ci/api/commits](../ci/api/commits.md) is deprecated.
+Since GitLab 8.1, this is the new commit status API.
### Get the status of a commit
diff --git a/doc/api/notes.md b/doc/api/notes.md
index d4d63e825ab..85d4f0bafa2 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -145,7 +145,6 @@ Parameters:
"state": "active",
"created_at": "2013-09-30T13:46:01Z"
},
- "expires_at": null,
"updated_at": "2013-10-02T07:34:20Z",
"created_at": "2013-10-02T07:34:20Z"
}
diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md
index a7acf37b5bc..fb802102e3a 100644
--- a/doc/api/project_snippets.md
+++ b/doc/api/project_snippets.md
@@ -51,7 +51,6 @@ Parameters:
"state": "active",
"created_at": "2012-05-23T08:00:58Z"
},
- "expires_at": null,
"updated_at": "2012-06-28T10:52:04Z",
"created_at": "2012-06-28T10:52:04Z"
}
diff --git a/doc/api/users.md b/doc/api/users.md
index b7fc903825e..82c57a2fd43 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -151,6 +151,8 @@ Parameters:
"name": "John Smith",
"state": "active",
"created_at": "2012-05-23T08:00:58Z",
+ "confirmed_at": "2012-05-23T08:00:58Z",
+ "last_sign_in_at": "2015-03-23T08:00:58Z",
"bio": null,
"skype": "",
"linkedin": "",
diff --git a/doc/ci/api/README.md b/doc/ci/api/README.md
index cf9710ede57..aea808007fc 100644
--- a/doc/ci/api/README.md
+++ b/doc/ci/api/README.md
@@ -1,86 +1,22 @@
# GitLab CI API
-## Resources
-
-- [Projects](projects.md)
-- [Runners](runners.md)
-- [Commits](commits.md)
-- [Builds](builds.md)
-
-
-## Authentication
-
-GitLab CI API uses different types of authentication depends on what API you use.
-Each API document has section with information about authentication you need to use.
-
-GitLab CI API has 4 authentication methods:
-
-* GitLab user token & GitLab url
-* GitLab CI project token
-* GitLab CI runners registration token
-* GitLab CI runner token
-
-
-### Authentication #1: GitLab user token & GitLab url
-
-Authentication is done by
-sending the `private-token` of a valid user and the `url` of an
-authorized GitLab instance via a query string along with the API
-request:
-
- GET http://gitlab.example.com/ci/api/v1/projects?private_token=QVy1PB7sTxfy4pqfZM1U&url=http://demo.gitlab.com/
+## Purpose
-If preferred, you may instead send the `private-token` as a header in
-your request:
+Main purpose of GitLab CI API is to provide necessary data and context for
+GitLab CI Runners.
- curl --header "PRIVATE-TOKEN: QVy1PB7sTxfy4pqfZM1U" "http://gitlab.example.com/ci/api/v1/projects?url=http://demo.gitlab.com/"
+For consumer API take a look at this [documentation](../../api/README.md) where
+you will find all relevant information.
+## API Prefix
-### Authentication #2: GitLab CI project token
+Current CI API prefix is `/ci/api/v1`.
-Each project in GitLab CI has it own token.
-It can be used to get project commits and builds information.
-You can use project token only for certain project.
+You need to prepend this prefix to all examples in this documentation, like:
-### Authentication #3: GitLab CI runners registration token
+ GET /ci/api/v1/builds/:id/artifacts
-This token is not persisted and is generated on each application start.
-It can be used only for registering new runners in system. You can find it on
-GitLab CI Runners web page https://gitlab-ci.example.com/admin/runners
-
-### Authentication #4: GitLab CI runner token
-
-Every GitLab CI runner has it own token that allow it to receive and update
-GitLab CI builds. This token exists of internal purposes and should be used only
-by runners
-
-## JSON
-
-All API requests are serialized using JSON. You don't need to specify
-`.json` at the end of API URL.
-
-## Status codes
-
-The API is designed to return different status codes according to context and action. In this way if a request results in an error the caller is able to get insight into what went wrong, e.g. status code `400 Bad Request` is returned if a required attribute is missing from the request. The following list gives an overview of how the API functions generally behave.
-
-API request types:
-
-- `GET` requests access one or more resources and return the result as JSON
-- `POST` requests return `201 Created` if the resource is successfully created and return the newly created resource as JSON
-- `GET`, `PUT` and `DELETE` return `200 OK` if the resource is accessed, modified or deleted successfully, the (modified) result is returned as JSON
-- `DELETE` requests are designed to be idempotent, meaning a request a resource still returns `200 OK` even it was deleted before or is not available. The reasoning behind it is the user is not really interested if the resource existed before or not.
-
-The following list shows the possible return codes for API requests.
-
-Return values:
+## Resources
-- `200 OK` - The `GET`, `PUT` or `DELETE` request was successful, the resource(s) itself is returned as JSON
-- `201 Created` - The `POST` request was successful and the resource is returned as JSON
-- `400 Bad Request` - A required attribute of the API request is missing, e.g. the title of an issue is not given
-- `401 Unauthorized` - The user is not authenticated, a valid user token is necessary, see above
-- `403 Forbidden` - The request is not allowed, e.g. the user is not allowed to delete a project
-- `404 Not Found` - A resource could not be accessed, e.g. an ID for a resource could not be found
-- `405 Method Not Allowed` - The request is not supported
-- `409 Conflict` - A conflicting resource already exists, e.g. creating a project with a name that already exists
-- `422 Unprocessable` - The entity could not be processed
-- `500 Server Error` - While handling the request something went wrong on the server side
+- [Builds](builds.md)
+- [Runners](runners.md)
diff --git a/doc/ci/api/builds.md b/doc/ci/api/builds.md
index 018ca22dbbd..d100e261178 100644
--- a/doc/ci/api/builds.md
+++ b/doc/ci/api/builds.md
@@ -1,85 +1,73 @@
# Builds API
-This API used by runners to receive and update builds.
+API used by runners to receive and update builds.
-__Authentication is done by runner token__
+_**Note:** This API is intended to be used only by Runners as their own
+communication channel. For the consumer API see the
+[Builds API](../../api/builds.md)._
+
+## Authentication
+
+This API uses two types of authentication:
+
+1. Unique runner's token
+
+ Token assigned to runner after it has been registered.
+
+2. Using build authorization token
+
+ This is project's CI token that can be found in Continuous Integration
+ project settings.
+
+ Build authorization token can be passed as a parameter or a value of
+ `BUILD-TOKEN` header. This method are interchangeable.
## Builds
### Runs oldest pending build by runner
- POST /ci/builds/register
+ POST /ci/api/v1/builds/register
Parameters:
- * `token` (required) - The unique token of runner
-
-Returns:
-
-```json
-{
- "id": 48584,
- "ref": "0.1.1",
- "tag": true,
- "sha": "d63117656af6ff57d99e50cc270f854691f335ad",
- "status": "success",
- "name": "pages",
- "token": "9dd60b4f1a439d1765357446c1084c",
- "stage": "test",
- "project_id": 479,
- "project_name": "test",
- "commands": "echo commands",
- "repo_url": "http://gitlab-ci-token:token@gitlab.example/group/test.git",
- "before_sha": "0000000000000000000000000000000000000000",
- "allow_git_fetch": false,
- "options": {
- "image": "docker:image",
- "artifacts": {
- "paths": [
- "public"
- ]
- },
- "cache": {
- "paths": [
- "vendor"
- ]
- }
- },
- "timeout": 3600,
- "variables": [
- {
- "key": "CI_BUILD_TAG",
- "value": "0.1.1",
- "public": true
- }
- ],
- "depends_on_builds": [
- {
- "id": 48584,
- "ref": "0.1.1",
- "tag": true,
- "sha": "d63117656af6ff57d99e50cc270f854691f335ad",
- "status": "success",
- "name": "build",
- "token": "9dd60b4f1a439d1765357446c1084c",
- "stage": "build",
- "project_id": 479,
- "project_name": "test",
- "artifacts_file": {
- "filename": "artifacts.zip",
- "size": 0
- }
- }
- ]
-}
-```
+ * `token` (required) - Unique runner token
+
### Update details of an existing build
- PUT /ci/builds/:id
+ PUT /ci/api/v1/builds/:id
Parameters:
* `id` (required) - The ID of a project
+ * `token` (required) - Unique runner token
* `state` (optional) - The state of a build
* `trace` (optional) - The trace of a build
+
+### Upload artifacts to build
+
+ POST /ci/api/v1/builds/:id/artifacts
+
+Parameters:
+
+ * `id` (required) - The ID of a build
+ * `token` (required) - The build authorization token
+ * `file` (required) - Artifacts file
+
+### Download the artifacts file from build
+
+ GET /ci/api/v1/builds/:id/artifacts
+
+Parameters:
+
+ * `id` (required) - The ID of a build
+ * `token` (required) - The build authorization token
+
+### Remove the artifacts file from build
+
+ DELETE /ci/api/v1/builds/:id/artifacts
+
+Parameters:
+
+ * ` id` (required) - The ID of a build
+ * `token` (required) - The build authorization token
diff --git a/doc/ci/api/commits.md b/doc/ci/api/commits.md
deleted file mode 100644
index 871de7abcce..00000000000
--- a/doc/ci/api/commits.md
+++ /dev/null
@@ -1,108 +0,0 @@
-# Commits API
-
-**DEPRECATED**
-
-Since GitLab 8.1, there is a new commit status API. Please see the [revised
-documentation](../../api/commits.md#commit-status).
-
----
-
-__Authentication is done by GitLab CI project token__
-
-## Commits
-
-### Retrieve all commits per project
-
-Get list of commits per project
-
- GET /ci/commits
-
-Parameters:
-
- * `project_id` (required) - The ID of a project
- * `project_token` (requires) - Project token
- * `page` (optional)
- * `per_page` (optional) - items per request (default is 20)
-
-Returns:
-
-```json
-[{
- "id": 3,
- "ref": "master",
- "sha": "65617dfc36761baa1f46a7006f2a88916f7f56cf",
- "project_id": 2,
- "before_sha": "96906f2bceb04c7323f8514aa5ad8cb1313e2898",
- "created_at": "2014-11-05T09:46:35.247Z",
- "status": "success",
- "finished_at": "2014-11-05T09:46:44.254Z",
- "duration": 5.062692165374756,
- "git_commit_message": "wow\n",
- "git_author_name": "Administrator",
- "git_author_email": "admin@example.com",
- "builds": [{
- "id": 7,
- "project_id": 2,
- "ref": "master",
- "status": "success",
- "finished_at": "2014-11-05T09:46:44.254Z",
- "created_at": "2014-11-05T09:46:35.259Z",
- "updated_at": "2014-11-05T09:46:44.255Z",
- "sha": "65617dfc36761baa1f46a7006f2a88916f7f56cf",
- "started_at": "2014-11-05T09:46:39.192Z",
- "before_sha": "96906f2bceb04c7323f8514aa5ad8cb1313e2898",
- "runner_id": 1,
- "coverage": null,
- "commit_id": 3
- }]
-}]
-```
-
-### Create commit
-
-Inform GitLab CI about new commit you want it to build.
-
-__If commit already exists in GitLab CI it will not be created__
-
-
- POST /ci/commits
-
-Parameters:
-
- * `project_id` (required) - The ID of a project
- * `project_token` (requires) - Project token
- * `data` (required) - Push data. For example see comment in `lib/api/commits.rb`
-
-Returns:
-
-```json
-{
- "id": 3,
- "ref": "master",
- "sha": "65617dfc36761baa1f46a7006f2a88916f7f56cf",
- "project_id": 2,
- "before_sha": "96906f2bceb04c7323f8514aa5ad8cb1313e2898",
- "created_at": "2014-11-05T09:46:35.247Z",
- "status": "success",
- "finished_at": "2014-11-05T09:46:44.254Z",
- "duration": 5.062692165374756,
- "git_commit_message": "wow\n",
- "git_author_name": "Administrator",
- "git_author_email": "admin@example.com",
- "builds": [{
- "id": 7,
- "project_id": 2,
- "ref": "master",
- "status": "success",
- "finished_at": "2014-11-05T09:46:44.254Z",
- "created_at": "2014-11-05T09:46:35.259Z",
- "updated_at": "2014-11-05T09:46:44.255Z",
- "sha": "65617dfc36761baa1f46a7006f2a88916f7f56cf",
- "started_at": "2014-11-05T09:46:39.192Z",
- "before_sha": "96906f2bceb04c7323f8514aa5ad8cb1313e2898",
- "runner_id": 1,
- "coverage": null,
- "commit_id": 3
- }]
-}
-```
diff --git a/doc/ci/api/projects.md b/doc/ci/api/projects.md
deleted file mode 100644
index fe6b1c01352..00000000000
--- a/doc/ci/api/projects.md
+++ /dev/null
@@ -1,149 +0,0 @@
-# Projects API
-
-This API is intended to aid in the setup and configuration of
-projects on GitLab CI.
-
-__Authentication is done by GitLab user token & GitLab url__
-
-## Projects
-
-### List Authorized Projects
-
-Lists all projects that the authenticated user has access to.
-
-```
-GET /ci/projects
-```
-
-Returns:
-
-```json
-[
- {
- "id" : 271,
- "name" : "gitlabhq",
- "timeout" : 1800,
- "token" : "iPWx6WM4lhHNedGfBpPJNP",
- "default_ref" : "master",
- "gitlab_url" : "http://demo.gitlabhq.com/gitlab/gitlab-shell",
- "path" : "gitlab/gitlab-shell",
- "always_build" : false,
- "polling_interval" : null,
- "public" : false,
- "ssh_url_to_repo" : "git@demo.gitlab.com:gitlab/gitlab-shell.git",
- "gitlab_id" : 3
- },
- {
- "id" : 272,
- "name" : "gitlab-ci",
- "timeout" : 1800,
- "token" : "iPWx6WM4lhHNedGfBpPJNP",
- "default_ref" : "master",
- "gitlab_url" : "http://demo.gitlabhq.com/gitlab/gitlab-shell",
- "path" : "gitlab/gitlab-shell",
- "always_build" : false,
- "polling_interval" : null,
- "public" : false,
- "ssh_url_to_repo" : "git@demo.gitlab.com:gitlab/gitlab-shell.git",
- "gitlab_id" : 4
- }
-]
-```
-
-### List Owned Projects
-
-Lists all projects that the authenticated user owns.
-
-```
-GET /ci/projects/owned
-```
-
-Returns:
-
-```json
-[
- {
- "id" : 272,
- "name" : "gitlab-ci",
- "timeout" : 1800,
- "token" : "iPWx6WM4lhHNedGfBpPJNP",
- "default_ref" : "master",
- "gitlab_url" : "http://demo.gitlabhq.com/gitlab/gitlab-shell",
- "path" : "gitlab/gitlab-shell",
- "always_build" : false,
- "polling_interval" : null,
- "public" : false,
- "ssh_url_to_repo" : "git@demo.gitlab.com:gitlab/gitlab-shell.git",
- "gitlab_id" : 4
- }
-]
-```
-
-### Single Project
-
-Returns information about a single project for which the user is
-authorized.
-
- GET /ci/projects/:id
-
-Parameters:
-
- * `id` (required) - The ID of the GitLab CI project
-
-### Create Project
-
-Creates a GitLab CI project using GitLab project details.
-
- POST /ci/projects
-
-Parameters:
-
- * `name` (required) - The name of the project
- * `gitlab_id` (required) - The ID of the project on the GitLab instance
- * `default_ref` (optional) - The branch to run on (default to `master`)
-
-### Update Project
-
-Updates a GitLab CI project using GitLab project details that the
-authenticated user has access to.
-
- PUT /ci/projects/:id
-
-Parameters:
-
- * `name` - The name of the project
- * `default_ref` - The branch to run on (default to `master`)
-
-### Remove Project
-
-Removes a GitLab CI project that the authenticated user has access to.
-
- DELETE /ci/projects/:id
-
-Parameters:
-
- * `id` (required) - The ID of the GitLab CI project
-
-### Link Project to Runner
-
-Links a runner to a project so that it can make builds (only via
-authorized user).
-
- POST /ci/projects/:id/runners/:runner_id
-
-Parameters:
-
- * `id` (required) - The ID of the GitLab CI project
- * `runner_id` (required) - The ID of the GitLab CI runner
-
-### Remove Project from Runner
-
-Removes a runner from a project so that it can not make builds (only
-via authorized user).
-
- DELETE /ci/projects/:id/runners/:runner_id
-
-Parameters:
-
- * `id` (required) - The ID of the GitLab CI project
- * `runner_id` (required) - The ID of the GitLab CI runner \ No newline at end of file
diff --git a/doc/ci/api/runners.md b/doc/ci/api/runners.md
index e9033aeacd5..2f01da4bd76 100644
--- a/doc/ci/api/runners.md
+++ b/doc/ci/api/runners.md
@@ -1,81 +1,46 @@
# Runners API
+API used by runners to register and delete themselves.
+
_**Note:** This API is intended to be used only by Runners as their own
communication channel. For the consumer API see the
[new Runners API](../../api/runners.md)._
-## Runners
-
-### Retrieve all runners
+## Authentication
-__Authentication is done by GitLab user token & GitLab url__
+This API uses two types of authentication:
-Used to get information about all runners registered on the GitLab CI
-instance.
+1. Unique runner's token
- GET /ci/runners
+ Token assigned to runner after it has been registered.
-Returns:
+2. Using runners' registration token
-```json
-[
- {
- "id" : 85,
- "token" : "12b68e90394084703135"
- },
- {
- "id" : 86,
- "token" : "76bf894e969364709864"
- },
-]
-```
+ This is a token that can be found in project's settings.
+ It can be also found in Admin area &raquo; Runners settings.
-### Register a new runner
+ There are two types of tokens you can pass - shared runner registration
+ token or project specific registration token.
+## Runners
-__Authentication is done with a Shared runner registration token or a project Specific runner registration token__
+### Register a new runner
Used to make GitLab CI aware of available runners.
- POST /ci/runners/register
+ POST /ci/api/v1/runners/register
Parameters:
- * `token` (required) - The registration token. It is 2 types of token you can pass here.
+ * `token` (required) - Registration token
-1. Shared runner registration token
-2. Project specific registration token
-
-Returns:
-
-```json
-{
- "id" : 85,
- "token" : "12b68e90394084703135"
-}
-```
### Delete a runner
+Used to remove runner.
-__Authentication is done by runner token__
-
-Used to removing runners.
-
- DELETE /ci/runners/delete
+ DELETE /ci/api/v1/runners/delete
Parameters:
- * `token` (required) - The runner token.
-
-Returns:
-
-```json
-{
- "id" : 1,
- "token" : "d14963981a428f70121777e50643d1",
- "created_at" : "2015-02-26T11:39:39.232Z",
- "updated_at" : "2015-02-26T11:39:39.232Z",
- "description" : "awesome runner"
-}
-```
+ * `token` (required) - Unique runner token
diff --git a/doc/development/README.md b/doc/development/README.md
index b9a0d81e5ba..f5c3107ff44 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -9,4 +9,5 @@
- [Rake tasks](rake_tasks.md) for development
- [Shell commands](shell_commands.md) in the GitLab codebase
- [Sidekiq debugging](sidekiq_debugging.md)
+- [SQL guidelines](sql.md) for SQL guidelines
- [UI guide](ui_guide.md) for building GitLab with existing css styles and elements
diff --git a/doc/development/sql.md b/doc/development/sql.md
new file mode 100644
index 00000000000..23fd7604957
--- /dev/null
+++ b/doc/development/sql.md
@@ -0,0 +1,219 @@
+# SQL Query Guidelines
+
+This document describes various guidelines to follow when writing SQL queries,
+either using ActiveRecord/Arel or raw SQL queries.
+
+## Using LIKE Statements
+
+The most common way to search for data is using the `LIKE` statement. For
+example, to get all issues with a title starting with "WIP:" you'd write the
+following query:
+
+```sql
+SELECT *
+FROM issues
+WHERE title LIKE 'WIP:%';
+```
+
+On PostgreSQL the `LIKE` statement is case-sensitive. On MySQL this depends on
+the case-sensitivity of the collation, which is usually case-insensitive. To
+perform a case-insensitive `LIKE` on PostgreSQL you have to use `ILIKE` instead.
+This statement in turn isn't supported on MySQL.
+
+To work around this problem you should write `LIKE` queries using Arel instead
+of raw SQL fragments as Arel automatically uses `ILIKE` on PostgreSQL and `LIKE`
+on MySQL. This means that instead of this:
+
+```ruby
+Issue.where('title LIKE ?', 'WIP:%')
+```
+
+You'd write this instead:
+
+```ruby
+Issue.where(Issue.arel_table[:title].matches('WIP:%'))
+```
+
+Here `matches` generates the correct `LIKE` / `ILIKE` statement depending on the
+database being used.
+
+If you need to chain multiple `OR` conditions you can also do this using Arel:
+
+```ruby
+table = Issue.arel_table
+
+Issue.where(table[:title].matches('WIP:%').or(table[:foo].matches('WIP:%')))
+```
+
+For PostgreSQL this produces:
+
+```sql
+SELECT *
+FROM issues
+WHERE (title ILIKE 'WIP:%' OR foo ILIKE 'WIP:%')
+```
+
+In turn for MySQL this produces:
+
+```sql
+SELECT *
+FROM issues
+WHERE (title LIKE 'WIP:%' OR foo LIKE 'WIP:%')
+```
+
+## LIKE & Indexes
+
+Neither PostgreSQL nor MySQL use any indexes when using `LIKE` / `ILIKE` with a
+wildcard at the start. For example, this will not use any indexes:
+
+```sql
+SELECT *
+FROM issues
+WHERE title ILIKE '%WIP:%';
+```
+
+Because the value for `ILIKE` starts with a wildcard the database is not able to
+use an index as it doesn't know where to start scanning the indexes.
+
+MySQL provides no known solution to this problem. Luckily PostgreSQL _does_
+provide a solution: trigram GIN indexes. These indexes can be created as
+follows:
+
+```sql
+CREATE INDEX [CONCURRENTLY] index_name_here
+ON table_name
+USING GIN(column_name gin_trgm_ops);
+```
+
+The key here is the `GIN(column_name gin_trgm_ops)` part. This creates a [GIN
+index][gin-index] with the operator class set to `gin_trgm_ops`. These indexes
+_can_ be used by `ILIKE` / `LIKE` and can lead to greatly improved performance.
+One downside of these indexes is that they can easily get quite large (depending
+on the amount of data indexed).
+
+To keep naming of these indexes consistent please use the following naming
+pattern:
+
+ index_TABLE_on_COLUMN_trigram
+
+For example, a GIN/trigram index for `issues.title` would be called
+`index_issues_on_title_trigram`.
+
+Due to these indexes taking quite some time to be built they should be built
+concurrently. This can be done by using `CREATE INDEX CONCURRENTLY` instead of
+just `CREATE INDEX`. Concurrent indexes can _not_ be created inside a
+transaction. Transactions for migrations can be disabled using the following
+pattern:
+
+```ruby
+class MigrationName < ActiveRecord::Migration
+ disable_ddl_transaction!
+end
+```
+
+For example:
+
+```ruby
+class AddUsersLowerUsernameEmailIndexes < ActiveRecord::Migration
+ disable_ddl_transaction!
+
+ def up
+ return unless Gitlab::Database.postgresql?
+
+ execute 'CREATE INDEX CONCURRENTLY index_on_users_lower_username ON users (LOWER(username));'
+ execute 'CREATE INDEX CONCURRENTLY index_on_users_lower_email ON users (LOWER(email));'
+ end
+
+ def down
+ return unless Gitlab::Database.postgresql?
+
+ remove_index :users, :index_on_users_lower_username
+ remove_index :users, :index_on_users_lower_email
+ end
+end
+```
+
+## Plucking IDs
+
+This can't be stressed enough: **never** use ActiveRecord's `pluck` to pluck a
+set of values into memory only to use them as an argument for another query. For
+example, this will make the database **very** sad:
+
+```ruby
+projects = Project.all.pluck(:id)
+
+MergeRequest.where(source_project_id: projects)
+```
+
+Instead you can just use sub-queries which perform far better:
+
+```ruby
+MergeRequest.where(source_project_id: Project.all.select(:id))
+```
+
+The _only_ time you should use `pluck` is when you actually need to operate on
+the values in Ruby itself (e.g. write them to a file). In almost all other cases
+you should ask yourself "Can I not just use a sub-query?".
+
+## Use UNIONs
+
+UNIONs aren't very commonly used in most Rails applications but they're very
+powerful and useful. In most applications queries tend to use a lot of JOINs to
+get related data or data based on certain criteria, but JOIN performance can
+quickly deteriorate as the data involved grows.
+
+For example, if you want to get a list of projects where the name contains a
+value _or_ the name of the namespace contains a value most people would write
+the following query:
+
+```sql
+SELECT *
+FROM projects
+JOIN namespaces ON namespaces.id = projects.namespace_id
+WHERE projects.name ILIKE '%gitlab%'
+OR namespaces.name ILIKE '%gitlab%';
+```
+
+Using a large database this query can easily take around 800 milliseconds to
+run. Using a UNION we'd write the following instead:
+
+```sql
+SELECT projects.*
+FROM projects
+WHERE projects.name ILIKE '%gitlab%'
+
+UNION
+
+SELECT projects.*
+FROM projects
+JOIN namespaces ON namespaces.id = projects.namespace_id
+WHERE namespaces.name ILIKE '%gitlab%';
+```
+
+This query in turn only takes around 15 milliseconds to complete while returning
+the exact same records.
+
+This doesn't mean you should start using UNIONs everywhere, but it's something
+to keep in mind when using lots of JOINs in a query and filtering out records
+based on the joined data.
+
+GitLab comes with a `Gitlab::SQL::Union` class that can be used to build a UNION
+of multiple `ActiveRecord::Relation` objects. You can use this class as
+follows:
+
+```ruby
+union = Gitlab::SQL::Union.new([projects, more_projects, ...])
+
+Project.from("(#{union.to_sql}) projects")
+```
+
+## Ordering by Creation Date
+
+When ordering records based on the time they were created you can simply order
+by the `id` column instead of ordering by `created_at`. Because IDs are always
+unique and incremented in the order that rows are created this will produce the
+exact same results. This also means there's no need to add an index on
+`created_at` to ensure consistent performance as `id` is already indexed by
+default.
+
+[gin-index]: http://www.postgresql.org/docs/current/static/gin.html
diff --git a/doc/hooks/custom_hooks.md b/doc/hooks/custom_hooks.md
index 0f2665a3bf7..5a4b2f43ba7 100644
--- a/doc/hooks/custom_hooks.md
+++ b/doc/hooks/custom_hooks.md
@@ -2,7 +2,7 @@
**Note: Custom git hooks must be configured on the filesystem of the GitLab
server. Only GitLab server administrators will be able to complete these tasks.
-Please explore webhooks as an option if you do not have filesystem access. For a user configurable Git Hooks interface, please see [GitLab Enterprise Edition Git Hooks](http://doc.gitlab.com/ee/git_hooks/git_hooks.html).**
+Please explore [web hooks](doc/web_hooks/web_hooks.md) as an option if you do not have filesystem access. For a user configurable Git Hooks interface, please see [GitLab Enterprise Edition Git Hooks](http://doc.gitlab.com/ee/git_hooks/git_hooks.html).**
Git natively supports hooks that are executed on different actions.
Examples of server-side git hooks include pre-receive, post-receive, and update.
diff --git a/doc/integration/slack.md b/doc/integration/slack.md
index ecbe0d3e887..f6ba80f46d5 100644
--- a/doc/integration/slack.md
+++ b/doc/integration/slack.md
@@ -2,19 +2,11 @@
## On Slack
-To enable Slack integration you must create an Incoming WebHooks integration on Slack;
+To enable Slack integration you must create an Incoming WebHooks integration on Slack:
1. [Sign in to Slack](https://slack.com/signin)
-1. Select **Apps & Custom Integrations** from the dropdown next to your team name.
-
-1. Click the **Configure** link (right-upper corner).
-
-1. Select the **Custom integrations** tab.
-
-1. Click the **Incoming WebHooks** row.
-
-1. Click the **Add configuration** button.
+1. Visit [Incoming WebHooks](https://my.slack.com/services/new/incoming-webhook/)
1. Choose the channel name you want to send notifications to.
diff --git a/doc/update/upgrader.md b/doc/update/upgrader.md
index fd0327686b1..5fa39ef1b0a 100644
--- a/doc/update/upgrader.md
+++ b/doc/update/upgrader.md
@@ -4,7 +4,7 @@
Although deprecated, if someone wants to make this script into a gem or otherwise improve it merge requests are welcome.
-*Make sure you view this [upgrade guide from the 'master' branch](../../../master/doc/update/upgrader.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the 'master' branch](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/upgrader.md) for the most up to date instructions.*
GitLab Upgrader - a ruby script that allows you easily upgrade GitLab to latest minor version.
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index b82306bd1da..e2b53c45ab1 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -582,7 +582,6 @@ X-Gitlab-Event: Note Hook
"created_at": "2015-04-09 02:40:38 UTC",
"updated_at": "2015-04-09 02:40:38 UTC",
"file_name": "test.rb",
- "expires_at": null,
"type": "ProjectSnippet",
"visibility_level": 0
}
diff --git a/features/dashboard/archived_projects.feature b/features/dashboard/archived_projects.feature
index 69b3a776441..bed9282f1c6 100644
--- a/features/dashboard/archived_projects.feature
+++ b/features/dashboard/archived_projects.feature
@@ -10,3 +10,8 @@ Feature: Dashboard Archived Projects
Scenario: I should see non-archived projects on dashboard
Then I should see "Shop" project link
And I should not see "Forum" project link
+
+ Scenario: I toggle show of archived projects on dashboard
+ When I click "Show archived projects" link
+ Then I should see "Shop" project link
+ And I should see "Forum" project link
diff --git a/features/explore/projects.feature b/features/explore/projects.feature
index 7df6b6f09ba..092e18d1b86 100644
--- a/features/explore/projects.feature
+++ b/features/explore/projects.feature
@@ -140,4 +140,4 @@ Feature: Explore Projects
When I visit the explore starred projects
Then I should see project "Community"
And I should see project "Internal"
- And I should see project "Archive"
+ And I should not see project "Archive"
diff --git a/features/steps/dashboard/archived_projects.rb b/features/steps/dashboard/archived_projects.rb
index 36e092f50c6..6510f8d9b32 100644
--- a/features/steps/dashboard/archived_projects.rb
+++ b/features/steps/dashboard/archived_projects.rb
@@ -19,4 +19,8 @@ class Spinach::Features::DashboardArchivedProjects < Spinach::FeatureSteps
step 'I should see "Forum" project link' do
expect(page).to have_link "Forum"
end
+
+ step 'I click "Show archived projects" link' do
+ click_link "Show archived projects"
+ end
end
diff --git a/features/support/env.rb b/features/support/env.rb
index 62c80b9c948..357d164d87f 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -14,6 +14,7 @@ require 'sidekiq/testing/inline'
require_relative 'capybara'
require_relative 'db_cleaner'
+require_relative 'rerun'
%w(select2_helper test_env repo_helpers).each do |f|
require Rails.root.join('spec', 'support', f)
diff --git a/features/support/rerun.rb b/features/support/rerun.rb
new file mode 100644
index 00000000000..8b176c5be89
--- /dev/null
+++ b/features/support/rerun.rb
@@ -0,0 +1,14 @@
+# The spinach-rerun-reporter doesn't define the on_undefined_step
+# See it here: https://github.com/javierav/spinach-rerun-reporter/blob/master/lib/spinach/reporter/rerun.rb
+module Spinach
+ class Reporter
+ class Rerun
+ def on_undefined_step(step_data, failure, step_definitions = nil)
+ super step_data, failure, step_definitions
+
+ # save feature file and scenario line
+ @rerun << "#{current_feature.filename}:#{current_scenario.line}"
+ end
+ end
+ end
+end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index b021db8fa5b..5b5b8bd044b 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -23,6 +23,8 @@ module API
end
class UserFull < User
+ expose :last_sign_in_at
+ expose :confirmed_at
expose :email
expose :theme_id, :color_scheme_id, :projects_limit, :current_sign_in_at
expose :identities, using: Entities::Identity
@@ -141,7 +143,7 @@ module API
class ProjectSnippet < Grape::Entity
expose :id, :title, :file_name
expose :author, using: Entities::UserBasic
- expose :expires_at, :updated_at, :created_at
+ expose :updated_at, :created_at
end
class ProjectEntity < Grape::Entity
diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb
index 3f483847efa..46e51a4bf6d 100644
--- a/lib/gitlab/bitbucket_import/importer.rb
+++ b/lib/gitlab/bitbucket_import/importer.rb
@@ -76,7 +76,7 @@ module Gitlab
project.issues.create!(
description: body,
title: issue["title"],
- state: %w(resolved invalid duplicate wontfix).include?(issue["status"]) ? 'closed' : 'opened',
+ state: %w(resolved invalid duplicate wontfix closed).include?(issue["status"]) ? 'closed' : 'opened',
author_id: gl_user_id(project, reporter)
)
end
diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake
index 3acfc6e2075..01d23b89bb7 100644
--- a/lib/tasks/spinach.rake
+++ b/lib/tasks/spinach.rake
@@ -4,53 +4,59 @@ namespace :spinach do
namespace :project do
desc "GitLab | Spinach | Run project commits, issues and merge requests spinach features"
task :half do
- cmds = [
- %W(rake gitlab:setup),
- %W(spinach --tags @project_commits,@project_issues,@project_merge_requests),
- ]
- run_commands(cmds)
+ run_spinach_tests('@project_commits,@project_issues,@project_merge_requests')
end
desc "GitLab | Spinach | Run remaining project spinach features"
task :rest do
- cmds = [
- %W(rake gitlab:setup),
- %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets,~@project_commits,~@project_issues,~@project_merge_requests),
- ]
- run_commands(cmds)
+ run_spinach_tests('~@admin,~@dashboard,~@profile,~@public,~@snippets,~@project_commits,~@project_issues,~@project_merge_requests')
end
end
desc "GitLab | Spinach | Run project spinach features"
task :project do
- cmds = [
- %W(rake gitlab:setup),
- %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets),
- ]
- run_commands(cmds)
+ run_spinach_tests('~@admin,~@dashboard,~@profile,~@public,~@snippets')
end
desc "GitLab | Spinach | Run other spinach features"
task :other do
- cmds = [
- %W(rake gitlab:setup),
- %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets),
- ]
- run_commands(cmds)
+ run_spinach_tests('@admin,@dashboard,@profile,@public,@snippets')
+ end
+
+ desc "GitLab | Spinach | Run other spinach features"
+ task :builds do
+ run_spinach_tests('@builds')
end
end
desc "GitLab | Run spinach"
task :spinach do
- cmds = [
- %W(rake gitlab:setup),
- %W(spinach),
- ]
- run_commands(cmds)
+ run_spinach_tests(nil)
+end
+
+def run_command(cmd)
+ system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd)
end
-def run_commands(cmds)
- cmds.each do |cmd|
- system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) or raise("#{cmd} failed!")
+def run_spinach_command(args)
+ run_command(%w(spinach -r rerun) + args)
+end
+
+def run_spinach_tests(tags)
+ #run_command(%w(rake gitlab:setup)) or raise('gitlab:setup failed!')
+
+ success = run_spinach_command(%W(--tags #{tags}))
+ 3.times do |_|
+ break if success
+ break unless File.exists?('tmp/spinach-rerun.txt')
+
+ tests = File.foreach('tmp/spinach-rerun.txt').map(&:chomp)
+ puts ''
+ puts "Spinach tests for #{tags}: Retrying tests... #{tests}".red
+ puts ''
+ sleep(3)
+ success = run_spinach_command(tests)
end
+
+ raise("spinach tests for #{tags} failed!") unless success
end
diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh
index b6f076a90c3..82de51a9a2e 100755
--- a/scripts/prepare_build.sh
+++ b/scripts/prepare_build.sh
@@ -2,15 +2,27 @@
if [ -f /.dockerinit ]; then
mkdir -p vendor
- if [ ! -e vendor/phantomjs_1.9.8-0jessie_amd64.deb ]; then
+
+ # Install phantomjs package
+ pushd vendor
+ if [ ! -e phantomjs_1.9.8-0jessie_amd64.deb ]; then
wget -q https://gitlab.com/axil/phantomjs-debian/raw/master/phantomjs_1.9.8-0jessie_amd64.deb
- mv phantomjs_1.9.8-0jessie_amd64.deb vendor/
fi
- dpkg -i vendor/phantomjs_1.9.8-0jessie_amd64.deb
+ dpkg -i phantomjs_1.9.8-0jessie_amd64.deb
+ popd
+
+ # Try to install packages
+ for i in $(seq 1 3); do
+ apt-get update -yqqq || true
+
+ if apt-get -o dir::cache::archives="vendor/apt" install -y -qq --force-yes \
+ libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client unzip; then
+ break
+ fi
- apt-get update -qq
- apt-get -o dir::cache::archives="vendor/apt" install -y -qq --force-yes \
- libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client unzip
+ sleep 3s
+ echo "Retrying package installation..."
+ done
cp config/database.yml.mysql config/database.yml
sed -i 's/username:.*/username: root/g' config/database.yml
@@ -20,7 +32,7 @@ if [ -f /.dockerinit ]; then
cp config/resque.yml.example config/resque.yml
sed -i 's/localhost/redis/g' config/resque.yml
- export FLAGS=(--path vendor)
+ export FLAGS=(--path vendor --retry 3)
else
export PATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin
cp config/database.yml.mysql config/database.yml
diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb
index 883bbaedd4e..70ed8f3a62e 100644
--- a/spec/controllers/projects/forks_controller_spec.rb
+++ b/spec/controllers/projects/forks_controller_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Projects::ForksController do
let(:user) { create(:user) }
- let(:project) { create(:project, visibility_level: Project::PUBLIC) }
+ let(:project) { create(:project, :public) }
let(:forked_project) { Projects::ForkService.new(project, user).execute }
let(:group) { create(:group, owner: forked_project.creator) }
diff --git a/spec/factories/personal_snippets.rb b/spec/factories/personal_snippets.rb
index b493a6968ff..0f13b2c1020 100644
--- a/spec/factories/personal_snippets.rb
+++ b/spec/factories/personal_snippets.rb
@@ -1,15 +1,4 @@
FactoryGirl.define do
factory :personal_snippet, parent: :snippet, class: :PersonalSnippet do
- trait :public do
- visibility_level PersonalSnippet::PUBLIC
- end
-
- trait :internal do
- visibility_level PersonalSnippet::INTERNAL
- end
-
- trait :private do
- visibility_level PersonalSnippet::PRIVATE
- end
end
end
diff --git a/spec/factories/project_snippets.rb b/spec/factories/project_snippets.rb
index 154442bd3db..d681a2c8483 100644
--- a/spec/factories/project_snippets.rb
+++ b/spec/factories/project_snippets.rb
@@ -1,9 +1,5 @@
FactoryGirl.define do
- factory :project_snippet do
+ factory :project_snippet, parent: :snippet, class: :ProjectSnippet do
project
- author
- title
- content
- file_name
end
end
diff --git a/spec/factories/snippets.rb b/spec/factories/snippets.rb
index b9127b3d75e..365f12a0c95 100644
--- a/spec/factories/snippets.rb
+++ b/spec/factories/snippets.rb
@@ -12,5 +12,17 @@ FactoryGirl.define do
title
content
file_name
+
+ trait :public do
+ visibility_level Snippet::PUBLIC
+ end
+
+ trait :internal do
+ visibility_level Snippet::INTERNAL
+ end
+
+ trait :private do
+ visibility_level Snippet::PRIVATE
+ end
end
end
diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb
index 1b4ffc2d717..7fdc5e5d7aa 100644
--- a/spec/finders/snippets_finder_spec.rb
+++ b/spec/finders/snippets_finder_spec.rb
@@ -5,15 +5,14 @@ describe SnippetsFinder do
let(:user1) { create :user }
let(:group) { create :group }
- let(:project1) { create(:empty_project, :public, group: group) }
- let(:project2) { create(:empty_project, :private, group: group) }
-
+ let(:project1) { create(:empty_project, :public, group: group) }
+ let(:project2) { create(:empty_project, :private, group: group) }
context ':all filter' do
before do
- @snippet1 = create(:personal_snippet, visibility_level: Snippet::PRIVATE)
- @snippet2 = create(:personal_snippet, visibility_level: Snippet::INTERNAL)
- @snippet3 = create(:personal_snippet, visibility_level: Snippet::PUBLIC)
+ @snippet1 = create(:personal_snippet, :private)
+ @snippet2 = create(:personal_snippet, :internal)
+ @snippet3 = create(:personal_snippet, :public)
end
it "returns all private and internal snippets" do
@@ -31,9 +30,9 @@ describe SnippetsFinder do
context ':by_user filter' do
before do
- @snippet1 = create(:personal_snippet, visibility_level: Snippet::PRIVATE, author: user)
- @snippet2 = create(:personal_snippet, visibility_level: Snippet::INTERNAL, author: user)
- @snippet3 = create(:personal_snippet, visibility_level: Snippet::PUBLIC, author: user)
+ @snippet1 = create(:personal_snippet, :private, author: user)
+ @snippet2 = create(:personal_snippet, :internal, author: user)
+ @snippet3 = create(:personal_snippet, :public, author: user)
end
it "returns all public and internal snippets" do
@@ -75,9 +74,9 @@ describe SnippetsFinder do
context 'by_project filter' do
before do
- @snippet1 = create(:project_snippet, visibility_level: Snippet::PRIVATE, project: project1)
- @snippet2 = create(:project_snippet, visibility_level: Snippet::INTERNAL, project: project1)
- @snippet3 = create(:project_snippet, visibility_level: Snippet::PUBLIC, project: project1)
+ @snippet1 = create(:project_snippet, :private, project: project1)
+ @snippet2 = create(:project_snippet, :internal, project: project1)
+ @snippet3 = create(:project_snippet, :public, project: project1)
end
it "returns public snippets for unauthorized user" do
@@ -93,7 +92,7 @@ describe SnippetsFinder do
end
it "returns all snippets for project members" do
- project1.team << [user, :developer]
+ project1.team << [user, :developer]
snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1)
expect(snippets).to include(@snippet1, @snippet2, @snippet3)
end
diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb
index aafc24397a9..cd7596a763d 100644
--- a/spec/helpers/visibility_level_helper_spec.rb
+++ b/spec/helpers/visibility_level_helper_spec.rb
@@ -58,7 +58,7 @@ describe VisibilityLevelHelper do
describe "skip_level?" do
describe "forks" do
- let(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
+ let(:project) { create(:project, :internal) }
let(:fork_project) { create(:forked_project_with_submodules) }
before do
@@ -74,7 +74,7 @@ describe VisibilityLevelHelper do
end
describe "non-forked project" do
- let(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
+ let(:project) { create(:project, :internal) }
it "skips levels" do
expect(skip_level?(project, Gitlab::VisibilityLevel::PUBLIC)).to be_falsey
@@ -84,7 +84,7 @@ describe VisibilityLevelHelper do
end
describe "Snippet" do
- let(:snippet) { create(:snippet, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
+ let(:snippet) { create(:snippet, :internal) }
it "skips levels" do
expect(skip_level?(snippet, Gitlab::VisibilityLevel::PUBLIC)).to be_falsey
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
new file mode 100644
index 00000000000..c413132abe5
--- /dev/null
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe Gitlab::BitbucketImport::Importer, lib: true do
+ before do
+ Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "bitbucket")
+ end
+
+ let(:statuses) do
+ [
+ "open",
+ "resolved",
+ "on hold",
+ "invalid",
+ "duplicate",
+ "wontfix",
+ "closed" # undocumented status
+ ]
+ end
+ let(:sample_issues_statuses) do
+ issues = []
+
+ statuses.map.with_index do |status, index|
+ issues << {
+ local_id: index,
+ status: status,
+ title: "Issue #{index}",
+ content: "Some content to issue #{index}"
+ }
+ end
+
+ issues
+ end
+
+ let(:project_identifier) { 'namespace/repo' }
+ let(:data) do
+ {
+ bb_session: {
+ bitbucket_access_token: "123456",
+ bitbucket_access_token_secret: "secret"
+ }
+ }
+ end
+ let(:project) do
+ create(
+ :project,
+ import_source: project_identifier,
+ import_data: ProjectImportData.new(data: data)
+ )
+ end
+ let(:importer) { Gitlab::BitbucketImport::Importer.new(project) }
+ let(:issues_statuses_sample_data) do
+ {
+ count: sample_issues_statuses.count,
+ issues: sample_issues_statuses
+ }
+ end
+
+ context 'issues statuses' do
+ before do
+ stub_request(
+ :get,
+ "https://bitbucket.org/api/1.0/repositories/#{project_identifier}"
+ ).to_return(status: 200, body: { has_issues: true }.to_json)
+
+ stub_request(
+ :get,
+ "https://bitbucket.org/api/1.0/repositories/#{project_identifier}/issues?limit=50&sort=utc_created_on&start=0"
+ ).to_return(status: 200, body: issues_statuses_sample_data.to_json)
+
+ sample_issues_statuses.each_with_index do |issue, index|
+ stub_request(
+ :get,
+ "https://bitbucket.org/api/1.0/repositories/#{project_identifier}/issues/#{issue[:local_id]}/comments"
+ ).to_return(
+ status: 200,
+ body: [{ author_info: { username: "username" }, utc_created_on: index }].to_json
+ )
+ end
+ end
+
+ it 'map statuses to open or closed' do
+ importer.execute
+
+ expect(project.issues.where(state: "closed").size).to eq(5)
+ expect(project.issues.where(state: "opened").size).to eq(2)
+ end
+ end
+end
diff --git a/spec/models/project_snippet_spec.rb b/spec/models/project_snippet_spec.rb
index cc92eb0bd9f..e0feb606f78 100644
--- a/spec/models/project_snippet_spec.rb
+++ b/spec/models/project_snippet_spec.rb
@@ -10,7 +10,6 @@
# created_at :datetime
# updated_at :datetime
# file_name :string(255)
-# expires_at :datetime
# type :string(255)
# visibility_level :integer default(0), not null
#
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index f9842d23afa..c458d9c9b1b 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -561,7 +561,7 @@ describe Project, models: true do
end
describe '#visibility_level_allowed?' do
- let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL }
+ let(:project) { create(:project, :internal) }
context 'when checking on non-forked project' do
it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 1c7d66398cb..34866be3395 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -148,6 +148,12 @@ describe Repository, models: true do
expect(branch.name).to eq('new_feature')
end
+
+ it 'calls the after_create_branch hook' do
+ expect(repository).to receive(:after_create_branch)
+
+ repository.add_branch(user, 'new_feature', 'master')
+ end
end
context 'when pre hooks failed' do
@@ -405,7 +411,7 @@ describe Repository, models: true do
end
end
- describe '#expire_branch_ache' do
+ describe '#expire_branch_cache' do
# This method is private but we need it for testing purposes. Sadly there's
# no other proper way of testing caching operations.
let(:cache) { repository.send(:cache) }
@@ -556,11 +562,12 @@ describe Repository, models: true do
end
end
- describe '#before_create_tag' do
+ describe '#before_push_tag' do
it 'flushes the cache' do
expect(repository).to receive(:expire_cache)
+ expect(repository).to receive(:expire_tag_count_cache)
- repository.before_create_tag
+ repository.before_push_tag
end
end
@@ -595,4 +602,89 @@ describe Repository, models: true do
repository.after_remove_branch
end
end
+
+ describe "#main_language" do
+ it 'shows the main language of the project' do
+ expect(repository.main_language).to eq("Ruby")
+ end
+
+ it 'returns nil when the repository is empty' do
+ allow(repository).to receive(:empty?).and_return(true)
+
+ expect(repository.main_language).to be_nil
+ end
+ end
+
+ describe '#before_remove_tag' do
+ it 'flushes the tag cache' do
+ expect(repository).to receive(:expire_tag_count_cache)
+
+ repository.before_remove_tag
+ end
+ end
+
+ describe '#branch_count' do
+ it 'returns the number of branches' do
+ expect(repository.branch_count).to be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe '#tag_count' do
+ it 'returns the number of tags' do
+ expect(repository.tag_count).to be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe '#expire_branch_count_cache' do
+ let(:cache) { repository.send(:cache) }
+
+ it 'expires the cache' do
+ expect(cache).to receive(:expire).with(:branch_count)
+
+ repository.expire_branch_count_cache
+ end
+ end
+
+ describe '#expire_tag_count_cache' do
+ let(:cache) { repository.send(:cache) }
+
+ it 'expires the cache' do
+ expect(cache).to receive(:expire).with(:tag_count)
+
+ repository.expire_tag_count_cache
+ end
+ end
+
+ describe '#add_tag' do
+ it 'adds a tag' do
+ expect(repository).to receive(:before_push_tag)
+
+ expect_any_instance_of(Gitlab::Shell).to receive(:add_tag).
+ with(repository.path_with_namespace, '8.5', 'master', 'foo')
+
+ repository.add_tag('8.5', 'master', 'foo')
+ end
+ end
+
+ describe '#rm_branch' do
+ let(:user) { create(:user) }
+
+ it 'removes a branch' do
+ expect(repository).to receive(:before_remove_branch)
+ expect(repository).to receive(:after_remove_branch)
+
+ repository.rm_branch(user, 'feature')
+ end
+ end
+
+ describe '#rm_tag' do
+ it 'removes a tag' do
+ expect(repository).to receive(:before_remove_tag)
+
+ expect_any_instance_of(Gitlab::Shell).to receive(:rm_tag).
+ with(repository.path_with_namespace, '8.5')
+
+ repository.rm_tag('8.5')
+ end
+ end
end
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index eb2dbbdc5a4..7e5b5499aea 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -10,7 +10,6 @@
# created_at :datetime
# updated_at :datetime
# file_name :string(255)
-# expires_at :datetime
# type :string(255)
# visibility_level :integer default(0), not null
#
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index b82c5c7685f..96e8c8c51f8 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -47,6 +47,8 @@ describe API::API, api: true do
expect(json_response.first.keys).to include 'identities'
expect(json_response.first.keys).to include 'can_create_project'
expect(json_response.first.keys).to include 'two_factor_enabled'
+ expect(json_response.first.keys).to include 'last_sign_in_at'
+ expect(json_response.first.keys).to include 'confirmed_at'
end
end
end
diff --git a/spec/services/delete_tag_service_spec.rb b/spec/services/delete_tag_service_spec.rb
new file mode 100644
index 00000000000..5b7ba521812
--- /dev/null
+++ b/spec/services/delete_tag_service_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+describe DeleteTagService, services: true do
+ let(:project) { create(:project) }
+ let(:repository) { project.repository }
+ let(:user) { create(:user) }
+ let(:service) { described_class.new(project, user) }
+
+ let(:tag) { double(:tag, name: '8.5', target: 'abc123') }
+
+ describe '#execute' do
+ before do
+ allow(repository).to receive(:find_tag).and_return(tag)
+ end
+
+ it 'removes the tag' do
+ expect_any_instance_of(Gitlab::Shell).to receive(:rm_tag).
+ and_return(true)
+
+ expect(repository).to receive(:before_remove_tag)
+ expect(service).to receive(:success)
+
+ service.execute('8.5')
+ end
+ end
+end
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 994585fb32c..f5c51e46e8b 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -155,6 +155,23 @@ describe GitPushService, services: true do
end
end
+ describe "Updates main language" do
+
+ context "before push" do
+ it { expect(project.main_language).to eq(nil) }
+ end
+
+ context "after push" do
+ before do
+ @service = execute_service(project, user, @oldrev, @newrev, @ref)
+ end
+
+ it { expect(@service.update_main_language).to eq(true) }
+ it { expect(project.main_language).to eq("Ruby") }
+ end
+ end
+
+
describe "Web Hooks" do
context "execute web hooks" do
it "when pushing a branch for the first time" do
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index 3c06a890163..e8b9e6b9238 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -102,8 +102,8 @@ describe Projects::UpdateService, services: true do
describe :visibility_level do
let(:user) { create :user, admin: true }
- let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL }
- let(:forked_project) { create :forked_project_with_submodules, visibility_level: Gitlab::VisibilityLevel::INTERNAL }
+ let(:project) { create(:project, :internal) }
+ let(:forked_project) { create(:forked_project_with_submodules, :internal) }
let(:opts) { {} }
before do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 8f381f46e57..159fb964171 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -15,6 +15,7 @@ require 'rspec/rails'
require 'shoulda/matchers'
require 'sidekiq/testing/inline'
require 'benchmark/ips'
+require 'rspec/retry'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
@@ -25,6 +26,9 @@ RSpec.configure do |config|
config.use_instantiated_fixtures = false
config.mock_with :rspec
+ config.verbose_retry = true
+ config.display_try_failure_messages = true
+
config.include Devise::TestHelpers, type: :controller
config.include LoginHelpers, type: :feature
config.include LoginHelpers, type: :request