Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKamil Trzcinski <ayufan@ayufan.eu>2016-06-03 14:10:01 +0300
committerKamil Trzcinski <ayufan@ayufan.eu>2016-06-03 14:10:01 +0300
commit0a51c954641c99610e9eddd9323398fb00d273e2 (patch)
treece0e58fcec98f566d7c96af3234da1a8b644c267
parent3577b57f6b03a0d3c3d32daab6dc6ccf5f6e45f7 (diff)
parent3f4ac2ff60c9d83ec65b19070e4d054e12e67dd2 (diff)
Merge remote-tracking branch 'origin/master' into rename-ci-commit
-rw-r--r--.vagrant_enabled0
-rw-r--r--CHANGELOG30
-rw-r--r--Gemfile8
-rw-r--r--Gemfile.lock114
-rw-r--r--app/assets/javascripts/search_autocomplete.js.coffee22
-rw-r--r--app/assets/stylesheets/pages/detail_page.scss2
-rw-r--r--app/assets/stylesheets/pages/search.scss4
-rw-r--r--app/controllers/projects/branches_controller.rb2
-rw-r--r--app/helpers/todos_helper.rb4
-rw-r--r--app/models/ci/build.rb1
-rw-r--r--app/models/concerns/issuable.rb12
-rw-r--r--app/models/project.rb18
-rw-r--r--app/models/user.rb17
-rw-r--r--app/services/projects/create_service.rb26
-rw-r--r--app/services/projects/import_service.rb2
-rw-r--r--app/views/layouts/_search.html.haml7
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml4
-rw-r--r--app/views/layouts/nav/_project.html.haml19
-rw-r--r--app/views/projects/branches/destroy.js.haml1
-rw-r--r--app/views/projects/commits/_head.html.haml8
-rw-r--r--app/views/projects/pipelines/_head.html.haml2
-rw-r--r--app/views/projects/tags/destroy.js.haml1
-rw-r--r--app/views/projects/tree/show.html.haml1
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml10
-rw-r--r--app/workers/repository_fork_worker.rb6
-rw-r--r--app/workers/repository_import_worker.rb3
-rw-r--r--doc/development/ui_guide.md22
-rw-r--r--features/project/active_tab.feature37
-rw-r--r--features/project/builds/summary.feature1
-rw-r--r--features/project/shortcuts.feature8
-rw-r--r--features/steps/project/active_tab.rb4
-rw-r--r--features/steps/project/builds/summary.rb4
-rw-r--r--features/steps/project/project_find_file.rb4
-rw-r--r--features/steps/shared/project_tab.rb16
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb4
-rw-r--r--spec/features/projects/shortcuts_spec.rb (renamed from spec/features/project/shortcuts_spec.rb)0
-rw-r--r--spec/models/concerns/issuable_spec.rb14
-rw-r--r--spec/models/issue_spec.rb17
-rw-r--r--spec/models/merge_request_spec.rb17
-rw-r--r--spec/services/projects/import_service_spec.rb2
40 files changed, 270 insertions, 204 deletions
diff --git a/.vagrant_enabled b/.vagrant_enabled
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/.vagrant_enabled
diff --git a/CHANGELOG b/CHANGELOG
index 2467a45c0eb..ec026b8f398 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -7,6 +7,7 @@ v 8.9.0 (unreleased)
- Fix issue todo not remove when leave project !4150 (Long Nguyen)
- Allow forking projects with restricted visibility level
- Improve note validation to prevent errors when creating invalid note via API
+ - Reduce number of fog gem dependencies
- Remove project notification settings associated with deleted projects
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects
- Redesign navigation for project pages
@@ -27,15 +28,30 @@ v 8.9.0 (unreleased)
- Measure queue duration between gitlab-workhorse and Rails
- Make authentication service for Container Registry to be compatible with < Docker 1.11
- Add Application Setting to configure Container Registry token expire delay (default 5min)
+ - Cache assigned issue and merge request counts in sidebar nav
+ - Cache project build count in sidebar nav
+ - Reduce number of queries needed to render issue labels in the sidebar
+ - Improve error handling importing projects
+ - Put project Files and Commits tabs under Code tab
v 8.8.3
- - Fix incorrect links on pipeline page when merge request created from fork
- - Fix gitlab importer failing to import new projects due to missing credentials
- - Fix serious performance bug with rendering Markdown with InlineDiffFilter
- - Fix import URL migration not rescuing with the correct Error
- - In search results, only show notes on confidential issues that the user has access to
- - Fix health check access token changing due to old application settings being used
- - Fix wiki project clone address error (chujinjin)
+ - Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312
+ - Fixed JS error when trying to remove discussion form. !4303
+ - Fixed issue with button color when no CI enabled. !4287
+ - Fixed potential issue with 2 CI status polling events happening. !3869
+ - Improve design of Pipeline view. !4230
+ - Fix gitlab importer failing to import new projects due to missing credentials. !4301
+ - Fix import URL migration not rescuing with the correct Error. !4321
+ - Fix health check access token changing due to old application settings being used. !4332
+ - Make authentication service for Container Registry to be compatible with Docker versions before 1.11. !4363
+ - Add Application Setting to configure Container Registry token expire delay (default 5 min). !4364
+ - Pass the "Remember me" value to the 2FA token form. !4369
+ - Fix incorrect links on pipeline page when merge request created from fork. !4376
+ - Use downcased path to container repository as this is expected path by Docker. !4420
+ - Fix wiki project clone address error (chujinjin). !4429
+ - Fix serious performance bug with rendering Markdown with InlineDiffFilter. !4392
+ - Fix missing number on generated ordered list element. !4437
+ - Prevent disclosure of notes on confidential issues in search results.
v 8.8.2
- Added remove due date button. !4209
diff --git a/Gemfile b/Gemfile
index 0ab78c2a738..d9429de786f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -83,8 +83,14 @@ gem "carrierwave", '~> 0.10.0'
# Drag and Drop UI
gem 'dropzonejs-rails', '~> 0.7.1'
+# for backups
+gem 'fog-aws', '~> 0.9'
+gem 'fog-core', '~> 1.40'
+gem 'fog-local', '~> 0.3'
+gem 'fog-google', '~> 0.3'
+gem 'fog-openstack', '~> 0.1'
+
# for aws storage
-gem "fog", "~> 1.36.0"
gem "unf", '~> 0.1.4'
# Authorization
diff --git a/Gemfile.lock b/Gemfile.lock
index 4e000fa5b5b..8ae25269e65 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,7 +1,6 @@
GEM
remote: https://rubygems.org/
specs:
- CFPropertyList (2.3.2)
RedCloth (4.2.9)
ace-rails-ap (4.0.2)
actionmailer (4.2.6)
@@ -183,7 +182,7 @@ GEM
erubis (2.7.0)
escape_utils (1.1.1)
eventmachine (1.0.8)
- excon (0.45.4)
+ excon (0.49.0)
execjs (2.6.0)
expression_parser (0.9.0)
factory_girl (4.5.0)
@@ -200,8 +199,6 @@ GEM
multi_json
ffaker (2.0.0)
ffi (1.9.10)
- fission (0.5.0)
- CFPropertyList (~> 2.2)
flay (2.6.1)
ruby_parser (~> 3.0)
sexp_processor (~> 4.0)
@@ -211,109 +208,28 @@ GEM
flowdock (0.7.1)
httparty (~> 0.7)
multi_json
- fog (1.36.0)
- fog-aliyun (>= 0.1.0)
- fog-atmos
- fog-aws (>= 0.6.0)
- fog-brightbox (~> 0.4)
- fog-core (~> 1.32)
- fog-dynect (~> 0.0.2)
- fog-ecloud (~> 0.1)
- fog-google (<= 0.1.0)
- fog-json
- fog-local
- fog-powerdns (>= 0.1.1)
- fog-profitbricks
- fog-radosgw (>= 0.0.2)
- fog-riakcs
- fog-sakuracloud (>= 0.0.4)
- fog-serverlove
- fog-softlayer
- fog-storm_on_demand
- fog-terremark
- fog-vmfusion
- fog-voxel
- fog-xenserver
- fog-xml (~> 0.1.1)
- ipaddress (~> 0.5)
- nokogiri (~> 1.5, >= 1.5.11)
- fog-aliyun (0.1.0)
- fog-core (~> 1.27)
- fog-json (~> 1.0)
- ipaddress (~> 0.8)
- xml-simple (~> 1.1)
- fog-atmos (0.1.0)
- fog-core
- fog-xml
- fog-aws (0.8.1)
+ fog-aws (0.9.2)
fog-core (~> 1.27)
fog-json (~> 1.0)
fog-xml (~> 0.1)
ipaddress (~> 0.8)
- fog-brightbox (0.10.1)
- fog-core (~> 1.22)
- fog-json
- inflecto (~> 0.0.2)
- fog-core (1.35.0)
+ fog-core (1.40.0)
builder
- excon (~> 0.45)
+ excon (~> 0.49)
formatador (~> 0.2)
- fog-dynect (0.0.2)
- fog-core
- fog-json
- fog-xml
- fog-ecloud (0.3.0)
- fog-core
- fog-xml
- fog-google (0.1.0)
+ fog-google (0.3.2)
fog-core
fog-json
fog-xml
fog-json (1.0.2)
fog-core (~> 1.0)
multi_json (~> 1.10)
- fog-local (0.2.1)
+ fog-local (0.3.0)
fog-core (~> 1.27)
- fog-powerdns (0.1.1)
- fog-core (~> 1.27)
- fog-json (~> 1.0)
- fog-xml (~> 0.1)
- fog-profitbricks (0.0.5)
- fog-core
- fog-xml
- nokogiri
- fog-radosgw (0.0.5)
- fog-core (>= 1.21.0)
- fog-json
- fog-xml (>= 0.0.1)
- fog-riakcs (0.1.0)
- fog-core
- fog-json
- fog-xml
- fog-sakuracloud (1.7.5)
- fog-core
- fog-json
- fog-serverlove (0.1.2)
- fog-core
- fog-json
- fog-softlayer (1.0.3)
- fog-core
- fog-json
- fog-storm_on_demand (0.1.1)
- fog-core
- fog-json
- fog-terremark (0.1.0)
- fog-core
- fog-xml
- fog-vmfusion (0.1.0)
- fission
- fog-core
- fog-voxel (0.1.0)
- fog-core
- fog-xml
- fog-xenserver (0.2.2)
- fog-core
- fog-xml
+ fog-openstack (0.1.6)
+ fog-core (>= 1.39)
+ fog-json (>= 1.0)
+ ipaddress (>= 0.8)
fog-xml (0.1.2)
fog-core
nokogiri (~> 1.5, >= 1.5.11)
@@ -422,11 +338,10 @@ GEM
httpclient (2.7.0.1)
i18n (0.7.0)
ice_nine (0.11.1)
- inflecto (0.0.2)
influxdb (0.2.3)
cause
json
- ipaddress (0.8.2)
+ ipaddress (0.8.3)
jquery-atwho-rails (1.3.2)
jquery-rails (4.1.1)
rails-dom-testing (>= 1, < 3)
@@ -873,7 +788,6 @@ GEM
builder
expression_parser
rinku
- xml-simple (1.1.5)
xpath (2.0.0)
nokogiri (~> 1.3)
@@ -927,7 +841,11 @@ DEPENDENCIES
ffaker (~> 2.0.0)
flay
flog
- fog (~> 1.36.0)
+ fog-aws (~> 0.9)
+ fog-core (~> 1.40)
+ fog-google (~> 0.3)
+ fog-local (~> 0.3)
+ fog-openstack (~> 0.1)
font-awesome-rails (~> 4.2)
foreman
fuubar (~> 2.0.0)
diff --git a/app/assets/javascripts/search_autocomplete.js.coffee b/app/assets/javascripts/search_autocomplete.js.coffee
index 6a7b4ad1db7..2122e80f57a 100644
--- a/app/assets/javascripts/search_autocomplete.js.coffee
+++ b/app/assets/javascripts/search_autocomplete.js.coffee
@@ -20,8 +20,7 @@ class @SearchAutocomplete
@dropdown = @wrap.find('.dropdown')
@dropdownContent = @dropdown.find('.dropdown-content')
- @locationBadgeEl = @getElement('.search-location-badge')
- @locationText = @getElement('.location-text')
+ @locationBadgeEl = @getElement('.location-badge')
@scopeInputEl = @getElement('#scope')
@searchInput = @getElement('.search-input')
@projectInputEl = @getElement('#search_project_id')
@@ -133,7 +132,7 @@ class @SearchAutocomplete
scope: @scopeInputEl.val()
# Location badge
- _location: @locationText.text()
+ _location: @locationBadgeEl.text()
}
bindEvents: ->
@@ -143,12 +142,14 @@ class @SearchAutocomplete
@searchInput.on 'click', @onSearchInputClick
@searchInput.on 'focus', @onSearchInputFocus
@clearInput.on 'click', @onClearInputClick
+ @locationBadgeEl.on 'click', =>
+ @searchInput.focus()
onDocumentClick: (e) =>
# If clicking outside the search box
# And search input is not focused
# And we are not clicking inside a suggestion
- if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).parents('ul').length
+ if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).closest('.search-form').length
@onSearchInputBlur()
enableAutocomplete: ->
@@ -221,10 +222,8 @@ class @SearchAutocomplete
category = if item.category? then "#{item.category}: " else ''
value = if item.value? then item.value else ''
- html = "<span class='location-badge'>
- <i class='location-text'>#{category}#{value}</i>
- </span>"
- @locationBadgeEl.html(html)
+ badgeText = "#{category}#{value}"
+ @locationBadgeEl.text(badgeText).show()
@wrap.addClass('has-location-badge')
restoreOriginalState: ->
@@ -233,9 +232,8 @@ class @SearchAutocomplete
for input in inputs
@getElement("##{input}").val(@originalState[input])
-
if @originalState._location is ''
- @locationBadgeEl.empty()
+ @locationBadgeEl.hide()
else
@addLocationBadge(
value: @originalState._location
@@ -244,7 +242,7 @@ class @SearchAutocomplete
@dropdown.removeClass 'open'
badgePresent: ->
- @locationBadgeEl.children().length
+ @locationBadgeEl.length
resetSearchState: ->
inputs = Object.keys @originalState
@@ -257,7 +255,7 @@ class @SearchAutocomplete
@getElement("##{input}").val('')
removeLocationBadge: ->
- @locationBadgeEl.empty()
+ @locationBadgeEl.hide()
# Reset state
@resetSearchState()
diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss
index 5e61e61d85c..1b389d83525 100644
--- a/app/assets/stylesheets/pages/detail_page.scss
+++ b/app/assets/stylesheets/pages/detail_page.scss
@@ -29,8 +29,6 @@
margin-top: 6px;
p {
- overflow-x: auto;
-
&:last-child {
margin-bottom: 0;
}
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 2bff70c8c64..037ad520545 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -28,6 +28,7 @@
}
.search-input {
+ padding-right: 20px;
border: none;
font-size: 14px;
outline: none;
@@ -47,6 +48,7 @@
display: inline-block;
background-color: $location-badge-bg;
vertical-align: top;
+ cursor: default;
}
.search-input-container {
@@ -55,7 +57,7 @@
position: relative;
}
- .search-location-badge, .search-input-wrap {
+ .search-input-wrap {
// Fallback if flexbox is not supported
display: inline-block;
}
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index d09e7375b67..dd9508da049 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -50,7 +50,7 @@ class Projects::BranchesController < Projects::ApplicationController
redirect_to namespace_project_branches_path(@project.namespace,
@project), status: 303
end
- format.js { render status: status[:return_code] }
+ format.js { render nothing: true, status: status[:return_code] }
end
end
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index b9d7edb4185..b4923fbb138 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -17,7 +17,9 @@ module TodosHelper
def todo_target_link(todo)
target = todo.target_type.titleize.downcase
- link_to "#{target} #{todo.target_reference}", todo_target_path(todo), { title: todo.target.title }
+ link_to "#{target} #{todo.target_reference}", todo_target_path(todo),
+ class: 'has-tooltip',
+ title: todo.target.title
end
def todo_target_path(todo)
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 77837e9384a..b8ada6361ac 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -313,6 +313,7 @@ module Ci
build_data = Gitlab::BuildDataBuilder.build(self)
project.execute_hooks(build_data.dup, :build_hooks)
project.execute_services(build_data.dup, :build_hooks)
+ project.running_or_pending_build_count(force: true)
end
def artifacts?
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 2326a395cb8..e86d5236abb 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -68,6 +68,14 @@ module Issuable
strip_attributes :title
acts_as_paranoid
+
+ after_save :update_assignee_cache_counts, if: :assignee_id_changed?
+
+ def update_assignee_cache_counts
+ # make sure we flush the cache for both the old *and* new assignee
+ User.find(assignee_id_was).update_cache_counts if assignee_id_was
+ assignee.update_cache_counts if assignee
+ end
end
module ClassMethods
@@ -205,6 +213,10 @@ module Issuable
hook_data
end
+ def labels_array
+ labels.to_a
+ end
+
def label_names
labels.order('title ASC').pluck(:title)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index e0ea1026b9c..f47ef8a81de 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1011,4 +1011,22 @@ class Project < ActiveRecord::Base
update_attribute(:pending_delete, true)
end
+
+ def running_or_pending_build_count(force: false)
+ Rails.cache.fetch(['projects', id, 'running_or_pending_build_count'], force: force) do
+ builds.running_or_pending.count(:all)
+ end
+ end
+
+ def mark_import_as_failed(error_message)
+ original_errors = errors.dup
+ sanitized_message = Gitlab::UrlSanitizer.sanitize(error_message)
+
+ import_fail
+ update_column(:import_error, sanitized_message)
+ rescue ActiveRecord::ActiveRecordError => e
+ Rails.logger.error("Error setting import status to failed: #{e.message}. Original error: #{sanitized_message}")
+ ensure
+ @errors = original_errors
+ end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 15b6cbc2255..172845c9d25 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -776,6 +776,23 @@ class User < ActiveRecord::Base
notification_settings.find_or_initialize_by(source: source)
end
+ def assigned_open_merge_request_count(force: false)
+ Rails.cache.fetch(['users', id, 'assigned_open_merge_request_count'], force: force) do
+ assigned_merge_requests.opened.count
+ end
+ end
+
+ def assigned_open_issues_count(force: false)
+ Rails.cache.fetch(['users', id, 'assigned_open_issues_count'], force: force) do
+ assigned_issues.opened.count
+ end
+ end
+
+ def update_cache_counts
+ assigned_open_merge_request_count(force: true)
+ assigned_open_issues_count(force: true)
+ end
+
private
def projects_union
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 6728fabea1e..61cac5419ad 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -56,14 +56,14 @@ module Projects
after_create_actions if @project.persisted?
- @project.add_import_job if @project.import?
-
+ if @project.errors.empty?
+ @project.add_import_job if @project.import?
+ else
+ fail(error: @project.errors.full_messages.join(', '))
+ end
@project
rescue => e
- message = "Unable to save project: #{e.message}"
- Rails.logger.error(message)
- @project.errors.add(:base, message) if @project
- @project
+ fail(error: e.message)
end
protected
@@ -103,5 +103,19 @@ module Projects
end
end
end
+
+ def fail(error:)
+ message = "Unable to save project. Error: #{error}"
+ message << "Project ID: #{@project.id}" if @project && @project.id
+
+ Rails.logger.error(message)
+
+ if @project && @project.import?
+ @project.errors.add(:base, message)
+ @project.mark_import_as_failed(message)
+ end
+
+ @project
+ end
end
end
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index ef15ef6a473..c4838d31f2f 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -39,7 +39,7 @@ module Projects
begin
gitlab_shell.import_repository(project.path_with_namespace, project.import_url)
rescue Gitlab::Shell::Error => e
- raise Error, e.message
+ raise Error, "Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}"
end
end
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 6b208c3d0bb..b49207fc315 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -6,11 +6,8 @@
.search.search-form{class: "#{'has-location-badge' if label.present?}"}
= form_tag search_path, method: :get, class: 'navbar-form' do |f|
.search-input-container
- .search-location-badge
- - if label.present?
- %span.location-badge
- %i.location-text
- = label
+ - if label.present?
+ .location-badge= label
.search-input-wrap
.dropdown{ data: {url: search_autocomplete_path } }
= search_field_tag "search", nil, placeholder: 'Search', class: "search-input dropdown-menu-toggle", spellcheck: false, tabindex: "1", autocomplete: 'off', data: { toggle: 'dropdown' }
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 43532b0c155..306ebd5fcf7 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -30,13 +30,13 @@
= icon('exclamation-circle fw')
%span
Issues
- %span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
+ %span.count= number_with_delimiter(current_user.assigned_open_issues_count)
= nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
= icon('tasks fw')
%span
Merge Requests
- %span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
+ %span.count= number_with_delimiter(current_user.assigned_open_merge_request_count)
= nav_link(controller: :snippets) do
= link_to dashboard_snippets_path, title: 'Snippets' do
= icon('clipboard fw')
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 2c9b9006668..9792c1c93b4 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -33,18 +33,11 @@
%span
Activity
- if project_nav_tab? :files
- = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
+ = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases network)) do
= link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree' do
- = icon('files-o fw')
+ = icon('code fw')
%span
- Files
-
- - if project_nav_tab? :commits
- = nav_link(controller: %w(commit commits compare repositories tags branches releases network)) do
- = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
- = icon('history fw')
- %span
- Commits
+ Code
- if project_nav_tab? :pipelines
= nav_link(controller: :pipelines) do
@@ -129,4 +122,10 @@
= link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do
Builds
+ -# Shortcut to commits page
+ - if project_nav_tab? :commits
+ %li.hidden
+ = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
+ Commits
+
.fade-right
diff --git a/app/views/projects/branches/destroy.js.haml b/app/views/projects/branches/destroy.js.haml
deleted file mode 100644
index a21ddaf4930..00000000000
--- a/app/views/projects/branches/destroy.js.haml
+++ /dev/null
@@ -1 +0,0 @@
-$('.js-totalbranch-count').html("#{@repository.branch_count}")
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index d1bd76ab529..1c136133ab0 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -1,9 +1,11 @@
%ul.nav-links
+ = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
+ = link_to project_files_path(@project) do
+ Files
+
= nav_link(controller: [:commit, :commits]) do
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
Commits
- %span.badge
- = number_with_delimiter(@repository.commit_count)
= nav_link(controller: %w(network)) do
= link_to namespace_project_network_path(@project.namespace, @project, current_ref) do
@@ -16,9 +18,7 @@
= 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.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.tag_count
diff --git a/app/views/projects/pipelines/_head.html.haml b/app/views/projects/pipelines/_head.html.haml
index 2c8ae625e67..6e757df5417 100644
--- a/app/views/projects/pipelines/_head.html.haml
+++ b/app/views/projects/pipelines/_head.html.haml
@@ -11,4 +11,4 @@
= link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do
%span
Builds
- %span.badge.count.builds_counter= number_with_delimiter(@project.builds.running_or_pending.count(:all))
+ %span.badge.count.builds_counter= number_with_delimiter(@project.running_or_pending_build_count)
diff --git a/app/views/projects/tags/destroy.js.haml b/app/views/projects/tags/destroy.js.haml
index ffeacb5a004..e4a78fadbeb 100644
--- a/app/views/projects/tags/destroy.js.haml
+++ b/app/views/projects/tags/destroy.js.haml
@@ -1,3 +1,2 @@
-$('.js-totaltags-count').html("#{@repository.tags.size}");
- if @repository.tags.empty?
$('.tags').load(document.URL + ' .nothing-here-block').hide().fadeIn(1000)
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index 7e9ba09c720..59f60c4687c 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -3,6 +3,7 @@
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
= render 'projects/last_push'
+= render "projects/commits/head"
.tree-controls
= render 'projects/find_file_link'
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index c1eec450193..d6552ae7f18 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -114,20 +114,20 @@
.sidebar-collapsed-icon
= icon('tags')
%span
- = issuable.labels.count
+ = issuable.labels_array.size
.title.hide-collapsed
Labels
= icon('spinner spin', class: 'block-loading')
- if can_edit_issuable
= link_to 'Edit', '#', class: 'edit-link pull-right'
- .value.bold.issuable-show-labels.hide-collapsed{ class: ("has-labels" if issuable.labels.any?) }
- - if issuable.labels.any?
- - issuable.labels.each do |label|
+ .value.bold.issuable-show-labels.hide-collapsed{ class: ("has-labels" if issuable.labels_array.any?) }
+ - if issuable.labels_array.any?
+ - issuable.labels_array.each do |label|
= link_to_label(label, type: issuable.to_ability_name)
- else
.light None
.selectbox.hide-collapsed
- - issuable.labels.each do |label|
+ - issuable.labels_array.each do |label|
= hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil
.dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect{type: "button", data: {toggle: "dropdown", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", project_id: (@project.id if @project), issue_update: issuable_json_path(issuable), labels: (namespace_project_labels_path(@project.namespace, @project, :json) if @project)}}
diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb
index f9e32337983..d947f105516 100644
--- a/app/workers/repository_fork_worker.rb
+++ b/app/workers/repository_fork_worker.rb
@@ -15,8 +15,7 @@ class RepositoryForkWorker
result = gitlab_shell.fork_repository(source_path, target_path)
unless result
logger.error("Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}")
- project.update(import_error: "The project could not be forked.")
- project.import_fail
+ project.mark_import_as_failed('The project could not be forked.')
return
end
@@ -24,8 +23,7 @@ class RepositoryForkWorker
unless project.valid_repo?
logger.error("Project #{project_id} had an invalid repository after fork")
- project.update(import_error: "The forked repository is invalid.")
- project.import_fail
+ project.mark_import_as_failed('The forked repository is invalid.')
return
end
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index fbc7ed63c6a..7d819fe78f8 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -13,8 +13,7 @@ class RepositoryImportWorker
result = Projects::ImportService.new(project, current_user).execute
if result[:status] == :error
- project.update(import_error: Gitlab::UrlSanitizer.sanitize(result[:message]))
- project.import_fail
+ project.mark_import_as_failed(result[:message])
return
end
diff --git a/doc/development/ui_guide.md b/doc/development/ui_guide.md
index b4dcb748351..23760a14b39 100644
--- a/doc/development/ui_guide.md
+++ b/doc/development/ui_guide.md
@@ -33,4 +33,24 @@ be under 'Wiki' tab and so on and so forth.
We want GitLab to work well on small mobile screens as well. Size limitations make it is impossible to fit everything on a mobile screen. In this case it is OK to hide
part of the UI for smaller resolutions in favor of a better user experience.
However core functionality like browsing files, creating issues, writing comments, should
-be available on all resolutions. \ No newline at end of file
+be available on all resolutions.
+
+## Icons
+
+* `trash` icon for button or link that does destructive action like removing
+information from database or file system
+* `x` icon for closing/hiding UI element. For example close modal window
+* `pencil` icon for edit button or link
+* `eye` icon for subscribe action
+* `rss` for rss/atom feed
+* `plus` for link or dropdown that lead to page where you create new object (For example new issue page)
+
+
+## Buttons
+
+* Button should contain icon or text. Exceptions should be approved by UX designer.
+* Use gray button on white background or white button on gray background.
+* Use red button for destructive actions (not revertable). For example removing issue.
+* Use green or blue button for primary action. Primary button should be only one.
+Do not use both green and blue button in one form.
+
diff --git a/features/project/active_tab.feature b/features/project/active_tab.feature
index 5125a3e5773..26e67503021 100644
--- a/features/project/active_tab.feature
+++ b/features/project/active_tab.feature
@@ -10,14 +10,9 @@ Feature: Project Active Tab
Then the active main tab should be Home
And no other main tabs should be active
- Scenario: On Project Files
+ Scenario: On Project Code
Given I visit my project's files page
- Then the active main tab should be Files
- And no other main tabs should be active
-
- Scenario: On Project Commits
- Given I visit my project's commits page
- Then the active main tab should be Commits
+ Then the active main tab should be Code
And no other main tabs should be active
Scenario: On Project Issues
@@ -64,40 +59,46 @@ Feature: Project Active Tab
And no other sub navs should be active
And the active main tab should be Settings
- # Sub Tabs: Commits
+ # Sub Tabs: Code
+
+ Scenario: On Project Code/Files
+ Given I visit my project's files page
+ Then the active sub tab should be Files
+ And no other sub tabs should be active
+ And the active main tab should be Code
- Scenario: On Project Commits/Commits
+ Scenario: On Project Code/Commits
Given I visit my project's commits page
Then the active sub tab should be Commits
And no other sub tabs should be active
- And the active main tab should be Commits
+ And the active main tab should be Code
- Scenario: On Project Commits/Network
+ Scenario: On Project Code/Network
Given I visit my project's network page
Then the active sub tab should be Network
And no other sub tabs should be active
- And the active main tab should be Commits
+ And the active main tab should be Code
- Scenario: On Project Commits/Compare
+ Scenario: On Project Code/Compare
Given I visit my project's commits page
And I click the "Compare" tab
Then the active sub tab should be Compare
And no other sub tabs should be active
- And the active main tab should be Commits
+ And the active main tab should be Code
- Scenario: On Project Commits/Branches
+ Scenario: On Project Code/Branches
Given I visit my project's commits page
And I click the "Branches" tab
Then the active sub tab should be Branches
And no other sub tabs should be active
- And the active main tab should be Commits
+ And the active main tab should be Code
- Scenario: On Project Commits/Tags
+ Scenario: On Project Code/Tags
Given I visit my project's commits page
And I click the "Tags" tab
Then the active sub tab should be Tags
And no other sub tabs should be active
- And the active main tab should be Commits
+ And the active main tab should be Code
Scenario: On Project Issues/Browse
Given I visit my project's issues page
diff --git a/features/project/builds/summary.feature b/features/project/builds/summary.feature
index 3c029a973df..550ebccf0d7 100644
--- a/features/project/builds/summary.feature
+++ b/features/project/builds/summary.feature
@@ -24,3 +24,4 @@ Feature: Project Builds Summary
Then recent build has been erased
And recent build summary does not have artifacts widget
And recent build summary contains information saying that build has been erased
+ And the build count cache is updated
diff --git a/features/project/shortcuts.feature b/features/project/shortcuts.feature
index 10e7c234610..c73d0b32337 100644
--- a/features/project/shortcuts.feature
+++ b/features/project/shortcuts.feature
@@ -8,19 +8,21 @@ Feature: Project Shortcuts
@javascript
Scenario: Navigate to files tab
Given I press "g" and "f"
- Then the active main tab should be Files
+ Then the active main tab should be Code
+ Then the active sub tab should be Files
@javascript
Scenario: Navigate to commits tab
Given I visit my project's files page
Given I press "g" and "c"
- Then the active main tab should be Commits
+ Then the active main tab should be Code
+ Then the active sub tab should be Commits
@javascript
Scenario: Navigate to network tab
Given I press "g" and "n"
Then the active sub tab should be Network
- And the active main tab should be Commits
+ And the active main tab should be Code
@javascript
Scenario: Navigate to graphs tab
diff --git a/features/steps/project/active_tab.rb b/features/steps/project/active_tab.rb
index 4a5a71e7e61..745fd3471c4 100644
--- a/features/steps/project/active_tab.rb
+++ b/features/steps/project/active_tab.rb
@@ -63,10 +63,6 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
click_link('Tags')
end
- step 'the active sub tab should be Commits' do
- ensure_active_sub_tab('Commits')
- end
-
step 'the active sub tab should be Compare' do
ensure_active_sub_tab('Compare')
end
diff --git a/features/steps/project/builds/summary.rb b/features/steps/project/builds/summary.rb
index e9e2359146e..374eb0b0e07 100644
--- a/features/steps/project/builds/summary.rb
+++ b/features/steps/project/builds/summary.rb
@@ -36,4 +36,8 @@ class Spinach::Features::ProjectBuildsSummary < Spinach::FeatureSteps
expect(page).to have_content 'Build has been erased'
end
end
+
+ step 'the build count cache is updated' do
+ expect(@build.project.running_or_pending_build_count).to eq @build.project.builds.running_or_pending.count(:all)
+ end
end
diff --git a/features/steps/project/project_find_file.rb b/features/steps/project/project_find_file.rb
index 8c1d09d6cc6..47de4b91df1 100644
--- a/features/steps/project/project_find_file.rb
+++ b/features/steps/project/project_find_file.rb
@@ -13,12 +13,12 @@ class Spinach::Features::ProjectFindFile < Spinach::FeatureSteps
end
step 'I should see "find file" page' do
- ensure_active_main_tab('Files')
+ ensure_active_main_tab('Code')
expect(page).to have_selector('.file-finder-holder', count: 1)
end
step 'I fill in Find by path with "git"' do
- ensure_active_main_tab('Files')
+ ensure_active_main_tab('Code')
expect(page).to have_selector('.file-finder-holder', count: 1)
end
diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb
index b209020c5a9..bfee8793301 100644
--- a/features/steps/shared/project_tab.rb
+++ b/features/steps/shared/project_tab.rb
@@ -8,12 +8,8 @@ module SharedProjectTab
ensure_active_main_tab('Project')
end
- step 'the active main tab should be Files' do
- ensure_active_main_tab('Files')
- end
-
- step 'the active main tab should be Commits' do
- ensure_active_main_tab('Commits')
+ step 'the active main tab should be Code' do
+ ensure_active_main_tab('Code')
end
step 'the active main tab should be Graphs' do
@@ -51,4 +47,12 @@ module SharedProjectTab
step 'the active sub tab should be Network' do
ensure_active_sub_tab('Network')
end
+
+ step 'the active sub tab should be Files' do
+ ensure_active_sub_tab('Files')
+ end
+
+ step 'the active sub tab should be Commits' do
+ ensure_active_sub_tab('Commits')
+ end
end
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index 8ad73472117..c4b4a888b4e 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -122,27 +122,23 @@ describe Projects::BranchesController do
let(:branch) { "feature" }
it { expect(response.status).to eq(200) }
- it { expect(subject).to render_template('destroy') }
end
context "valid branch name with unencoded slashes" do
let(:branch) { "improve/awesome" }
it { expect(response.status).to eq(200) }
- it { expect(subject).to render_template('destroy') }
end
context "valid branch name with encoded slashes" do
let(:branch) { "improve%2Fawesome" }
it { expect(response.status).to eq(200) }
- it { expect(subject).to render_template('destroy') }
end
context "invalid branch name, valid ref" do
let(:branch) { "no-branch" }
it { expect(response.status).to eq(404) }
- it { expect(subject).to render_template('destroy') }
end
end
end
diff --git a/spec/features/project/shortcuts_spec.rb b/spec/features/projects/shortcuts_spec.rb
index 54aa9c66a08..54aa9c66a08 100644
--- a/spec/features/project/shortcuts_spec.rb
+++ b/spec/features/projects/shortcuts_spec.rb
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index fb20578d8d3..e9f827e9f50 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -227,6 +227,20 @@ describe Issue, "Issuable" do
end
end
+ describe '#labels_array' do
+ let(:project) { create(:project) }
+ let(:bug) { create(:label, project: project, title: 'bug') }
+ let(:issue) { create(:issue, project: project) }
+
+ before(:each) do
+ issue.labels << bug
+ end
+
+ it 'loads the association and returns it as an array' do
+ expect(issue.reload.labels_array).to eq([bug])
+ end
+ end
+
describe "votes" do
let(:project) { issue.project }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 87b3d8d650a..b87d68283e6 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -269,4 +269,21 @@ describe Issue, models: true do
end
end
end
+
+ describe 'cached counts' do
+ it 'updates when assignees change' do
+ user1 = create(:user)
+ user2 = create(:user)
+ issue = create(:issue, assignee: user1)
+
+ expect(user1.assigned_open_issues_count).to eq(1)
+ expect(user2.assigned_open_issues_count).to eq(0)
+
+ issue.assignee = user2
+ issue.save
+
+ expect(user1.assigned_open_issues_count).to eq(0)
+ expect(user2.assigned_open_issues_count).to eq(1)
+ end
+ end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 23d09331e5a..348f2e2f493 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -438,4 +438,21 @@ describe MergeRequest, models: true do
expect(mr.participants).to include(note1.author, note2.author)
end
end
+
+ describe 'cached counts' do
+ it 'updates when assignees change' do
+ user1 = create(:user)
+ user2 = create(:user)
+ mr = create(:merge_request, assignee: user1)
+
+ expect(user1.assigned_open_merge_request_count).to eq(1)
+ expect(user2.assigned_open_merge_request_count).to eq(0)
+
+ mr.assignee = user2
+ mr.save
+
+ expect(user1.assigned_open_merge_request_count).to eq(0)
+ expect(user2.assigned_open_merge_request_count).to eq(1)
+ end
+ end
end
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index 7f2dcdab960..9d90bfceb73 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -49,7 +49,7 @@ describe Projects::ImportService, services: true do
result = subject.execute
expect(result[:status]).to eq :error
- expect(result[:message]).to eq 'Failed to import the repository'
+ expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - Failed to import the repository"
end
end