Commit 02e8beaa authored by Han Loong Liauw's avatar Han Loong Liauw

Merge branch 'master' into remove-forks-from-projects-settings

parents 0bea5ced c856a7a5
...@@ -25,7 +25,6 @@ config/initializers/rack_attack.rb ...@@ -25,7 +25,6 @@ config/initializers/rack_attack.rb
config/initializers/smtp_settings.rb config/initializers/smtp_settings.rb
config/resque.yml config/resque.yml
config/unicorn.rb config/unicorn.rb
config/mail_room.yml
config/secrets.yml config/secrets.yml
coverage/* coverage/*
db/*.sqlite3 db/*.sqlite3
......
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.2.0 (unreleased)
- Show last project commit to default branch on project home page
- Highlight comment based on anchor in URL
v 8.1.0 (unreleased) v 8.1.0 (unreleased)
- Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu)
- Speed up load times of issue detail pages by roughly 1.5x
- Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu)
- Make diff file view easier to use on mobile screens (Stan Hu) - Make diff file view easier to use on mobile screens (Stan Hu)
- Improved performance of finding users by username or Email address
- Fix bug where merge request comments created by API would not trigger notifications (Stan Hu)
- Add support for creating directories from Files page (Stan Hu) - Add support for creating directories from Files page (Stan Hu)
- Allow removing of project without confirmation when JavaScript is disabled (Stan Hu) - Allow removing of project without confirmation when JavaScript is disabled (Stan Hu)
- Support filtering by "Any" milestone or issue and fix "No Milestone" and "No Label" filters (Stan Hu) - Support filtering by "Any" milestone or issue and fix "No Milestone" and "No Label" filters (Stan Hu)
...@@ -16,7 +25,11 @@ v 8.1.0 (unreleased) ...@@ -16,7 +25,11 @@ v 8.1.0 (unreleased)
- Move CI charts to project graphs area - Move CI charts to project graphs area
- Fix cases where Markdown did not render links in activity feed (Stan Hu) - Fix cases where Markdown did not render links in activity feed (Stan Hu)
- Add first and last to pagination (Zeger-Jan van de Weg) - Add first and last to pagination (Zeger-Jan van de Weg)
- Added Commit Status API
- Added Builds View
- Added when to .gitlab-ci.yml
- Show CI status on commit page - Show CI status on commit page
- Added CI_BUILD_TAG, _STAGE, _NAME and _TRIGGERED to CI builds
- Show CI status on Your projects page and Starred projects page - Show CI status on Your projects page and Starred projects page
- Remove "Continuous Integration" page from dashboard - Remove "Continuous Integration" page from dashboard
- Add notes and SSL verification entries to hook APIs (Ben Boeckel) - Add notes and SSL verification entries to hook APIs (Ben Boeckel)
...@@ -26,6 +39,7 @@ v 8.1.0 (unreleased) ...@@ -26,6 +39,7 @@ v 8.1.0 (unreleased)
- Move CI triggers page to project settings area - Move CI triggers page to project settings area
- Move CI project settings page to CE project settings area - Move CI project settings page to CE project settings area
- Fix bug when removed file was not appearing in merge request diff - Fix bug when removed file was not appearing in merge request diff
- Show warning when build cannot be served by any of the available CI runners
- Note the original location of a moved project when notifying users of the move - Note the original location of a moved project when notifying users of the move
- Improve error message when merging fails - Improve error message when merging fails
- Add support of multibyte characters in LDAP UID (Roman Petrov) - Add support of multibyte characters in LDAP UID (Roman Petrov)
...@@ -47,8 +61,15 @@ v 8.1.0 (unreleased) ...@@ -47,8 +61,15 @@ v 8.1.0 (unreleased)
- Persist filters when sorting on admin user page (Jerry Lukins) - Persist filters when sorting on admin user page (Jerry Lukins)
- Adds ability to remove the forked relationship from project settings - Adds ability to remove the forked relationship from project settings
screen. (Han Loong Liauw) screen. (Han Loong Liauw)
- Allow dashboard and group issues/MRs to be filtered by label
- Add spellcheck=false to certain input fields - Add spellcheck=false to certain input fields
- Invalidate stored service password if the endpoint URL is changed - Invalidate stored service password if the endpoint URL is changed
- Project names are not fully shown if group name is too big, even on group page view
- Apply new design for Files page
- Add "New Page" button to Wiki Pages tab (Stan Hu)
- Only render 404 page from /public
- Hide passwords from services API (Alex Lossent)
- Fix: Images cannot show when projects' path was changed
v 8.0.4 v 8.0.4
- Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu)
...@@ -63,6 +84,7 @@ v 8.0.3 ...@@ -63,6 +84,7 @@ v 8.0.3
- Fix URL shown in Slack notifications - Fix URL shown in Slack notifications
- Fix bug where projects would appear to be stuck in the forked import state (Stan Hu) - Fix bug where projects would appear to be stuck in the forked import state (Stan Hu)
- Fix Error 500 in creating merge requests with > 1000 diffs (Stan Hu) - Fix Error 500 in creating merge requests with > 1000 diffs (Stan Hu)
- Add work_in_progress key to MR web hooks (Ben Boeckel)
v 8.0.2 v 8.0.2
- Fix default avatar not rendering in network graph (Stan Hu) - Fix default avatar not rendering in network graph (Stan Hu)
......
source "https://rubygems.org" source "https://rubygems.org"
def darwin_only(require_as)
RUBY_PLATFORM.include?('darwin') && require_as
end
def linux_only(require_as)
RUBY_PLATFORM.include?('linux') && require_as
end
gem 'rails', '4.1.12' gem 'rails', '4.1.12'
# Specify a sprockets version due to security issue # Specify a sprockets version due to security issue
...@@ -47,7 +39,7 @@ gem "browser", '~> 1.0.0' ...@@ -47,7 +39,7 @@ gem "browser", '~> 1.0.0'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
gem "gitlab_git", '~> 7.2.18' gem "gitlab_git", '~> 7.2.19'
# LDAP Auth # LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes # GitLab fork with several improvements to original library. For full list of changes
...@@ -102,7 +94,7 @@ gem "seed-fu", '~> 2.3.5' ...@@ -102,7 +94,7 @@ gem "seed-fu", '~> 2.3.5'
gem 'html-pipeline', '~> 1.11.0' gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '~> 1.0.2', require: 'task_list/railtie' gem 'task_list', '~> 1.0.2', require: 'task_list/railtie'
gem 'github-markup', '~> 1.3.1' gem 'github-markup', '~> 1.3.1'
gem 'redcarpet', '~> 3.3.2' gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.2.9' gem 'RedCloth', '~> 4.2.9'
gem 'rdoc', '~>3.6' gem 'rdoc', '~>3.6'
gem 'org-ruby', '~> 0.9.12' gem 'org-ruby', '~> 0.9.12'
...@@ -196,7 +188,7 @@ gem 'charlock_holmes', '~> 0.6.9.4' ...@@ -196,7 +188,7 @@ gem 'charlock_holmes', '~> 0.6.9.4'
gem "sass-rails", '~> 4.0.5' gem "sass-rails", '~> 4.0.5'
gem "coffee-rails", '~> 4.1.0' gem "coffee-rails", '~> 4.1.0'
gem "uglifier", '~> 2.3.2' gem "uglifier", '~> 2.7.2'
gem 'turbolinks', '~> 2.5.0' gem 'turbolinks', '~> 2.5.0'
gem 'jquery-turbolinks', '~> 2.0.1' gem 'jquery-turbolinks', '~> 2.0.1'
...@@ -224,6 +216,9 @@ group :development do ...@@ -224,6 +216,9 @@ group :development do
gem 'quiet_assets', '~> 1.0.2' gem 'quiet_assets', '~> 1.0.2'
gem 'rack-mini-profiler', '~> 0.9.0', require: false gem 'rack-mini-profiler', '~> 0.9.0', require: false
gem 'rerun', '~> 0.10.0' gem 'rerun', '~> 0.10.0'
gem 'bullet', require: false
gem 'active_record_query_trace', require: false
gem 'rack-lineprof', platform: :mri
# Better errors handler # Better errors handler
gem 'better_errors', '~> 1.0.1' gem 'better_errors', '~> 1.0.1'
...@@ -290,7 +285,7 @@ gem 'newrelic-grape' ...@@ -290,7 +285,7 @@ gem 'newrelic-grape'
gem 'octokit', '~> 3.7.0' gem 'octokit', '~> 3.7.0'
gem "mail_room", "~> 0.5.2" gem "mail_room", "~> 0.6.1"
gem 'email_reply_parser', '~> 0.5.8' gem 'email_reply_parser', '~> 0.5.8'
...@@ -304,11 +299,3 @@ gem 'oauth2', '~> 1.0.0' ...@@ -304,11 +299,3 @@ gem 'oauth2', '~> 1.0.0'
# Soft deletion # Soft deletion
gem "paranoia", "~> 2.0" gem "paranoia", "~> 2.0"
group :development, :test do
gem 'guard-rspec', '~> 4.2.0'
gem 'rb-fsevent', require: darwin_only('rb-fsevent')
gem 'growl', require: darwin_only('growl')
gem 'rb-inotify', require: linux_only('rb-inotify')
end
...@@ -17,6 +17,7 @@ GEM ...@@ -17,6 +17,7 @@ GEM
activesupport (= 4.1.12) activesupport (= 4.1.12)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubis (~> 2.7.0)
active_record_query_trace (1.5)
activemodel (4.1.12) activemodel (4.1.12)
activesupport (= 4.1.12) activesupport (= 4.1.12)
builder (~> 3.1) builder (~> 3.1)
...@@ -87,6 +88,9 @@ GEM ...@@ -87,6 +88,9 @@ GEM
terminal-table (~> 1.4) terminal-table (~> 1.4)
browser (1.0.0) browser (1.0.0)
builder (3.2.2) builder (3.2.2)
bullet (4.14.9)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.9.0)
byebug (6.0.2) byebug (6.0.2)
cal-heatmap-rails (0.0.1) cal-heatmap-rails (0.0.1)
capybara (2.4.4) capybara (2.4.4)
...@@ -134,6 +138,7 @@ GEM ...@@ -134,6 +138,7 @@ GEM
daemons (1.2.3) daemons (1.2.3)
database_cleaner (1.4.1) database_cleaner (1.4.1)
debug_inspector (0.0.2) debug_inspector (0.0.2)
debugger-ruby_core_source (1.3.8)
default_value_for (3.0.1) default_value_for (3.0.1)
activerecord (>= 3.2.0, < 5.0) activerecord (>= 3.2.0, < 5.0)
descendants_tracker (0.0.4) descendants_tracker (0.0.4)
...@@ -278,7 +283,7 @@ GEM ...@@ -278,7 +283,7 @@ GEM
mime-types (~> 1.19) mime-types (~> 1.19)
gitlab_emoji (0.1.1) gitlab_emoji (0.1.1)
gemojione (~> 2.0) gemojione (~> 2.0)
gitlab_git (7.2.18) gitlab_git (7.2.19)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0) gitlab-linguist (~> 3.0)
...@@ -314,19 +319,6 @@ GEM ...@@ -314,19 +319,6 @@ GEM
grape-entity (0.4.8) grape-entity (0.4.8)
activesupport activesupport
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
growl (1.0.3)
guard (2.13.0)
formatador (>= 0.2.4)
listen (>= 2.7, <= 4.0)
lumberjack (~> 1.0)
nenv (~> 0.1)
notiffany (~> 0.0)
pry (>= 0.9.12)
shellany (~> 0.0)
thor (>= 0.18.1)
guard-rspec (4.2.10)
guard (~> 2.1)
rspec (>= 2.14, < 4.0)
haml (4.0.7) haml (4.0.7)
tilt tilt
haml-rails (0.9.0) haml-rails (0.9.0)
...@@ -387,12 +379,11 @@ GEM ...@@ -387,12 +379,11 @@ GEM
celluloid (~> 0.16.0) celluloid (~> 0.16.0)
rb-fsevent (>= 0.9.3) rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9) rb-inotify (>= 0.9)
lumberjack (1.0.9)
macaddr (1.7.1) macaddr (1.7.1)
systemu (~> 2.6.2) systemu (~> 2.6.2)
mail (2.6.3) mail (2.6.3)
mime-types (>= 1.16, < 3) mime-types (>= 1.16, < 3)
mail_room (0.5.2) mail_room (0.6.1)
method_source (0.8.2) method_source (0.8.2)
mime-types (1.25.1) mime-types (1.25.1)
mimemagic (0.3.0) mimemagic (0.3.0)
...@@ -403,7 +394,6 @@ GEM ...@@ -403,7 +394,6 @@ GEM
multi_xml (0.5.5) multi_xml (0.5.5)
multipart-post (2.0.0) multipart-post (2.0.0)
mysql2 (0.3.20) mysql2 (0.3.20)
nenv (0.2.0)
nested_form (0.3.2) nested_form (0.3.2)
net-ldap (0.11) net-ldap (0.11)
net-scp (1.2.1) net-scp (1.2.1)
...@@ -416,9 +406,6 @@ GEM ...@@ -416,9 +406,6 @@ GEM
newrelic_rpm (3.9.4.245) newrelic_rpm (3.9.4.245)
nokogiri (1.6.6.2) nokogiri (1.6.6.2)
mini_portile (~> 0.6.0) mini_portile (~> 0.6.0)
notiffany (0.0.7)
nenv (~> 0.1)
shellany (~> 0.0)
nprogress-rails (0.1.2.3) nprogress-rails (0.1.2.3)
oauth (0.4.7) oauth (0.4.7)
oauth2 (1.0.0) oauth2 (1.0.0)
...@@ -502,6 +489,10 @@ GEM ...@@ -502,6 +489,10 @@ GEM
rack-attack (4.3.0) rack-attack (4.3.0)
rack rack
rack-cors (0.4.0) rack-cors (0.4.0)
rack-lineprof (0.0.3)
rack (~> 1.5)
rblineprof (~> 0.3.6)
term-ansicolor (~> 1.3)
rack-mini-profiler (0.9.7) rack-mini-profiler (0.9.7)
rack (>= 1.1.3) rack (>= 1.1.3)
rack-mount (0.8.3) rack-mount (0.8.3)
...@@ -540,13 +531,15 @@ GEM ...@@ -540,13 +531,15 @@ GEM
rb-fsevent (0.9.5) rb-fsevent (0.9.5)
rb-inotify (0.9.5) rb-inotify (0.9.5)
ffi (>= 0.5.0) ffi (>= 0.5.0)
rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3)
rbvmomi (1.8.2) rbvmomi (1.8.2)
builder builder
nokogiri (>= 1.4.1) nokogiri (>= 1.4.1)
trollop trollop
rdoc (3.12.2) rdoc (3.12.2)
json (~> 1.4) json (~> 1.4)
redcarpet (3.3.2) redcarpet (3.3.3)
redis (3.2.1) redis (3.2.1)
redis-actionpack (4.0.0) redis-actionpack (4.0.0)
actionpack (~> 4) actionpack (~> 4)
...@@ -647,7 +640,6 @@ GEM ...@@ -647,7 +640,6 @@ GEM
sexp_processor (4.6.0) sexp_processor (4.6.0)
sham_rack (1.3.6) sham_rack (1.3.6)
rack rack
shellany (0.0.1)
shoulda-matchers (2.8.0) shoulda-matchers (2.8.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
sidekiq (3.3.0) sidekiq (3.3.0)
...@@ -741,7 +733,7 @@ GEM ...@@ -741,7 +733,7 @@ GEM
simple_oauth (~> 0.1.4) simple_oauth (~> 0.1.4)
tzinfo (1.2.2) tzinfo (1.2.2)
thread_safe (~> 0.1) thread_safe (~> 0.1)
uglifier (2.3.3) uglifier (2.7.2)
execjs (>= 0.3.0) execjs (>= 0.3.0)
json (>= 1.8.0) json (>= 1.8.0)
underscore-rails (1.4.4) underscore-rails (1.4.4)
...@@ -755,6 +747,7 @@ GEM ...@@ -755,6 +747,7 @@ GEM
unicorn-worker-killer (0.4.3) unicorn-worker-killer (0.4.3)
get_process_mem (~> 0) get_process_mem (~> 0)
unicorn (~> 4) unicorn (~> 4)
uniform_notifier (1.9.0)
uuid (2.3.8) uuid (2.3.8)
macaddr (~> 1.0) macaddr (~> 1.0)
version_sorter (2.0.0) version_sorter (2.0.0)
...@@ -784,6 +777,7 @@ PLATFORMS ...@@ -784,6 +777,7 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
RedCloth (~> 4.2.9) RedCloth (~> 4.2.9)
ace-rails-ap (~> 2.0.1) ace-rails-ap (~> 2.0.1)
active_record_query_trace
activerecord-deprecated_finders (~> 1.0.3) activerecord-deprecated_finders (~> 1.0.3)
activerecord-session_store (~> 0.1.0) activerecord-session_store (~> 0.1.0)
acts-as-taggable-on (~> 3.4) acts-as-taggable-on (~> 3.4)
...@@ -800,6 +794,7 @@ DEPENDENCIES ...@@ -800,6 +794,7 @@ DEPENDENCIES
bootstrap-sass (~> 3.0) bootstrap-sass (~> 3.0)
brakeman (= 3.0.1) brakeman (= 3.0.1)
browser (~> 1.0.0) browser (~> 1.0.0)
bullet
byebug byebug
cal-heatmap-rails (~> 0.0.1) cal-heatmap-rails (~> 0.0.1)
capybara (~> 2.4.0) capybara (~> 2.4.0)
...@@ -834,15 +829,13 @@ DEPENDENCIES ...@@ -834,15 +829,13 @@ DEPENDENCIES
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-linguist (~> 3.0.1) gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1) gitlab_emoji (~> 0.1)
gitlab_git (~> 7.2.18) gitlab_git (~> 7.2.19)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1) gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.0.2) gollum-lib (~> 4.0.2)
gon (~> 5.0.0) gon (~> 5.0.0)
grape (~> 0.6.1) grape (~> 0.6.1)
grape-entity (~> 0.4.2) grape-entity (~> 0.4.2)
growl
guard-rspec (~> 4.2.0)
haml-rails (~> 0.9.0) haml-rails (~> 0.9.0)
hipchat (~> 1.5.0) hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0) html-pipeline (~> 1.11.0)
...@@ -854,7 +847,7 @@ DEPENDENCIES ...@@ -854,7 +847,7 @@ DEPENDENCIES
jquery-ui-rails (~> 4.2.1) jquery-ui-rails (~> 4.2.1)
kaminari (~> 0.16.3) kaminari (~> 0.16.3)
letter_opener (~> 1.1.2) letter_opener (~> 1.1.2)
mail_room (~> 0.5.2) mail_room (~> 0.6.1)
minitest (~> 5.7.0) minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6) mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.3.16) mysql2 (~> 0.3.16)
...@@ -882,14 +875,13 @@ DEPENDENCIES ...@@ -882,14 +875,13 @@ DEPENDENCIES
quiet_assets (~> 1.0.2) quiet_assets (~> 1.0.2)
rack-attack (~> 4.3.0) rack-attack (~> 4.3.0)
rack-cors (~> 0.4.0) rack-cors (~> 0.4.0)
rack-lineprof
rack-mini-profiler (~> 0.9.0) rack-mini-profiler (~> 0.9.0)
rack-oauth2 (~> 1.0.5) rack-oauth2 (~> 1.0.5)
rails (= 4.1.12) rails (= 4.1.12)
raphael-rails (~> 2.1.2) raphael-rails (~> 2.1.2)
rb-fsevent
rb-inotify
rdoc (~> 3.6) rdoc (~> 3.6)
redcarpet (~> 3.3.2) redcarpet (~> 3.3.3)
redis-rails (~> 4.0.0) redis-rails (~> 4.0.0)
request_store (~> 1.2.0) request_store (~> 1.2.0)
rerun (~> 0.10.0) rerun (~> 0.10.0)
...@@ -926,7 +918,7 @@ DEPENDENCIES ...@@ -926,7 +918,7 @@ DEPENDENCIES
thin (~> 1.6.1) thin (~> 1.6.1)
tinder (~> 1.10.0) tinder (~> 1.10.0)
turbolinks (~> 2.5.0) turbolinks (~> 2.5.0)
uglifier (~> 2.3.2) uglifier (~> 2.7.2)
underscore-rails (~> 1.4.4) underscore-rails (~> 1.4.4)
unf (~> 0.1.4) unf (~> 0.1.4)
unicorn (~> 4.8.2) unicorn (~> 4.8.2)
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
# #
# ### Example Markup # ### Example Markup
# #
# <div id="tree-content-holder"> # <div id="blob-content-holder">
# <div class="file-content"> # <div class="file-content">
# <div class="line-numbers"> # <div class="line-numbers">
# <a href="#L1" id="L1" data-line-number="1">1</a> # <a href="#L1" id="L1" data-line-number="1">1</a>
...@@ -53,7 +53,7 @@ class @LineHighlighter ...@@ -53,7 +53,7 @@ class @LineHighlighter
$.scrollTo("#L#{range[0]}", offset: -150) $.scrollTo("#L#{range[0]}", offset: -150)
bindEvents: -> bindEvents: ->
$('#tree-content-holder').on 'mousedown', 'a[data-line-number]', @clickHandler $('#blob-content-holder').on 'mousedown', 'a[data-line-number]', @clickHandler
# While it may seem odd to bind to the mousedown event and then throw away # While it may seem odd to bind to the mousedown event and then throw away
# the click event, there is a method to our madness. # the click event, there is a method to our madness.
...@@ -62,7 +62,7 @@ class @LineHighlighter ...@@ -62,7 +62,7 @@ class @LineHighlighter
# active state even when the event is cancelled, resulting in an ugly border # active state even when the event is cancelled, resulting in an ugly border
# around the link and/or a persisted underline text decoration. # around the link and/or a persisted underline text decoration.
$('#tree-content-holder').on 'click', 'a[data-line-number]', (event) -> $('#blob-content-holder').on 'click', 'a[data-line-number]', (event) ->
event.preventDefault() event.preventDefault()
clickHandler: (event) => clickHandler: (event) =>
......
...@@ -68,8 +68,8 @@ class @MergeRequestTabs ...@@ -68,8 +68,8 @@ class @MergeRequestTabs
scrollToElement: (container) -> scrollToElement: (container) ->
if window.location.hash if window.location.hash
top = $(container + " " + window.location.hash).offset().top $el = $("#{container} #{window.location.hash}")
$('body').scrollTo(top) $('body').scrollTo($el.offset().top) if $el.length
# Activate a tab based on the current action # Activate a tab based on the current action
activateTab: (action) -> activateTab: (action) ->
...@@ -127,7 +127,7 @@ class @MergeRequestTabs ...@@ -127,7 +127,7 @@ class @MergeRequestTabs
document.getElementById('commits').innerHTML = data.html document.getElementById('commits').innerHTML = data.html
$('.js-timeago').timeago() $('.js-timeago').timeago()
@commitsLoaded = true @commitsLoaded = true
@scrollToElement(".commits") @scrollToElement("#commits")
loadDiff: (source) -> loadDiff: (source) ->
return if @diffsLoaded return if @diffsLoaded
...@@ -137,7 +137,7 @@ class @MergeRequestTabs ...@@ -137,7 +137,7 @@ class @MergeRequestTabs
success: (data) => success: (data) =>
document.getElementById('diffs').innerHTML = data.html document.getElementById('diffs').innerHTML = data.html
@diffsLoaded = true @diffsLoaded = true
@scrollToElement(".diffs") @scrollToElement("#diffs")
# Show or hide the loading spinner # Show or hide the loading spinner
# #
......
...@@ -7,6 +7,7 @@ class @ShortcutsNavigation extends Shortcuts ...@@ -7,6 +7,7 @@ class @ShortcutsNavigation extends Shortcuts
Mousetrap.bind('g e', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-project-activity')) Mousetrap.bind('g e', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-project-activity'))
Mousetrap.bind('g f', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-tree')) Mousetrap.bind('g f', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-tree'))
Mousetrap.bind('g c', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-commits')) Mousetrap.bind('g c', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-commits'))
Mousetrap.bind('g b', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-builds'))
Mousetrap.bind('g n', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-network')) Mousetrap.bind('g n', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-network'))
Mousetrap.bind('g g', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-graphs')) Mousetrap.bind('g g', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-graphs'))
Mousetrap.bind('g i', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-issues')) Mousetrap.bind('g i', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-issues'))
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
line-height: 36px; line-height: 36px;
} }
.content-block,
.gray-content-block { .gray-content-block {
margin: -$gl-padding; margin: -$gl-padding;
background-color: $background-color; background-color: $background-color;
...@@ -27,6 +28,10 @@ ...@@ -27,6 +28,10 @@
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
color: $gl-gray; color: $gl-gray;
&.white {
background-color: white;
}
&.top-block { &.top-block {
border-top: none; border-top: none;
} }
......
...@@ -398,7 +398,3 @@ table { ...@@ -398,7 +398,3 @@ table {
.space-right { .space-right {
margin-right: 10px; margin-right: 10px;
} }
.in-line {
display: inline-block;
}
...@@ -22,4 +22,5 @@ ...@@ -22,4 +22,5 @@
.gfm-commit, .gfm-commit_range { .gfm-commit, .gfm-commit_range {
font-family: $monospace_font; font-family: $monospace_font;
font-size: 90%;
} }
...@@ -117,8 +117,12 @@ ul.content-list { ...@@ -117,8 +117,12 @@ ul.content-list {
} }
.controls { .controls {
padding-top: 5px; padding-top: 4px;
float: right; float: right;
.btn {
padding: 10px 14px;
}
} }
} }
} }
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
margin-right: 0; margin-right: 0;
} }
.issues-filters, .issues-details-filters,
.dash-projects-filters, .dash-projects-filters,
.check-all-holder { .check-all-holder {
display: none; display: none;
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
.center-top-menu { .center-top-menu {
height: 45px; height: 45px;
margin-bottom: 30px;
li a { li a {
font-size: 14px; font-size: 14px;
...@@ -90,9 +91,11 @@ ...@@ -90,9 +91,11 @@
} }
} }
.projects-search-form { .activity-filter-block {
margin: 0 -5px !important; display: none;
}
.projects-search-form {
.btn { .btn {
display: none; display: none;
} }
...@@ -100,6 +103,11 @@ ...@@ -100,6 +103,11 @@
} }
@media (max-width: $screen-sm-max) { @media (max-width: $screen-sm-max) {
.page-with-sidebar .content-wrapper {
padding: 0;
padding-top: 1px;
}
.issues-filters { .issues-filters {
.milestone-filter, .labels-filter { .milestone-filter, .labels-filter {
display: none; display: none;
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
} }
.select2-results .select2-result-label { .select2-results .select2-result-label {
padding: 16px; padding: 9px;
} }
.select2-drop{ .select2-drop{
...@@ -143,4 +143,4 @@ ...@@ -143,4 +143,4 @@
.ajax-users-dropdown { .ajax-users-dropdown {
min-width: 250px !important; min-width: 250px !important;
} }
\ No newline at end of file
...@@ -28,7 +28,7 @@ table { ...@@ -28,7 +28,7 @@ table {
border-bottom: 1px solid $border-color !important; border-bottom: 1px solid $border-color !important;
} }
td { td {
border-color: #F1F1F1 !important; border-color: $table-border-color !important;
border-bottom: 1px solid; border-bottom: 1px solid;
} }
} }
......
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
border-bottom: 1px solid #ECEEF1; border-bottom: 1px solid #ECEEF1;
border-right: 1px solid #ECEEF1; border-right: 1px solid #ECEEF1;
&:target {
background: $hover;
}
&:last-child { &:last-child {
border-bottom: none; border-bottom: none;
} }
......
...@@ -156,3 +156,5 @@ $nav-link-padding: 13px $gl-padding; ...@@ -156,3 +156,5 @@ $nav-link-padding: 13px $gl-padding;
$pre-bg: #f8fafc !default; $pre-bg: #f8fafc !default;
$pre-color: $gl-gray !default; $pre-color: $gl-gray !default;
$pre-border-color: #e7e9ed; $pre-border-color: #e7e9ed;
$table-bg-accent: $background-color;
@mixin md-typography { @mixin md-typography {
color: $md-text-color; color: $md-text-color;
word-wrap: break-word;
a { a {
color: $md-link-color; color: $md-link-color;
...@@ -73,6 +74,8 @@ ...@@ -73,6 +74,8 @@
} }
blockquote { blockquote {
color: #7f8fa4;
font-size: inherit;
padding: 8px 21px; padding: 8px 21px;
margin: 12px 0 12px; margin: 12px 0 12px;
border-left: 3px solid #e7e9ed; border-left: 3px solid #e7e9ed;
...@@ -80,7 +83,7 @@ ...@@ -80,7 +83,7 @@
blockquote p { blockquote p {
color: #7f8fa4 !important; color: #7f8fa4 !important;
font-size: 15px; font-size: inherit;
line-height: 1.5; line-height: 1.5;
} }
...@@ -101,9 +104,9 @@ ...@@ -101,9 +104,9 @@
pre { pre {
margin: 12px 0 12px 0 !important; margin: 12px 0 12px 0 !important;
background-color: #f8fafc !important; background-color: #f8fafc;
font-size: 13px !important; font-size: 13px !important;
color: #5b6169 !important; color: #5b6169;
line-height: 1.6em !important; line-height: 1.6em !important;
@include border-radius(2px); @include border-radius(2px);
} }
...@@ -112,9 +115,9 @@ ...@@ -112,9 +115,9 @@
font-weight: inherit; font-weight: inherit;
} }
ul, ol {
ul { padding: 0;
color: #5c5d5e; margin: 6px 0 6px 18px !important;
} }
li { li {
...@@ -136,6 +139,33 @@ ...@@ -136,6 +139,33 @@
text-decoration: none; text-decoration: none;
} }
} }
/* Link to current header. */
h1, h2, h3, h4, h5, h6 {
position: relative;
a.anchor {
// Setting `display: none` would prevent the anchor being scrolled to, so
// instead we set the height to 0 and it gets updated on hover.
height: 0;
}
&:hover > a.anchor {
$size: 16px;
position: absolute;
right: 100%;
top: 50%;
margin-top: -$size/2;
margin-right: 0px;
padding-right: 20px;
display: inline-block;
width: $size;
height: $size;
background-image: image-url("icon-link.png");
background-size: contain;
background-repeat: no-repeat;
}
}
} }
...@@ -202,49 +232,11 @@ a > code { ...@@ -202,49 +232,11 @@ a > code {
} }
/** /**
* Wiki typography * Apply Markdown typography
* *
*/ */
.wiki { .wiki {
@include md-typography; @include md-typography;
word-wrap: break-word;
padding: 7px;
/* Link to current header. */
h1, h2, h3, h4, h5, h6 {
position: relative;
a.anchor {
// Setting `display: none` would prevent the anchor being scrolled to, so
// instead we set the height to 0 and it gets updated on hover.
height: 0;
}
&:hover > a.anchor {
$size: 16px;
position: absolute;
right: 100%;
top: 50%;
margin-top: -$size/2;
margin-right: 0px;
padding-right: 20px;
display: inline-block;
width: $size;
height: $size;
background-image: image-url("icon-link.png");
background-size: contain;
background-repeat: no-repeat;
}
}
ul,ol {
padding: 0;
margin: 6px 0 6px 18px !important;
}
ol {
color: #5c5d5e;
}
} }
.md-area { .md-area {
......
...@@ -16,6 +16,7 @@ $avatar_radius: 50%; ...@@ -16,6 +16,7 @@ $avatar_radius: 50%;
$code_font_size: 13px; $code_font_size: 13px;
$code_line_height: 1.5; $code_line_height: 1.5;
$border-color: #dce0e6; $border-color: #dce0e6;
$table-border-color: #eef0f2;
$background-color: #F7F8FA; $background-color: #F7F8FA;
$header-height: 58px; $header-height: 58px;
$fixed-layout-width: 1200px; $fixed-layout-width: 1200px;
......
/* https://github.com/MozMorris/tomorrow-pygments */ /* https://github.com/MozMorris/tomorrow-pygments */
pre.code.highlight.dark,
.code.dark { .code.dark {
background-color: #1d1f21; background-color: #1d1f21 !important;
color: #c5c8c6; color: #c5c8c6 !important;
pre.code, pre.highlight,
.line-numbers, .line-numbers,
.line-numbers a { .line-numbers a {
background-color: #1d1f21 !important; background-color: #1d1f21 !important;
...@@ -23,8 +22,8 @@ pre.code.highlight.dark, ...@@ -23,8 +22,8 @@ pre.code.highlight.dark,
// Search result highlight // Search result highlight
span.highlight_word { span.highlight_word {
background: #ffe792; background-color: #ffe792 !important;
color: #000000; color: #000000 !important;
} }
.hll { background-color: #373b41 } .hll { background-color: #373b41 }
......
/* https://github.com/richleland/pygments-css/blob/master/monokai.css */ /* https://github.com/richleland/pygments-css/blob/master/monokai.css */
pre.code.monokai,
.code.monokai { .code.monokai {
background: #272822; background-color: #272822 !important;
color: #f8f8f2; color: #f8f8f2 !important;
pre.highlight, pre.highlight,
.line-numbers, .line-numbers,
.line-numbers a { .line-numbers a {
background:#272822 !important; background-color :#272822 !important;
color:#f8f8f2 !important; color: #f8f8f2 !important;
} }
pre.code { pre.code {
...@@ -23,8 +22,8 @@ pre.code.monokai, ...@@ -23,8 +22,8 @@ pre.code.monokai,
// Search result highlight // Search result highlight
span.highlight_word { span.highlight_word {
background: #ffe792; background-color: #ffe792 !important;
color: #000000; color: #000000 !important;
} }
.hll { background-color: #49483e } .hll { background-color: #49483e }
......
/* https://gist.github.com/qguv/7936275 */ /* https://gist.github.com/qguv/7936275 */
pre.code.highlight.solarized-dark,
.code.solarized-dark { .code.solarized-dark {
background-color: #002b36; background-color: #002b36 !important;
color: #93a1a1; color: #93a1a1 !important;
pre.code, pre.highlight,
.line-numbers, .line-numbers,
.line-numbers a { .line-numbers a {
background-color: #002b36 !important; background-color: #002b36 !important;
...@@ -23,7 +22,7 @@ pre.code.highlight.solarized-dark, ...@@ -23,7 +22,7 @@ pre.code.highlight.solarized-dark,
// Search result highlight // Search result highlight
span.highlight_word { span.highlight_word {
background: #094554; background-color: #094554 !important;
} }
/* Solarized Dark /* Solarized Dark
......
/* https://gist.github.com/qguv/7936275 */ /* https://gist.github.com/qguv/7936275 */
pre.code.highlight.solarized-light,
.code.solarized-light { .code.solarized-light {
background-color: #fdf6e3; background-color: #fdf6e3 !important;
color: #586e75; color: #586e75 !important;
pre.code, pre.highlight,
.line-numbers, .line-numbers,
.line-numbers a { .line-numbers a {
background-color: #fdf6e3 !important; background-color: #fdf6e3 !important;
...@@ -23,7 +22,7 @@ pre.code.highlight.solarized-light, ...@@ -23,7 +22,7 @@ pre.code.highlight.solarized-light,
// Search result highlight // Search result highlight
span.highlight_word { span.highlight_word {
background: #eee8d5; background-color: #eee8d5 !important;
} }
/* Solarized Light /* Solarized Light
......
/* https://github.com/aahan/pygments-github-style */ /* https://github.com/aahan/pygments-github-style */
pre.code.highlight.white,
.code.white { .code.white {
background-color: #f8fafc;
font-size: 13px;
color: #5b6169;
line-height: 1.6em;
background-color: #f8fafc !important;
color: #5b6169 !important;
pre.highlight,
.line-numbers, .line-numbers,
.line-numbers a { .line-numbers a {
background-color: $background-color !important; background-color: $background-color !important;
color: $gl-gray !important; color: $gl-gray !important;
} }
pre.highlight {
background-color: #fff !important;
color: #333 !important;
}
pre.code { pre.code {
border-left: 1px solid $border-color; border-left: 1px solid $border-color;
background-color: #fff !important;
color: #333 !important;
} }
// highlight line via anchor // highlight line via anchor
...@@ -28,7 +24,7 @@ pre.code.highlight.white, ...@@ -28,7 +24,7 @@ pre.code.highlight.white,
// Search result highlight // Search result highlight
span.highlight_word { span.highlight_word {
background: #fafe3d; background-color: #fafe3d !important;
} }
.hll { background-color: #f8f8f8 } .hll { background-color: #f8f8f8 }
......
...@@ -68,3 +68,7 @@ body.modal-open { ...@@ -68,3 +68,7 @@ body.modal-open {
.modal .modal-dialog { .modal .modal-dialog {
width: 860px; width: 860px;
} }
.documentation {
padding: 7px;
}
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
} }
p { p {
padding: 0 $gl-padding;
color: #5c5d5e; color: #5c5d5e;
} }
} }
...@@ -511,7 +512,37 @@ pre.light-well { ...@@ -511,7 +512,37 @@ pre.light-well {
} }
} }
.inline-form { .project-last-commit {
display: inline-block; margin: 0 7px;
.ci-status {
margin-right: 16px;
}
.commit-row-message {
color: $gl-gray;
}
.commit_short_id {
margin-right: 5px;
color: $gl-link-color;
font-weight: 600;
}
.commit-author-link {
margin-left: 7px;
text-decoration: none;
.avatar {
float: none;
margin-right: 4px;
}
.commit-author-name {
font-weight: 600;
}
}
} }
.project-show-readme .readme-holder {
padding: 7px;
}
.tree-holder { .tree-holder {
.tree-content-holder { .tree-table-holder {
float: left; margin-left: -$gl-padding;
width: 100%; margin-right: -$gl-padding;
} }
.tree_progress { .tree_progress {
...@@ -13,10 +13,15 @@ ...@@ -13,10 +13,15 @@
} }
.tree-table { .tree-table {
@extend .table; margin-bottom: 0;
@include border-radius(0);
tr { tr {
> td, > th {
padding: 10px $gl-padding;
line-height: 32px;
border-color: $table-border-color !important;
}
&:hover { &:hover {
td { td {
background: $hover; background: $hover;
...@@ -27,9 +32,9 @@ ...@@ -27,9 +32,9 @@
} }
&.selected { &.selected {
td { td {
background: $background-color; background: $gray-dark;
border-top: 1px solid #EEE; border-top: 1px solid $border-gray-dark;
border-bottom: 1px solid #EEE; border-bottom: 1px solid $border-gray-dark;
} }
} }
} }
...@@ -85,19 +90,6 @@ ...@@ -85,19 +90,6 @@
margin-right: 15px; margin-right: 15px;
} }
.readme-holder {
margin: 0 auto;
.readme-file-title {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #777;
border-bottom: 1px solid #DDD;
padding: 10px 0;
}
}
.blob-commit-info { .blob-commit-info {
list-style: none; list-style: none;
margin: 0; margin: 0;
......
...@@ -39,7 +39,13 @@ class Admin::ServicesController < Admin::ApplicationController ...@@ -39,7 +39,13 @@ class Admin::ServicesController < Admin::ApplicationController
end end
def application_services_params def application_services_params
params.permit(:id, application_services_params = params.permit(:id,
service: Projects::ServicesController::ALLOWED_PARAMS) service: Projects::ServicesController::ALLOWED_PARAMS)
if application_services_params[:service].is_a?(Hash)
Projects::ServicesController::FILTER_BLANK_PARAMS.each do |param|
application_services_params[:service].delete(param) if application_services_params[:service][param].blank?
end
end
application_services_params
end end
end end
...@@ -30,7 +30,7 @@ class ApplicationController < ActionController::Base ...@@ -30,7 +30,7 @@ class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound do |exception| rescue_from ActiveRecord::RecordNotFound do |exception|
log_exception(exception) log_exception(exception)
render "errors/not_found", layout: "errors", status: 404 render_404
end end
protected protected
...@@ -149,12 +149,8 @@ class ApplicationController < ActionController::Base ...@@ -149,12 +149,8 @@ class ApplicationController < ActionController::Base
render "errors/access_denied", layout: "errors", status: 404 render "errors/access_denied", layout: "errors", status: 404
end end
def not_found!
render "errors/not_found", layout: "errors", status: 404
end
def git_not_found! def git_not_found!
render "errors/git_not_found", layout: "errors", status: 404 render html: "errors/git_not_found", layout: "errors", status: 404
end end
def method_missing(method_sym, *arguments, &block) def method_missing(method_sym, *arguments, &block)
......
...@@ -6,7 +6,7 @@ module Ci ...@@ -6,7 +6,7 @@ module Ci
@runners = Ci::Runner.order('id DESC') @runners = Ci::Runner.order('id DESC')
@runners = @runners.search(params[:search]) if params[:search].present? @runners = @runners.search(params[:search]) if params[:search].present?
@runners = @runners.page(params[:page]).per(30) @runners = @runners.page(params[:page]).per(30)
@active_runners_cnt = Ci::Runner.where("contacted_at > ?", 1.minutes.ago).count @active_runners_cnt = Ci::Runner.online.count
end end
def show def show
...@@ -66,7 +66,7 @@ module Ci ...@@ -66,7 +66,7 @@ module Ci
end end
def runner_params def runner_params
params.require(:runner).permit(:token, :description, :tag_list, :contacted_at, :active) params.require(:runner).permit(:token, :description, :tag_list, :active)
end end
end end
end end
...@@ -62,7 +62,7 @@ class Import::BitbucketController < Import::BaseController ...@@ -62,7 +62,7 @@ class Import::BitbucketController < Import::BaseController
end end
def verify_bitbucket_import_enabled def verify_bitbucket_import_enabled
not_found! unless bitbucket_import_enabled? render_404 unless bitbucket_import_enabled?
end end
def bitbucket_auth def bitbucket_auth
......
...@@ -99,6 +99,6 @@ class Import::FogbugzController < Import::BaseController ...@@ -99,6 +99,6 @@ class Import::FogbugzController < Import::BaseController
end end
def verify_fogbugz_import_enabled def verify_fogbugz_import_enabled
not_found! unless fogbugz_import_enabled? render_404 unless fogbugz_import_enabled?
end end
end end
...@@ -47,7 +47,7 @@ class Import::GithubController < Import::BaseController ...@@ -47,7 +47,7 @@ class Import::GithubController < Import::BaseController
end end
def verify_github_import_enabled def verify_github_import_enabled
not_found! unless github_import_enabled? render_404 unless github_import_enabled?
end end
def github_auth def github_auth
......
...@@ -44,7 +44,7 @@ class Import::GitlabController < Import::BaseController ...@@ -44,7 +44,7 @@ class Import::GitlabController < Import::BaseController
end end
def verify_gitlab_import_enabled def verify_gitlab_import_enabled
not_found! unless gitlab_import_enabled? render_404 unless gitlab_import_enabled?
end end
def gitlab_auth def gitlab_auth
......
...@@ -42,7 +42,7 @@ class Import::GitoriousController < Import::BaseController ...@@ -42,7 +42,7 @@ class Import::GitoriousController < Import::BaseController
end end
def verify_gitorious_import_enabled def verify_gitorious_import_enabled
not_found! unless gitorious_import_enabled? render_404 unless gitorious_import_enabled?
end end
end end
...@@ -106,7 +106,7 @@ class Import::GoogleCodeController < Import::BaseController ...@@ -106,7 +106,7 @@ class Import::GoogleCodeController < Import::BaseController
end end
def verify_google_code_import_enabled def verify_google_code_import_enabled
not_found! unless google_code_import_enabled? render_404 unless google_code_import_enabled?
end end
def user_map def user_map
......
...@@ -12,7 +12,7 @@ class Projects::AvatarsController < Projects::ApplicationController ...@@ -12,7 +12,7 @@ class Projects::AvatarsController < Projects::ApplicationController
filename: @blob.name filename: @blob.name
) )
else else
not_found! render_404
end end
end end
......
...@@ -113,14 +113,14 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -113,14 +113,14 @@ class Projects::BlobController < Projects::ApplicationController
end end
end end
return not_found! return render_404
end end
end end
def commit def commit
@commit = @repository.commit(@ref) @commit = @repository.commit(@ref)
return not_found! unless @commit return render_404 unless @commit
end end
def assign_blob_vars def assign_blob_vars
...@@ -128,7 +128,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -128,7 +128,7 @@ class Projects::BlobController < Projects::ApplicationController
@ref, @path = extract_ref(@id) @ref, @path = extract_ref(@id)
rescue InvalidPathError rescue InvalidPathError
not_found! render_404
end end
def after_edit_path def after_edit_path
......
class Projects::BuildsController < Projects::ApplicationController class Projects::BuildsController < Projects::ApplicationController
before_action :ci_project before_action :ci_project
before_action :build before_action :build, except: [:index, :cancel_all]
before_action :authorize_admin_project!, except: [:show, :status] before_action :authorize_admin_project!, except: [:index, :show, :status]
layout "project" layout "project"
def index
@scope = params[:scope]
@all_builds = project.ci_builds
@builds =
case @scope
when 'all'
@all_builds
when 'finished'
@all_builds.finished
else
@all_builds.running_or_pending
end
@builds = @builds.order('created_at DESC').page(params[:page]).per(30)
end
def cancel_all
@project.ci_builds.running_or_pending.each(&:cancel)
redirect_to namespace_project_builds_path(project.namespace, project)
end
def show def show
@builds = @ci_project.commits.find_by_sha(@build.sha).builds.order('id DESC') @builds = @ci_project.commits.find_by_sha(@build.sha).builds.order('id DESC')
@builds = @builds.where("id not in (?)", @build.id).page(params[:page]).per(20) @builds = @builds.where("id not in (?)", @build.id).page(params[:page]).per(20)
......
...@@ -55,9 +55,9 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -55,9 +55,9 @@ class Projects::IssuesController < Projects::ApplicationController
end end
def show def show
@participants = @issue.participants(current_user, @project) @participants = @issue.participants(current_user)
@note = @project.notes.new(noteable: @issue) @note = @project.notes.new(noteable: @issue)
@notes = @issue.notes.inc_author.fresh @notes = @issue.notes.with_associations.fresh
@noteable = @issue @noteable = @issue
respond_with(@issue) respond_with(@issue)
......
...@@ -246,7 +246,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -246,7 +246,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def define_show_vars def define_show_vars
@participants = @merge_request.participants(current_user, @project) @participants = @merge_request.participants(current_user)
# Build a note object for comment form # Build a note object for comment form
@note = @project.notes.new(noteable: @merge_request) @note = @project.notes.new(noteable: @merge_request)
......
...@@ -20,7 +20,7 @@ class Projects::RawController < Projects::ApplicationController ...@@ -20,7 +20,7 @@ class Projects::RawController < Projects::ApplicationController
disposition: 'inline' disposition: 'inline'
) )
else else
not_found! render_404
end end
end end
......
...@@ -3,6 +3,7 @@ class Projects::RefsController < Projects::ApplicationController ...@@ -3,6 +3,7 @@ class Projects::RefsController < Projects::ApplicationController
include TreeHelper include TreeHelper
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :validate_ref_id
before_action :assign_ref_vars before_action :assign_ref_vars
before_action :authorize_download_code! before_action :authorize_download_code!
...@@ -71,4 +72,10 @@ class Projects::RefsController < Projects::ApplicationController ...@@ -71,4 +72,10 @@ class Projects::RefsController < Projects::ApplicationController
format.js format.js
end end
end end
private
def validate_ref_id
return not_found! if params[:id].present? && params[:id] !~ Gitlab::Regex.git_reference_regex
end
end end
...@@ -11,18 +11,9 @@ class Projects::RepositoriesController < Projects::ApplicationController ...@@ -11,18 +11,9 @@ class Projects::RepositoriesController < Projects::ApplicationController
end end
def archive def archive
begin render json: ArchiveRepositoryService.new(@project, params[:ref], params[:format]).execute
file_path = ArchiveRepositoryService.new(@project, params[:ref], params[:format]).execute rescue => ex
rescue logger.error("#{self.class.name}: #{ex}")
return head :not_found return git_not_found!
end
if file_path
# Send file to user
response.headers["Content-Length"] = File.open(file_path).size.to_s
send_file file_path
else
redirect_to request.fullpath
end
end end
end end
...@@ -60,6 +60,6 @@ class Projects::RunnersController < Projects::ApplicationController ...@@ -60,6 +60,6 @@ class Projects::RunnersController < Projects::ApplicationController
end end
def runner_params def runner_params
params.require(:runner).permit(:description, :tag_list, :contacted_at, :active) params.require(:runner).permit(:description, :tag_list, :active)
end end
end end
...@@ -9,6 +9,10 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -9,6 +9,10 @@ class Projects::ServicesController < Projects::ApplicationController
:note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url, :note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url,
:notify, :color, :notify, :color,
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification] :server_host, :server_port, :default_irc_uri, :enable_ssl_verification]
# Parameters to ignore if no value is specified
FILTER_BLANK_PARAMS = [:password]
# Authorize # Authorize
before_action :authorize_admin_project! before_action :authorize_admin_project!
before_action :service, only: [:edit, :update, :test] before_action :service, only: [:edit, :update, :test]
...@@ -59,7 +63,9 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -59,7 +63,9 @@ class Projects::ServicesController < Projects::ApplicationController
def service_params def service_params
service_params = params.require(:service).permit(ALLOWED_PARAMS) service_params = params.require(:service).permit(ALLOWED_PARAMS)
service_params.delete("password") if service_params["password"].blank? FILTER_BLANK_PARAMS.each do |param|
service_params.delete(param) if service_params[param].blank?
end
service_params service_params
end end
end end
...@@ -10,7 +10,7 @@ class Projects::TreeController < Projects::ApplicationController ...@@ -10,7 +10,7 @@ class Projects::TreeController < Projects::ApplicationController
before_action :authorize_push_code!, only: [:create_dir] before_action :authorize_push_code!, only: [:create_dir]
def show def show
return not_found! unless @repository.commit(@ref) return render_404 unless @repository.commit(@ref)
if tree.entries.empty? if tree.entries.empty?
if @repository.blob_at(@commit.id, @path) if @repository.blob_at(@commit.id, @path)
...@@ -19,7 +19,7 @@ class Projects::TreeController < Projects::ApplicationController ...@@ -19,7 +19,7 @@ class Projects::TreeController < Projects::ApplicationController
File.join(@ref, @path)) File.join(@ref, @path))
) and return ) and return
elsif @path.present? elsif @path.present?
return not_found! return render_404
end end
end end
...@@ -31,7 +31,7 @@ class Projects::TreeController < Projects::ApplicationController ...@@ -31,7 +31,7 @@ class Projects::TreeController < Projects::ApplicationController
end end
def create_dir def create_dir
return not_found! unless @commit_params.values.all? return render_404 unless @commit_params.values.all?
begin begin
result = Files::CreateDirService.new(@project, current_user, @commit_params).execute result = Files::CreateDirService.new(@project, current_user, @commit_params).execute
......
...@@ -20,7 +20,7 @@ class Projects::UploadsController < Projects::ApplicationController ...@@ -20,7 +20,7 @@ class Projects::UploadsController < Projects::ApplicationController
end end
def show def show
return not_found! if uploader.nil? || !uploader.file.exists? return render_404 if uploader.nil? || !uploader.file.exists?
disposition = uploader.image? ? 'inline' : 'attachment' disposition = uploader.image? ? 'inline' : 'attachment'
send_file uploader.file.path, disposition: disposition send_file uploader.file.path, disposition: disposition
......
...@@ -10,7 +10,7 @@ class UploadsController < ApplicationController ...@@ -10,7 +10,7 @@ class UploadsController < ApplicationController
end end
unless uploader.file && uploader.file.exists? unless uploader.file && uploader.file.exists?
return not_found! return render_404
end end
disposition = uploader.image? ? 'inline' : 'attachment' disposition = uploader.image? ? 'inline' : 'attachment'
...@@ -21,7 +21,7 @@ class UploadsController < ApplicationController ...@@ -21,7 +21,7 @@ class UploadsController < ApplicationController
def find_model def find_model
unless upload_model && upload_mount unless upload_model && upload_mount
return not_found! return render_404
end end
@model = upload_model.find(params[:id]) @model = upload_model.find(params[:id])
...@@ -44,7 +44,7 @@ class UploadsController < ApplicationController ...@@ -44,7 +44,7 @@ class UploadsController < ApplicationController
return if authorized return if authorized
if current_user if current_user
not_found! render_404
else else
authenticate_user! authenticate_user!
end end
......
...@@ -68,13 +68,17 @@ module ApplicationHelper ...@@ -68,13 +68,17 @@ module ApplicationHelper
end end
end end
def avatar_icon(user_email = '', size = nil) def avatar_icon(user_or_email = nil, size = nil)
user = User.find_by(email: user_email) if user_or_email.is_a?(User)
user = user_or_email
else
user = User.find_by(email: user_or_email)
end
if user if user
user.avatar_url(size) || default_avatar user.avatar_url(size) || default_avatar
else else
gravatar_icon(user_email, size) gravatar_icon(user_or_email, size)
end end
end end
......
...@@ -25,6 +25,10 @@ module GitlabRoutingHelper ...@@ -25,6 +25,10 @@ module GitlabRoutingHelper
namespace_project_commits_path(project.namespace, project, @ref || project.repository.root_ref) namespace_project_commits_path(project.namespace, project, @ref || project.repository.root_ref)
end end
def project_builds_path(project, *args)
namespace_project_builds_path(project.namespace, project, *args)
end
def activity_project_path(project, *args) def activity_project_path(project, *args)
activity_namespace_project_path(project.namespace, project, *args) activity_namespace_project_path(project.namespace, project, *args)
end end
......
...@@ -92,11 +92,19 @@ module LabelsHelper ...@@ -92,11 +92,19 @@ module LabelsHelper
end end
end end
def project_labels_options(project) def projects_labels_options
labels = project.labels.to_a labels =
labels.unshift(Label::None) if @project
labels.unshift(Label::Any) @project.labels
options_from_collection_for_select(labels, 'name', 'title', params[:label_name]) else
Label.where(project_id: @projects)
end
grouped_labels = Labels::GroupService.new(labels).execute
grouped_labels.unshift(Label::None)
grouped_labels.unshift(Label::Any)
options_from_collection_for_select(grouped_labels, 'name', 'title', params[:label_name])
end end
# Required for Gitlab::Markdown::LabelReferenceFilter # Required for Gitlab::Markdown::LabelReferenceFilter
......
...@@ -47,7 +47,7 @@ module MergeRequestsHelper ...@@ -47,7 +47,7 @@ module MergeRequestsHelper
end end
def issues_sentence(issues) def issues_sentence(issues)
issues.map { |i| "##{i.iid}" }.to_sentence issues.map(&:to_reference).to_sentence
end end
def mr_change_branches_path(merge_request) def mr_change_branches_path(merge_request)
......
...@@ -29,7 +29,7 @@ module ProjectsHelper ...@@ -29,7 +29,7 @@ module ProjectsHelper
author_html = "" author_html = ""
# Build avatar image tag # Build avatar image tag
author_html << image_tag(avatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] author_html << image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
# Build name span tag # Build name span tag
author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name] author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name]
...@@ -117,6 +117,10 @@ module ProjectsHelper ...@@ -117,6 +117,10 @@ module ProjectsHelper
nav_tabs << :merge_requests nav_tabs << :merge_requests
end end
if can?(current_user, :read_build, project)
nav_tabs << :builds
end
if can?(current_user, :admin_project, project) if can?(current_user, :admin_project, project)
nav_tabs << :settings nav_tabs << :settings
end end
......
module RunnersHelper module RunnersHelper
def runner_status_icon(runner) def runner_status_icon(runner)
unless runner.contacted_at status = runner.status
return content_tag :i, nil, case status
class: "fa fa-warning-sign", when :not_connected
title: "New runner. Has not connected yet" content_tag :i, nil,
class: "fa fa-warning",
title: "New runner. Has not connected yet"
when :online, :offline, :paused
content_tag :i, nil,
class: "fa fa-circle runner-status-#{status}",
title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago"
end end
end
status = def runner_link(runner)
if runner.active? display_name = truncate(runner.display_name, length: 15)
runner.contacted_at > 3.hour.ago ? :online : :offline id = "\##{runner.id}"
else
:paused
end
content_tag :i, nil, if current_user && current_user.admin
class: "fa fa-circle runner-status-#{status}", link_to ci_admin_runner_path(runner) do
title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago" display_name + id
end
else
display_name + id
end
end end
end end
...@@ -41,6 +41,7 @@ class Ability ...@@ -41,6 +41,7 @@ class Ability
:read_project_member, :read_project_member,
:read_merge_request, :read_merge_request,
:read_note, :read_note,
:read_build,
:download_code :download_code
] ]
...@@ -127,6 +128,7 @@ class Ability ...@@ -127,6 +128,7 @@ class Ability
:read_project_member, :read_project_member,
:read_merge_request, :read_merge_request,
:read_note, :read_note,
:read_build,
:create_project, :create_project,
:create_issue, :create_issue,
:create_note :create_note
...@@ -135,6 +137,8 @@ class Ability ...@@ -135,6 +137,8 @@ class Ability
def project_report_rules def project_report_rules
project_guest_rules + [ project_guest_rules + [
:create_commit_status,
:read_commit_statuses,
:download_code, :download_code,
:fork_project, :fork_project,
:create_project_snippet, :create_project_snippet,
......
...@@ -24,32 +24,19 @@ ...@@ -24,32 +24,19 @@
# #
module Ci module Ci
class Build < ActiveRecord::Base class Build < CommitStatus
extend Ci::Model
LAZY_ATTRIBUTES = ['trace'] LAZY_ATTRIBUTES = ['trace']
belongs_to :commit, class_name: 'Ci::Commit'
belongs_to :runner, class_name: 'Ci::Runner' belongs_to :runner, class_name: 'Ci::Runner'
belongs_to :trigger_request, class_name: 'Ci::TriggerRequest' belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
belongs_to :user
serialize :options serialize :options
validates :commit, presence: true
validates :status, presence: true
validates :coverage, numericality: true, allow_blank: true validates :coverage, numericality: true, allow_blank: true
validates_presence_of :ref validates_presence_of :ref
scope :running, ->() { where(status: "running") }
scope :pending, ->() { where(status: "pending") }
scope :success, ->() { where(status: "success") }
scope :failed, ->() { where(status: "failed") }
scope :unstarted, ->() { where(runner_id: nil) } scope :unstarted, ->() { where(runner_id: nil) }
scope :running_or_pending, ->() { where(status:[:running, :pending]) }
scope :latest, ->() { where(id: unscope(:select).select('max(id)').group(:name, :ref)).order(stage_idx: :asc) }
scope :ignore_failures, ->() { where(allow_failure: false) } scope :ignore_failures, ->() { where(allow_failure: false) }
scope :for_ref, ->(ref) { where(ref: ref) }
scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) } scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) }
acts_as_taggable acts_as_taggable
...@@ -74,13 +61,14 @@ module Ci ...@@ -74,13 +61,14 @@ module Ci
def create_from(build) def create_from(build)
new_build = build.dup new_build = build.dup
new_build.status = :pending new_build.status = 'pending'
new_build.runner_id = nil new_build.runner_id = nil
new_build.trigger_request_id = nil
new_build.save new_build.save
end end
def retry(build) def retry(build)
new_build = Ci::Build.new(status: :pending) new_build = Ci::Build.new(status: 'pending')
new_build.ref = build.ref new_build.ref = build.ref
new_build.tag = build.tag new_build.tag = build.tag
new_build.options = build.options new_build.options = build.options
...@@ -98,57 +86,24 @@ module Ci ...@@ -98,57 +86,24 @@ module Ci
end end
state_machine :status, initial: :pending do state_machine :status, initial: :pending do
event :run do
transition pending: :running
end
event :drop do
transition running: :failed
end
event :success do
transition running: :success
end
event :cancel do
transition [:pending, :running] => :canceled
end
after_transition pending: :running do |build, transition|
build.update_attributes started_at: Time.now
end
after_transition any => [:success, :failed, :canceled] do |build, transition| after_transition any => [:success, :failed, :canceled] do |build, transition|
build.update_attributes finished_at: Time.now
project = build.project project = build.project
if project.web_hooks? if project.web_hooks?
Ci::WebHookService.new.build_end(build) Ci::WebHookService.new.build_end(build)
end end
if build.commit.should_create_next_builds?(build) build.commit.create_next_builds(build)
build.commit.create_next_builds(build.ref, build.tag, build.user, build.trigger_request)
end
project.execute_services(build) project.execute_services(build)
if project.coverage_enabled? if project.coverage_enabled?
build.update_coverage build.update_coverage
end end
end end
state :pending, value: 'pending'
state :running, value: 'running'
state :failed, value: 'failed'
state :success, value: 'success'
state :canceled, value: 'canceled'
end end
delegate :sha, :short_sha, :project, :gl_project, def ignored?
to: :commit, prefix: false failed? && allow_failure?
def before_sha
Gitlab::Git::BLANK_SHA
end end
def trace_html def trace_html
...@@ -156,36 +111,12 @@ module Ci ...@@ -156,36 +111,12 @@ module Ci
html || '' html || ''
end end
def started?
!pending? && !canceled? && started_at
end
def active?
running? || pending?
end
def complete?
canceled? || success? || failed?
end
def ignored?
failed? && allow_failure?
end
def timeout def timeout
project.timeout project.timeout
end end
def variables def variables
yaml_variables + project_variables + trigger_variables predefined_variables + yaml_variables + project_variables + trigger_variables
end
def duration
if started_at && finished_at
finished_at - started_at
elsif started_at
Time.now - started_at
end
end end
def project def project
...@@ -278,6 +209,37 @@ module Ci ...@@ -278,6 +209,37 @@ module Ci
"#{dir_to_trace}/#{id}.log" "#{dir_to_trace}/#{id}.log"
end end
def target_url
Gitlab::Application.routes.url_helpers.
namespace_project_build_url(gl_project.namespace, gl_project, self)
end
def cancel_url
if active?
Gitlab::Application.routes.url_helpers.
cancel_namespace_project_build_path(gl_project.namespace, gl_project, self)
end
end
def retry_url
if commands.present?
Gitlab::Application.routes.url_helpers.
retry_namespace_project_build_path(gl_project.namespace, gl_project, self)
end
end
def can_be_served?(runner)
(tag_list - runner.tag_list).empty?
end
def any_runners_online?
project.any_runners? { |runner| runner.active? && runner.online? && can_be_served?(runner) }
end
def show_warning?
pending? && !any_runners_online?
end
private private
def yaml_variables def yaml_variables
...@@ -305,5 +267,14 @@ module Ci ...@@ -305,5 +267,14 @@ module Ci
[] []
end end
end end
def predefined_variables
variables = []
variables << { key: :CI_BUILD_TAG, value: ref, public: true } if tag?
variables << { key: :CI_BUILD_NAME, value: name, public: true }
variables << { key: :CI_BUILD_STAGE, value: stage, public: true }
variables << { key: :CI_BUILD_TRIGGERED, value: 'true', public: true } if trigger_request
variables
end
end end
end end
...@@ -20,9 +20,12 @@ module Ci ...@@ -20,9 +20,12 @@ module Ci
extend Ci::Model extend Ci::Model
belongs_to :gl_project, class_name: '::Project', foreign_key: :gl_project_id belongs_to :gl_project, class_name: '::Project', foreign_key: :gl_project_id
has_many :builds, dependent: :destroy, class_name: 'Ci::Build' has_many :statuses, dependent: :destroy, class_name: 'CommitStatus'
has_many :builds, class_name: 'Ci::Build'
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
scope :ordered, -> { order('CASE WHEN ci_commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) }
validates_presence_of :sha validates_presence_of :sha
validate :valid_commit_sha validate :valid_commit_sha
...@@ -47,7 +50,7 @@ module Ci ...@@ -47,7 +50,7 @@ module Ci
end end
def retry def retry
builds_without_retry.each do |build| latest_builds.each do |build|
Ci::Build.retry(build) Ci::Build.retry(build)
end end
end end
...@@ -81,87 +84,76 @@ module Ci ...@@ -81,87 +84,76 @@ module Ci
end end
def stage def stage
running_or_pending = builds_without_retry.running_or_pending running_or_pending = statuses.latest.running_or_pending.ordered
running_or_pending.limit(1).pluck(:stage).first running_or_pending.first.try(:stage)
end end
def create_builds(ref, tag, user, trigger_request = nil) def create_builds(ref, tag, user, trigger_request = nil)
return if skip_ci? && trigger_request.blank?
return unless config_processor return unless config_processor
config_processor.stages.any? do |stage| config_processor.stages.any? do |stage|
CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present? CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request, 'success').present?
end end
end end
def create_next_builds(ref, tag, user, trigger_request) def create_next_builds(build)
return if skip_ci? && trigger_request.blank?
return unless config_processor return unless config_processor
stages = builds.where(ref: ref, tag: tag, trigger_request: trigger_request).group_by(&:stage) # don't create other builds if this one is retried
latest_builds = builds.similar(build).latest
return unless latest_builds.exists?(build.id)
config_processor.stages.any? do |stage| # get list of stages after this build
unless stages.include?(stage) next_stages = config_processor.stages.drop_while { |stage| stage != build.stage }
CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present? next_stages.delete(build.stage)
end
# get status for all prior builds
prior_builds = latest_builds.reject { |other_build| next_stages.include?(other_build.stage) }
status = Ci::Status.get_status(prior_builds)
# create builds for next stages based
next_stages.any? do |stage|
CreateBuildsService.new.execute(self, stage, build.ref, build.tag, build.user, build.trigger_request, status).present?
end end
end end
def refs def refs
builds.group(:ref).pluck(:ref) statuses.order(:ref).pluck(:ref).uniq
end end
def last_ref def latest_statuses
builds.latest.first.try(:ref) @latest_statuses ||= statuses.latest.to_a
end end
def builds_without_retry def latest_builds
builds.latest @latest_builds ||= builds.latest.to_a
end end
def builds_without_retry_for_ref(ref) def latest_builds_for_ref(ref)
builds.for_ref(ref).latest latest_builds.select { |build| build.ref == ref }
end end
def retried_builds def retried
@retried_builds ||= (builds.order(id: :desc) - builds_without_retry) @retried ||= (statuses.order(id: :desc) - statuses.latest)
end end
def status def status
if skip_ci? if yaml_errors.present?
return 'skipped'
elsif yaml_errors.present?
return 'failed' return 'failed'
elsif builds.none?
return 'skipped'
elsif success?
'success'
elsif pending?
'pending'
elsif running?
'running'
elsif canceled?
'canceled'
else
'failed'
end end
@status ||= Ci::Status.get_status(latest_statuses)
end end
def pending? def pending?
builds_without_retry.all? do |build| status == 'pending'
build.pending?
end
end end
def running? def running?
builds_without_retry.any? do |build| status == 'running'
build.running? || build.pending?
end
end end
def success? def success?
builds_without_retry.all? do |build| status == 'success'
build.success? || build.ignored?
end
end end
def failed? def failed?
...@@ -169,26 +161,21 @@ module Ci ...@@ -169,26 +161,21 @@ module Ci
end end
def canceled? def canceled?
builds_without_retry.all? do |build| status == 'canceled'
build.canceled?
end
end end
def duration def duration
@duration ||= builds_without_retry.select(&:duration).sum(&:duration).to_i duration_array = latest_statuses.map(&:duration).compact
end duration_array.reduce(:+).to_i
def duration_for_ref(ref)
builds_without_retry_for_ref(ref).select(&:duration).sum(&:duration).to_i
end end
def finished_at def finished_at
@finished_at ||= builds.order('finished_at DESC').first.try(:finished_at) @finished_at ||= statuses.order('finished_at DESC').first.try(:finished_at)
end end
def coverage def coverage
if project.coverage_enabled? if project.coverage_enabled?
coverage_array = builds_without_retry.map(&:coverage).compact coverage_array = latest_builds.map(&:coverage).compact
if coverage_array.size >= 1 if coverage_array.size >= 1
'%.2f' % (coverage_array.reduce(:+) / coverage_array.size) '%.2f' % (coverage_array.reduce(:+) / coverage_array.size)
end end
...@@ -196,7 +183,7 @@ module Ci ...@@ -196,7 +183,7 @@ module Ci
end end
def matrix_for_ref?(ref) def matrix_for_ref?(ref)
builds_without_retry_for_ref(ref).pluck(:id).size > 1 latest_builds_for_ref(ref).size > 1
end end
def config_processor def config_processor
...@@ -217,7 +204,6 @@ module Ci ...@@ -217,7 +204,6 @@ module Ci
end end
def skip_ci? def skip_ci?
return false if builds.any?
git_commit_message =~ /(\[ci skip\])/ if git_commit_message git_commit_message =~ /(\[ci skip\])/ if git_commit_message
end end
...@@ -225,16 +211,6 @@ module Ci ...@@ -225,16 +211,6 @@ module Ci
update!(committed_at: DateTime.now) update!(committed_at: DateTime.now)
end end
def should_create_next_builds?(build)
# don't create other builds if this one is retried
other_builds = builds.similar(build).latest
return false unless other_builds.include?(build)
other_builds.all? do |build|
build.success? || build.ignored?
end
end
private private
def save_yaml_error(error) def save_yaml_error(error)
......
...@@ -115,12 +115,12 @@ module Ci ...@@ -115,12 +115,12 @@ module Ci
web_url web_url
end end
def any_runners? def any_runners?(&block)
if runners.active.any? if runners.active.any?(&block)
return true return true
end end
shared_runners_enabled && Ci::Runner.shared.active.any? shared_runners_enabled && Ci::Runner.shared.active.any?(&block)
end end
def set_default_values def set_default_values
...@@ -205,7 +205,7 @@ module Ci ...@@ -205,7 +205,7 @@ module Ci
end end
def commits def commits
gl_project.ci_commits gl_project.ci_commits.ordered
end end
def builds def builds
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
module Ci module Ci
class Runner < ActiveRecord::Base class Runner < ActiveRecord::Base
extend Ci::Model extend Ci::Model
LAST_CONTACT_TIME = 5.minutes.ago
has_many :builds, class_name: 'Ci::Build' has_many :builds, class_name: 'Ci::Build'
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
...@@ -33,6 +35,7 @@ module Ci ...@@ -33,6 +35,7 @@ module Ci
scope :shared, ->() { where(is_shared: true) } scope :shared, ->() { where(is_shared: true) }
scope :active, ->() { where(active: true) } scope :active, ->() { where(active: true) }
scope :paused, ->() { where(active: false) } scope :paused, ->() { where(active: false) }
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
acts_as_taggable acts_as_taggable
...@@ -56,7 +59,7 @@ module Ci ...@@ -56,7 +59,7 @@ module Ci
end end
def display_name def display_name
return token unless !description.blank? return short_sha unless !description.blank?
description description
end end
...@@ -65,6 +68,20 @@ module Ci ...@@ -65,6 +68,20 @@ module Ci
is_shared is_shared
end end
def online?
contacted_at && contacted_at > LAST_CONTACT_TIME
end
def status
if contacted_at.nil?
:not_connected
elsif active?
online? ? :online : :offline
else
:paused
end
end
def belongs_to_one_project? def belongs_to_one_project?
runner_projects.count == 1 runner_projects.count == 1
end end
...@@ -78,7 +95,7 @@ module Ci ...@@ -78,7 +95,7 @@ module Ci
end end
def short_sha def short_sha
token[0...10] token[0...8] if token
end end
end end
end end
...@@ -184,4 +184,12 @@ class Commit ...@@ -184,4 +184,12 @@ class Commit
def parents def parents
@parents ||= Commit.decorate(super, project) @parents ||= Commit.decorate(super, project)
end end
def ci_commit
project.ci_commit(sha)
end
def status
ci_commit.try(:status) || :not_found
end
end end
class CommitStatus < ActiveRecord::Base
self.table_name = 'ci_builds'
belongs_to :commit, class_name: 'Ci::Commit'
belongs_to :user
validates :commit, presence: true
validates :status, inclusion: { in: %w(pending running failed success canceled) }
validates_presence_of :name
alias_attribute :author, :user
scope :running, -> { where(status: 'running') }
scope :pending, -> { where(status: 'pending') }
scope :success, -> { where(status: 'success') }
scope :failed, -> { where(status: 'failed') }
scope :running_or_pending, -> { where(status:[:running, :pending]) }
scope :finished, -> { where(status:[:success, :failed, :canceled]) }
scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :ref)) }
scope :ordered, -> { order(:ref, :stage_idx, :name) }
scope :for_ref, ->(ref) { where(ref: ref) }
scope :running_or_pending, -> { where(status: [:running, :pending]) }
state_machine :status, initial: :pending do
event :run do
transition pending: :running
end
event :drop do
transition [:pending, :running] => :failed
end
event :success do
transition [:pending, :running] => :success
end
event :cancel do
transition [:pending, :running] => :canceled
end
after_transition pending: :running do |build, transition|
build.update_attributes started_at: Time.now
end
after_transition any => [:success, :failed, :canceled] do |build, transition|
build.update_attributes finished_at: Time.now
end
state :pending, value: 'pending'
state :running, value: 'running'
state :failed, value: 'failed'
state :success, value: 'success'
state :canceled, value: 'canceled'
end
delegate :sha, :short_sha, :gl_project,
to: :commit, prefix: false
# TODO: this should be removed with all references
def before_sha
Gitlab::Git::BLANK_SHA
end
def started?
!pending? && !canceled? && started_at
end
def active?
running? || pending?
end
def complete?
canceled? || success? || failed?
end
def duration
if started_at && finished_at
finished_at - started_at
elsif started_at
Time.now - started_at
end
end
def cancel_url
nil
end
def retry_url
nil
end
def show_warning?
false
end
end
...@@ -47,7 +47,8 @@ module Issuable ...@@ -47,7 +47,8 @@ module Issuable
prefix: true prefix: true
attr_mentionable :title, :description attr_mentionable :title, :description
participant :author, :assignee, :notes, :mentioned_users
participant :author, :assignee, :notes_with_associations, :mentioned_users
end end
module ClassMethods module ClassMethods
...@@ -176,6 +177,10 @@ module Issuable ...@@ -176,6 +177,10 @@ module Issuable
self.class.to_s.underscore self.class.to_s.underscore
end end
def notes_with_associations
notes.includes(:author, :project)
end
private private
def filter_superceded_votes(votes, notes) def filter_superceded_votes(votes, notes)
......
...@@ -41,55 +41,49 @@ module Mentionable ...@@ -41,55 +41,49 @@ module Mentionable
self self
end end
# Determine whether or not a cross-reference Note has already been created between this Mentionable and def all_references(current_user = self.author, text = self.mentionable_text)
# the specified target. ext = Gitlab::ReferenceExtractor.new(self.project, current_user)
def has_mentioned?(target) ext.analyze(text)
SystemNoteService.cross_reference_exists?(target, local_reference) ext
end end
def mentioned_users(current_user = nil) def mentioned_users(current_user = nil)
return [] if mentionable_text.blank? all_references(current_user).users.uniq
ext = Gitlab::ReferenceExtractor.new(self.project, current_user)
ext.analyze(mentionable_text)
ext.users.uniq
end end
# Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference. # Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference.
def references(p = project, current_user = self.author, text = mentionable_text) def referenced_mentionables(current_user = self.author, text = self.mentionable_text)
return [] if text.blank? return [] if text.blank?
ext = Gitlab::ReferenceExtractor.new(p, current_user) refs = all_references(current_user, text)
ext.analyze(text) (refs.issues + refs.merge_requests + refs.commits).uniq - [local_reference]
(ext.issues + ext.merge_requests + ext.commits).uniq - [local_reference]
end end
# Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+. # Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+.
def create_cross_references!(p = project, a = author, without = []) def create_cross_references!(author = self.author, without = [], text = self.mentionable_text)
refs = references(p) refs = referenced_mentionables(author, text)
# We're using this method instead of Array diffing because that requires # We're using this method instead of Array diffing because that requires
# both of the object's `hash` values to be the same, which may not be the # both of the object's `hash` values to be the same, which may not be the
# case for otherwise identical Commit objects. # case for otherwise identical Commit objects.
refs.reject! { |ref| without.include?(ref) } refs.reject! { |ref| without.include?(ref) || cross_reference_exists?(ref) }
refs.each do |ref| refs.each do |ref|
SystemNoteService.cross_reference(ref, local_reference, a) SystemNoteService.cross_reference(ref, local_reference, author)
end end
end end
# When a mentionable field is changed, creates cross-reference notes that # When a mentionable field is changed, creates cross-reference notes that
# don't already exist # don't already exist
def create_new_cross_references!(p = project, a = author) def create_new_cross_references!(author = self.author)
changes = detect_mentionable_changes changes = detect_mentionable_changes
return if changes.empty? return if changes.empty?
original_text = changes.collect { |_, vals| vals.first }.join(' ') original_text = changes.collect { |_, vals| vals.first }.join(' ')
preexisting = references(p, self.author, original_text) preexisting = referenced_mentionables(author, original_text)
create_cross_references!(p, a, preexisting) create_cross_references!(author, preexisting)
end end
private private
...@@ -111,4 +105,10 @@ module Mentionable ...@@ -111,4 +105,10 @@ module Mentionable
# Only include changed fields that are mentionable # Only include changed fields that are mentionable
source.select { |key, val| mentionable.include?(key) } source.select { |key, val| mentionable.include?(key) }
end end
# Determine whether or not a cross-reference Note has already been created between this Mentionable and
# the specified target.
def cross_reference_exists?(target)
SystemNoteService.cross_reference_exists?(target, local_reference)
end
end end
...@@ -37,8 +37,8 @@ module Participable ...@@ -37,8 +37,8 @@ module Participable
# Be aware that this method makes a lot of sql queries. # Be aware that this method makes a lot of sql queries.
# Save result into variable if you are going to reuse it inside same request # Save result into variable if you are going to reuse it inside same request
def participants(current_user = self.author, project = self.project) def participants(current_user = self.author)
participants = self.class.participant_attrs.flat_map do |attr| self.class.participant_attrs.flat_map do |attr|
meth = method(attr) meth = method(attr)
value = value =
...@@ -48,28 +48,22 @@ module Participable ...@@ -48,28 +48,22 @@ module Participable
meth.call meth.call
end end
participants_for(value, current_user, project) participants_for(value, current_user)
end.compact.uniq end.compact.uniq.select do |user|
user.can?(:read_project, self.project)
if project
participants.select! do |user|
user.can?(:read_project, project)
end
end end
participants
end end
private private
def participants_for(value, current_user = nil, project = nil) def participants_for(value, current_user = nil)
case value case value
when User when User
[value] [value]
when Enumerable, ActiveRecord::Relation when Enumerable, ActiveRecord::Relation
value.flat_map { |v| participants_for(v, current_user, project) } value.flat_map { |v| participants_for(v, current_user) }
when Participable when Participable
value.participants(current_user, project) value.participants(current_user)
end end
end end
end end
class GenericCommitStatus < CommitStatus
before_validation :set_default_values
# GitHub compatible API
alias_attribute :context, :name
def set_default_values
self.context ||= 'default'
self.stage ||= 'external'
end
def tags
[:external]
end
end
...@@ -64,7 +64,7 @@ class Group < Namespace ...@@ -64,7 +64,7 @@ class Group < Namespace
end end
def owners def owners
@owners ||= group_members.owners.map(&:user) @owners ||= group_members.owners.includes(:user).map(&:user)
end end
def add_users(user_ids, access_level, current_user = nil) def add_users(user_ids, access_level, current_user = nil)
......
class GroupLabel
attr_accessor :title, :labels
alias_attribute :name, :title
def initialize(title, labels)
@title = title
@labels = labels
end
end
class GroupMilestone class GroupMilestone
attr_accessor :title, :milestones
alias_attribute :name, :title alias_attribute :name, :title
def initialize(title, milestones) def initialize(title, milestones)
...@@ -7,18 +7,10 @@ class GroupMilestone ...@@ -7,18 +7,10 @@ class GroupMilestone
@milestones = milestones @milestones = milestones
end end
def title
@title
end
def safe_title def safe_title
@title.parameterize @title.parameterize
end end
def milestones
@milestones
end
def projects def projects
milestones.map { |milestone| milestone.project } milestones.map { |milestone| milestone.project }
end end
......
...@@ -227,7 +227,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -227,7 +227,7 @@ class MergeRequest < ActiveRecord::Base
end end
def work_in_progress? def work_in_progress?
title =~ /\A\[?WIP\]?:? /i !!(title =~ /\A\[?WIP\]?:? /i)
end end
def mergeable? def mergeable?
...@@ -275,7 +275,8 @@ class MergeRequest < ActiveRecord::Base ...@@ -275,7 +275,8 @@ class MergeRequest < ActiveRecord::Base
attrs = { attrs = {
source: source_project.hook_attrs, source: source_project.hook_attrs,
target: target_project.hook_attrs, target: target_project.hook_attrs,
last_commit: nil last_commit: nil,
work_in_progress: work_in_progress?
} }
unless last_commit.nil? unless last_commit.nil?
......
...@@ -163,7 +163,8 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -163,7 +163,8 @@ class MergeRequestDiff < ActiveRecord::Base
merge_request.fetch_ref merge_request.fetch_ref
# Get latest sha of branch from source project # Get latest sha of branch from source project
source_sha = merge_request.source_project.commit(source_branch).sha source_commit = merge_request.source_project.commit(source_branch)
source_sha = source_commit.try(:sha)
Gitlab::CompareResult.new( Gitlab::CompareResult.new(
Gitlab::Git::Compare.new( Gitlab::Git::Compare.new(
......
...@@ -118,6 +118,8 @@ class Namespace < ActiveRecord::Base ...@@ -118,6 +118,8 @@ class Namespace < ActiveRecord::Base
gitlab_shell.add_namespace(path_was) gitlab_shell.add_namespace(path_was)
if gitlab_shell.mv_namespace(path_was, path) if gitlab_shell.mv_namespace(path_was, path)
Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
# If repositories moved successfully we need to # If repositories moved successfully we need to
# send update instructions to users. # send update instructions to users.
# However we cannot allow rollback since we moved namespace dir # However we cannot allow rollback since we moved namespace dir
......
...@@ -60,9 +60,13 @@ class Note < ActiveRecord::Base ...@@ -60,9 +60,13 @@ class Note < ActiveRecord::Base
scope :inc_author_project, ->{ includes(:project, :author) } scope :inc_author_project, ->{ includes(:project, :author) }
scope :inc_author, ->{ includes(:author) } scope :inc_author, ->{ includes(:author) }
scope :with_associations, -> do
includes(:author, :noteable, :updated_by,
project: [:project_members, { group: [:group_members] }])
end
serialize :st_diff serialize :st_diff
before_create :set_diff, if: ->(n) { n.line_code.present? } before_create :set_diff, if: ->(n) { n.line_code.present? }
after_update :set_references
class << self class << self
def discussions_from_notes(notes) def discussions_from_notes(notes)
...@@ -333,15 +337,13 @@ class Note < ActiveRecord::Base ...@@ -333,15 +337,13 @@ class Note < ActiveRecord::Base
end end
def noteable_type_name def noteable_type_name
if noteable_type.present? noteable_type.downcase if noteable_type.present?
noteable_type.downcase
end
end end
# FIXME: Hack for polymorphic associations with STI # FIXME: Hack for polymorphic associations with STI
# For more information visit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations # For more information visit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations
def noteable_type=(sType) def noteable_type=(noteable_type)
super(sType.to_s.classify.constantize.base_class.to_s) super(noteable_type.to_s.classify.constantize.base_class.to_s)
end end
# Reset notes events cache # Reset notes events cache
...@@ -357,10 +359,6 @@ class Note < ActiveRecord::Base ...@@ -357,10 +359,6 @@ class Note < ActiveRecord::Base
Event.reset_event_cache_for(self) Event.reset_event_cache_for(self)
end end
def set_references
create_new_cross_references!(project, author)
end
def system? def system?
read_attribute(:system) read_attribute(:system)
end end
......
...@@ -119,7 +119,7 @@ class Project < ActiveRecord::Base ...@@ -119,7 +119,7 @@ class Project < ActiveRecord::Base
has_many :deploy_keys, through: :deploy_keys_projects has_many :deploy_keys, through: :deploy_keys_projects
has_many :users_star_projects, dependent: :destroy has_many :users_star_projects, dependent: :destroy
has_many :starrers, through: :users_star_projects, source: :user has_many :starrers, through: :users_star_projects, source: :user
has_many :ci_commits, ->() { order('CASE WHEN ci_commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) }, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id
has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build' has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build'
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
...@@ -656,6 +656,8 @@ class Project < ActiveRecord::Base ...@@ -656,6 +656,8 @@ class Project < ActiveRecord::Base
# db changes in order to prevent out of sync between db and fs # db changes in order to prevent out of sync between db and fs
raise Exception.new('repository cannot be renamed') raise Exception.new('repository cannot be renamed')
end end
Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.path)
end end
def hook_attrs def hook_attrs
......
...@@ -48,7 +48,7 @@ class BambooService < CiService ...@@ -48,7 +48,7 @@ class BambooService < CiService
end end
def reset_password def reset_password
if prop_updated?(:bamboo_url) if bamboo_url_changed? && !password_touched?
self.password = nil self.password = nil
end end
end end
......
...@@ -49,7 +49,7 @@ module Ci ...@@ -49,7 +49,7 @@ module Ci
commit = build.commit commit = build.commit
return unless commit return unless commit
return unless commit.builds_without_retry.include? build return unless commit.latest_builds.include? build
case commit.status.to_sym case commit.status.to_sym
when :failed when :failed
......
...@@ -48,7 +48,7 @@ module Ci ...@@ -48,7 +48,7 @@ module Ci
# it doesn't make sense to send emails for retried builds # it doesn't make sense to send emails for retried builds
commit = build.commit commit = build.commit
return unless commit return unless commit
return unless commit.builds_without_retry.include?(build) return unless commit.latest_builds.include?(build)
case build.status.to_sym case build.status.to_sym
when :failed when :failed
......
...@@ -23,7 +23,7 @@ module Ci ...@@ -23,7 +23,7 @@ module Ci
def attachments def attachments
fields = [] fields = []
commit.builds_without_retry.each do |build| commit.latest_builds.each do |build|
next if build.allow_failure? next if build.allow_failure?
next unless build.failed? next unless build.failed?
fields << { fields << {
......
...@@ -48,7 +48,7 @@ module Ci ...@@ -48,7 +48,7 @@ module Ci
commit = build.commit commit = build.commit
return unless commit return unless commit
return unless commit.builds_without_retry.include?(build) return unless commit.latest_builds.include?(build)
case commit.status.to_sym case commit.status.to_sym
when :failed when :failed
......
...@@ -45,7 +45,7 @@ class TeamcityService < CiService ...@@ -45,7 +45,7 @@ class TeamcityService < CiService
end end
def reset_password def reset_password
if prop_updated?(:teamcity_url) if teamcity_url_changed? && !password_touched?
self.password = nil self.password = nil
end end
end end
......
...@@ -139,15 +139,28 @@ class ProjectTeam ...@@ -139,15 +139,28 @@ class ProjectTeam
Gitlab::Access.options.key max_member_access(user_id) Gitlab::Access.options.key max_member_access(user_id)
end end
# This method assumes project and group members are eager loaded for optimal
# performance.
def max_member_access(user_id) def max_member_access(user_id)
access = [] access = []
access << project.project_members.find_by(user_id: user_id).try(:access_field)
project.project_members.each do |member|
if member.user_id == user_id
access << member.access_field if member.access_field
break
end
end
if group if group
access << group.group_members.find_by(user_id: user_id).try(:access_field) group.group_members.each do |member|
if member.user_id == user_id
access << member.access_field if member.access_field
break
end
end
end end
access.compact.max access.max
end end
private private
......
...@@ -480,6 +480,10 @@ class Repository ...@@ -480,6 +480,10 @@ class Repository
end end
end end
def merge_base(first_commit_id, second_commit_id)
rugged.merge_base(first_commit_id, second_commit_id)
end
def search_files(query, ref) def search_files(query, ref)
offset = 2 offset = 2
args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} #{query} #{ref || root_ref}) args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} #{query} #{ref || root_ref})
......
...@@ -33,6 +33,8 @@ class Service < ActiveRecord::Base ...@@ -33,6 +33,8 @@ class Service < ActiveRecord::Base
after_initialize :initialize_properties after_initialize :initialize_properties
after_commit :reset_updated_properties
belongs_to :project belongs_to :project
has_one :service_hook has_one :service_hook
...@@ -103,6 +105,7 @@ class Service < ActiveRecord::Base ...@@ -103,6 +105,7 @@ class Service < ActiveRecord::Base
# Provide convenient accessor methods # Provide convenient accessor methods
# for each serialized property. # for each serialized property.
# Also keep track of updated properties in a similar way as ActiveModel::Dirty
def self.prop_accessor(*args) def self.prop_accessor(*args)
args.each do |arg| args.each do |arg|
class_eval %{ class_eval %{
...@@ -111,21 +114,39 @@ class Service < ActiveRecord::Base ...@@ -111,21 +114,39 @@ class Service < ActiveRecord::Base
end end
def #{arg}=(value) def #{arg}=(value)
updated_properties['#{arg}'] = #{arg} unless #{arg}_changed?
self.properties['#{arg}'] = value self.properties['#{arg}'] = value
end end
def #{arg}_changed?
#{arg}_touched? && #{arg} != #{arg}_was
end
def #{arg}_touched?
updated_properties.include?('#{arg}')
end
def #{arg}_was
updated_properties['#{arg}']
end
} }
end end
end end
# ActiveRecord does not provide a mechanism to track changes in serialized keys. # Returns a hash of the properties that have been assigned a new value since last save,
# This is why we need to perform extra query to do it mannually. # indicating their original values (attr => original value).
def prop_updated?(prop_name) # ActiveRecord does not provide a mechanism to track changes in serialized keys,
relation_name = self.type.underscore # so we need a specific implementation for service properties.
previous_value = project.send(relation_name).send(prop_name) # This allows to track changes to properties set with the accessor methods,
return false if previous_value.nil? # but not direct manipulation of properties hash.
previous_value != send(prop_name) def updated_properties
@updated_properties ||= ActiveSupport::HashWithIndifferentAccess.new
end end
def reset_updated_properties
@updated_properties = nil
end
def async_execute(data) def async_execute(data)
return unless supported_events.include?(data[:object_kind]) return unless supported_events.include?(data[:object_kind])
......
...@@ -68,6 +68,7 @@ class User < ActiveRecord::Base ...@@ -68,6 +68,7 @@ class User < ActiveRecord::Base
include Referable include Referable
include Sortable include Sortable
include TokenAuthenticatable include TokenAuthenticatable
include CaseSensitivity
default_value_for :admin, false default_value_for :admin, false
default_value_for :can_create_group, gitlab_config.default_can_create_group default_value_for :can_create_group, gitlab_config.default_can_create_group
...@@ -273,8 +274,13 @@ class User < ActiveRecord::Base ...@@ -273,8 +274,13 @@ class User < ActiveRecord::Base
end end
def by_login(login) def by_login(login)
where('lower(username) = :value OR lower(email) = :value', return nil unless login
value: login.to_s.downcase).first
if login.include?('@'.freeze)
unscoped.iwhere(email: login).take
else
unscoped.iwhere(username: login).take
end
end end
def find_by_username!(username) def find_by_username!(username)
......
...@@ -9,17 +9,10 @@ class ArchiveRepositoryService ...@@ -9,17 +9,10 @@ class ArchiveRepositoryService
def execute(options = {}) def execute(options = {})
project.repository.clean_old_archives project.repository.clean_old_archives
raise "No archive file path" unless file_path metadata = project.repository.archive_metadata(ref, storage_path, format)
raise "Repository or ref not found" if metadata.empty?
return file_path if archived? metadata
unless archiving?
RepositoryArchiveWorker.perform_async(project.id, ref, format)
end
archived = wait_until_archived(options[:timeout] || 5.0)
file_path if archived
end end
private private
...@@ -27,36 +20,4 @@ class ArchiveRepositoryService ...@@ -27,36 +20,4 @@ class ArchiveRepositoryService
def storage_path def storage_path
Gitlab.config.gitlab.repository_downloads_path Gitlab.config.gitlab.repository_downloads_path
end end
def file_path
@file_path ||= project.repository.archive_file_path(ref, storage_path, format)
end
def pid_file_path
@pid_file_path ||= project.repository.archive_pid_file_path(ref, storage_path, format)
end
def archived?
File.exist?(file_path)
end
def archiving?
File.exist?(pid_file_path)
end
def wait_until_archived(timeout = 5.0)
return archived? if timeout == 0.0
t1 = Time.now
begin
sleep 0.1
success = archived?
t2 = Time.now
end until success || t2 - t1 >= timeout
success
end
end end
module Ci module Ci
class CreateBuildsService class CreateBuildsService
def execute(commit, stage, ref, tag, user, trigger_request) def execute(commit, stage, ref, tag, user, trigger_request, status)
builds_attrs = commit.config_processor.builds_for_stage_and_ref(stage, ref, tag) builds_attrs = commit.config_processor.builds_for_stage_and_ref(stage, ref, tag)
# check when to create next build
builds_attrs = builds_attrs.select do |build_attrs|
case build_attrs[:when]
when 'on_success'
status == 'success'
when 'on_failure'
status == 'failed'
when 'always'
%w(success failed).include?(status)
end
end
builds_attrs.map do |build_attrs| builds_attrs.map do |build_attrs|
# don't create the same build twice # don't create the same build twice
unless commit.builds.find_by(ref: ref, tag: tag, trigger_request: trigger_request, name: build_attrs[:name]) unless commit.builds.find_by(ref: ref, tag: tag, trigger_request: trigger_request, name: build_attrs[:name])
......
...@@ -17,8 +17,10 @@ module Ci ...@@ -17,8 +17,10 @@ module Ci
tag = origin_ref.start_with?('refs/tags/') tag = origin_ref.start_with?('refs/tags/')
commit = project.gl_project.ensure_ci_commit(sha) commit = project.gl_project.ensure_ci_commit(sha)
commit.update_committed! unless commit.skip_ci?
commit.create_builds(ref, tag, user) commit.update_committed!
commit.create_builds(ref, tag, user)
end
commit commit
end end
......
...@@ -17,7 +17,7 @@ module Ci ...@@ -17,7 +17,7 @@ module Ci
builds = builds.order('created_at ASC') builds = builds.order('created_at ASC')
build = builds.find do |build| build = builds.find do |build|
(build.tag_list - current_runner.tag_list).empty? build.can_be_served?(current_runner)
end end
......
...@@ -49,10 +49,13 @@ class GitPushService ...@@ -49,10 +49,13 @@ class GitPushService
elsif push_to_existing_branch?(ref, oldrev) elsif push_to_existing_branch?(ref, oldrev)
# Collect data for this git push # Collect data for this git push
@push_commits = project.repository.commits_between(oldrev, newrev) @push_commits = project.repository.commits_between(oldrev, newrev)
project.update_merge_requests(oldrev, newrev, ref, @user)
process_commit_messages(ref) process_commit_messages(ref)
end end
# Update merge requests that may be affected by this push. A new branch
# could cause the last commit of a merge request to change.
project.update_merge_requests(oldrev, newrev, ref, @user)
@push_data = build_push_data(oldrev, newrev, ref) @push_data = build_push_data(oldrev, newrev, ref)
# If CI was disabled but .gitlab-ci.yml file was pushed # If CI was disabled but .gitlab-ci.yml file was pushed
...@@ -74,48 +77,30 @@ class GitPushService ...@@ -74,48 +77,30 @@ class GitPushService
def process_commit_messages(ref) def process_commit_messages(ref)
is_default_branch = is_default_branch?(ref) is_default_branch = is_default_branch?(ref)
@push_commits.each do |commit| authors = Hash.new do |hash, commit|
# Close issues if these commits were pushed to the project's default branch and the commit message matches the email = commit.author_email
# closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to return hash[email] if hash.has_key?(email)
# a different branch.
issues_to_close = commit.closes_issues(user)
# Load commit author only if needed. hash[email] = commit_user(commit)
# For push with 1k commits it prevents 900+ requests in database end
author = nil
@push_commits.each do |commit|
# Keep track of the issues that will be actually closed because they are on a default branch. # Keep track of the issues that will be actually closed because they are on a default branch.
# Hence, when creating cross-reference notes, the not-closed issues (on non-default branches) # Hence, when creating cross-reference notes, the not-closed issues (on non-default branches)
# will also have cross-reference. # will also have cross-reference.
actually_closed_issues = [] closed_issues = []
if issues_to_close.present? && is_default_branch if is_default_branch
author ||= commit_user(commit) # Close issues if these commits were pushed to the project's default branch and the commit message matches the
actually_closed_issues = issues_to_close # closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to
issues_to_close.each do |issue| # a different branch.
Issues::CloseService.new(project, author, {}).execute(issue, commit) closed_issues = commit.closes_issues(user)
closed_issues.each do |issue|
Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit)
end end
end end
if project.default_issues_tracker? commit.create_cross_references!(authors[commit], closed_issues)
create_cross_reference_notes(commit, actually_closed_issues)
end
end
end
def create_cross_reference_notes(commit, issues_to_close)
# Create cross-reference notes for any other references than those given in issues_to_close.
# Omit any issues that were referenced in an issue-closing phrase, or have already been
# mentioned from this commit (probably from this commit being pushed to a different branch).
refs = commit.references(project, user) - issues_to_close
refs.reject! { |r| commit.has_mentioned?(r) }
if refs.present?
author ||= commit_user(commit)
refs.each do |r|
SystemNoteService.cross_reference(r, commit, author)
end
end end
end end
......
...@@ -10,7 +10,7 @@ module Issues ...@@ -10,7 +10,7 @@ module Issues
issue.update_attributes(label_ids: label_params) issue.update_attributes(label_ids: label_params)
notification_service.new_issue(issue, current_user) notification_service.new_issue(issue, current_user)
event_service.open_issue(issue, current_user) event_service.open_issue(issue, current_user)
issue.create_cross_references!(issue.project, current_user) issue.create_cross_references!(current_user)
execute_hooks(issue, 'open') execute_hooks(issue, 'open')
end end
......
...@@ -35,7 +35,7 @@ module Issues ...@@ -35,7 +35,7 @@ module Issues
create_title_change_note(issue, issue.previous_changes['title'].first) create_title_change_note(issue, issue.previous_changes['title'].first)
end end
issue.create_new_cross_references!(issue.project, current_user) issue.create_new_cross_references!
execute_hooks(issue, 'update') execute_hooks(issue, 'update')
end end
......
module Labels
class GroupService < ::BaseService
def initialize(project_labels)
@project_labels = project_labels.group_by(&:title)
end
def execute
build(@project_labels)
end
def label(title)
if title
group_label = @project_labels[title].group_by(&:title)
build(group_label).first
else
nil
end
end
private
def build(label)
label.map { |title, labels| GroupLabel.new(title, labels) }
end
end
end
...@@ -18,7 +18,7 @@ module MergeRequests ...@@ -18,7 +18,7 @@ module MergeRequests
merge_request.update_attributes(label_ids: label_params) merge_request.update_attributes(label_ids: label_params)
event_service.open_mr(merge_request, current_user) event_service.open_mr(merge_request, current_user)
notification_service.new_merge_request(merge_request, current_user) notification_service.new_merge_request(merge_request, current_user)
merge_request.create_cross_references!(merge_request.project, current_user) merge_request.create_cross_references!(current_user)
execute_hooks(merge_request) execute_hooks(merge_request)
end end
......
...@@ -6,12 +6,20 @@ module MergeRequests ...@@ -6,12 +6,20 @@ module MergeRequests
@oldrev, @newrev = oldrev, newrev @oldrev, @newrev = oldrev, newrev
@branch_name = Gitlab::Git.ref_name(ref) @branch_name = Gitlab::Git.ref_name(ref)
@fork_merge_requests = @project.fork_merge_requests.opened @fork_merge_requests = @project.fork_merge_requests.opened
@commits = @project.repository.commits_between(oldrev, newrev) @commits = []
# Leave a system note if a branch were deleted/added
if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev)
comment_mr_branch_presence_changed
comment_mr_with_commits if @commits.present?
else
@commits = @project.repository.commits_between(oldrev, newrev)
comment_mr_with_commits
close_merge_requests
end
close_merge_requests
reload_merge_requests reload_merge_requests
execute_mr_web_hooks execute_mr_web_hooks
comment_mr_with_commits
true true
end end
...@@ -31,7 +39,6 @@ module MergeRequests ...@@ -31,7 +39,6 @@ module MergeRequests
commit_ids.include?(merge_request.last_commit.id) commit_ids.include?(merge_request.last_commit.id)
end end
merge_requests.uniq.select(&:source_project).each do |merge_request| merge_requests.uniq.select(&:source_project).each do |merge_request|
MergeRequests::PostMergeService. MergeRequests::PostMergeService.
new(merge_request.target_project, @current_user). new(merge_request.target_project, @current_user).
...@@ -70,13 +77,38 @@ module MergeRequests ...@@ -70,13 +77,38 @@ module MergeRequests
end end
end end
# Add comment about branches being deleted or added to merge requests
def comment_mr_branch_presence_changed
presence = Gitlab::Git.blank_ref?(@oldrev) ? :add : :delete
merge_requests_for_source_branch.each do |merge_request|
last_commit = merge_request.last_commit
# Only look at changed commits in restore branch case
unless Gitlab::Git.blank_ref?(@newrev)
begin
# Since any number of commits could have been made to the restored branch,
# find the common root to see what has been added.
common_ref = @project.repository.merge_base(last_commit.id, @newrev)
# If the a commit no longer exists in this repo, gitlab_git throws
# a Rugged::OdbError. This is fixed in https://gitlab.com/gitlab-org/gitlab_git/merge_requests/52
@commits = @project.repository.commits_between(common_ref, @newrev) if common_ref
rescue
end
# Prevent system notes from seeing a blank SHA
@oldrev = nil
end
SystemNoteService.change_branch_presence(
merge_request, merge_request.project, @current_user,
:source, @branch_name, presence)
end
end
# Add comment about pushing new commits to merge requests # Add comment about pushing new commits to merge requests
def comment_mr_with_commits def comment_mr_with_commits
merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a merge_requests_for_source_branch.each do |merge_request|
merge_requests += @fork_merge_requests.where(source_branch: @branch_name).to_a
merge_requests = filter_merge_requests(merge_requests)
merge_requests.each do |merge_request|
mr_commit_ids = Set.new(merge_request.commits.map(&:id)) mr_commit_ids = Set.new(merge_request.commits.map(&:id))
new_commits, existing_commits = @commits.partition do |commit| new_commits, existing_commits = @commits.partition do |commit|
...@@ -91,14 +123,7 @@ module MergeRequests ...@@ -91,14 +123,7 @@ module MergeRequests
# Call merge request webhook with update branches # Call merge request webhook with update branches
def execute_mr_web_hooks def execute_mr_web_hooks
merge_requests = @project.origin_merge_requests.opened merge_requests_for_source_branch.each do |merge_request|
.where(source_branch: @branch_name)
.to_a
merge_requests += @fork_merge_requests.where(source_branch: @branch_name)
.to_a
merge_requests = filter_merge_requests(merge_requests)
merge_requests.each do |merge_request|
execute_hooks(merge_request, 'update') execute_hooks(merge_request, 'update')
end end
end end
...@@ -106,5 +131,13 @@ module MergeRequests ...@@ -106,5 +131,13 @@ module MergeRequests
def filter_merge_requests(merge_requests) def filter_merge_requests(merge_requests)
merge_requests.uniq.select(&:source_project) merge_requests.uniq.select(&:source_project)
end end
def merge_requests_for_source_branch
@source_merge_requests ||= begin
merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a
merge_requests += @fork_merge_requests.where(source_branch: @branch_name).to_a
filter_merge_requests(merge_requests)
end
end
end end
end end
...@@ -59,7 +59,7 @@ module MergeRequests ...@@ -59,7 +59,7 @@ module MergeRequests
merge_request.mark_as_unchecked merge_request.mark_as_unchecked
end end
merge_request.create_new_cross_references!(merge_request.project, current_user) merge_request.create_new_cross_references!
execute_hooks(merge_request, 'update') execute_hooks(merge_request, 'update')
end end
......
...@@ -11,13 +11,7 @@ module Notes ...@@ -11,13 +11,7 @@ module Notes
# Skip system notes, like status changes and cross-references. # Skip system notes, like status changes and cross-references.
unless note.system unless note.system
event_service.leave_note(note, note.author) event_service.leave_note(note, note.author)
note.create_cross_references!
# Create a cross-reference note if this Note contains GFM that names an
# issue, merge request, or commit.
note.references.each do |mentioned|
SystemNoteService.cross_reference(mentioned, note.noteable, note.author)
end
execute_hooks(note) execute_hooks(note)
end end
end end
......
...@@ -4,7 +4,7 @@ module Notes ...@@ -4,7 +4,7 @@ module Notes
return note unless note.editable? return note unless note.editable?
note.update_attributes(params.merge(updated_by: current_user)) note.update_attributes(params.merge(updated_by: current_user))
note.create_new_cross_references!
note.reset_events_cache note.reset_events_cache
note note
......
...@@ -27,6 +27,7 @@ module Projects ...@@ -27,6 +27,7 @@ module Projects
def transfer(project, new_namespace) def transfer(project, new_namespace)
Project.transaction do Project.transaction do
old_path = project.path_with_namespace old_path = project.path_with_namespace
old_namespace = project.namespace
new_path = File.join(new_namespace.try(:path) || '', project.path) new_path = File.join(new_namespace.try(:path) || '', project.path)
if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present? if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present?
...@@ -51,6 +52,9 @@ module Projects ...@@ -51,6 +52,9 @@ module Projects
# clear project cached events # clear project cached events
project.reset_events_cache project.reset_events_cache
# Move uploads
Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path)
true true
end end
end end
......
...@@ -168,6 +168,31 @@ class SystemNoteService ...@@ -168,6 +168,31 @@ class SystemNoteService
create_note(noteable: noteable, project: project, author: author, note: body) create_note(noteable: noteable, project: project, author: author, note: body)
end end
# Called when a branch in Noteable is added or deleted
#
# noteable - Noteable object
# project - Project owning noteable
# author - User performing the change
# branch_type - :source or :target
# branch - branch name
# presence - :add or :delete
#
# Example Note text:
#
# "Restored target branch `feature`"
#
# Returns the created Note object
def self.change_branch_presence(noteable, project, author, branch_type, branch, presence)
verb =
if presence == :add
'restored'
else
'deleted'
end
body = "#{verb} #{branch_type.to_s} branch `#{branch}`".capitalize
create_note(noteable: noteable, project: project, author: author, note: body)
end
# Called when a Mentionable references a Noteable # Called when a Mentionable references a Noteable
# #
# noteable - Noteable object being referenced # noteable - Noteable object being referenced
......
...@@ -26,7 +26,7 @@ class FileUploader < CarrierWave::Uploader::Base ...@@ -26,7 +26,7 @@ class FileUploader < CarrierWave::Uploader::Base
end end
def secure_url def secure_url
File.join(Gitlab.config.gitlab.url, @project.path_with_namespace, "uploads", @secret, file.filename) File.join("/uploads", @secret, file.filename)
end end
def file_storage? def file_storage?
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment