Commit cfc4a2dbe3652180c1d08d5d9e154e28cbb340fb

Authored by Franz-Robert van Vugt
2 parents f03820eb fc5ac145

Merge branch 'master' of https://github.com/gitlabhq/gitlabhq into patch-1

Showing 170 changed files with 2246 additions and 1429 deletions   Show diff stats
1 ---color --drb 1 +--color
@@ -19,10 +19,10 @@ rvm: @@ -19,10 +19,10 @@ rvm:
19 services: 19 services:
20 - redis-server 20 - redis-server
21 before_script: 21 before_script:
22 - - "bundle exec rake db:setup"  
23 - - "bundle exec rake db:seed_fu"  
24 - "cp config/database.yml.$DB config/database.yml" 22 - "cp config/database.yml.$DB config/database.yml"
25 - "cp config/gitlab.yml.example config/gitlab.yml" 23 - "cp config/gitlab.yml.example config/gitlab.yml"
  24 + - "bundle exec rake db:setup"
  25 + - "bundle exec rake db:seed_fu"
26 script: "bundle exec rake $TASK --trace" 26 script: "bundle exec rake $TASK --trace"
27 notifications: 27 notifications:
28 email: false 28 email: false
  1 +v 6.8.0
  2 + - Ability to at mention users that are participating in issue and merge req. discussion
  3 + - Enabled GZip Compression for assets in example Nginx, make sure that Nginx is compiled with --with-http_gzip_static_module flag (this is default in Ubuntu)
  4 + - Make user search case-insensitive (Christopher Arnold)
  5 +
  6 +v 6.7.2
  7 + - Fix upgrader script
  8 +
  9 +v 6.7.1
  10 + - Fix GitLab CI integration
  11 +
1 v 6.7.0 12 v 6.7.0
2 - Increased the example Nginx client_max_body_size from 5MB to 20MB, consider updating it manually on existing installations 13 - Increased the example Nginx client_max_body_size from 5MB to 20MB, consider updating it manually on existing installations
3 - Add support for Gemnasium as a Project Service (Olivier Gonzalez) 14 - Add support for Gemnasium as a Project Service (Olivier Gonzalez)
@@ -9,6 +20,7 @@ v 6.7.0 @@ -9,6 +20,7 @@ v 6.7.0
9 - Show contribution guide link for new issue form (Jeroen van Baarsen) 20 - Show contribution guide link for new issue form (Jeroen van Baarsen)
10 - Fix CI status for merge requests from fork 21 - Fix CI status for merge requests from fork
11 - Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard) 22 - Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard)
  23 + - New page load indicator that includes a spinner that scrolls with the page
12 - Converted all the help sections into markdown 24 - Converted all the help sections into markdown
13 - LDAP user filters 25 - LDAP user filters
14 - Streamline the content of notification emails (Pierre de La Morinerie) 26 - Streamline the content of notification emails (Pierre de La Morinerie)
@@ -24,6 +36,12 @@ v 6.7.0 @@ -24,6 +36,12 @@ v 6.7.0
24 - Faster authorized_keys rebuilding in `rake gitlab:shell:setup` (requires gitlab-shell 1.8.5) 36 - Faster authorized_keys rebuilding in `rake gitlab:shell:setup` (requires gitlab-shell 1.8.5)
25 - Create and Update MR calls now support the description parameter (Greg Messner) 37 - Create and Update MR calls now support the description parameter (Greg Messner)
26 - Markdown relative links in the wiki link to wiki pages, markdown relative links in repositories link to files in the repository 38 - Markdown relative links in the wiki link to wiki pages, markdown relative links in repositories link to files in the repository
  39 + - Added Slack service integration (Federico Ravasio)
  40 + - Better API responses for access_levels (sponsored by O'Reilly Media)
  41 + - Requires at least 2 unicorn workers
  42 + - Requires gitlab-shell v1.9+
  43 + - Replaced gemoji(due to closed licencing problem) with Phantom Open Emoji library(combined SIL Open Font License, MIT License and the CC 3.0 License)
  44 + - Fix `/:username.keys` response content type (Dmitry Medvinsky)
27 45
28 v 6.6.5 46 v 6.6.5
29 - Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard) 47 - Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard)
CONTRIBUTING.md
@@ -24,9 +24,12 @@ Issues and merge requests should be in English and contain appropriate language @@ -24,9 +24,12 @@ Issues and merge requests should be in English and contain appropriate language
24 24
25 To get support for your particular problem please use the channels as detailed in the [getting help section of the readme](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/README.md#getting-help). Professional [support subscriptions](http://www.gitlab.com/subscription/) and [consulting services](http://www.gitlab.com/consultancy/) are available from [GitLab.com](http://www.gitlab.com/). 25 To get support for your particular problem please use the channels as detailed in the [getting help section of the readme](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/README.md#getting-help). Professional [support subscriptions](http://www.gitlab.com/subscription/) and [consulting services](http://www.gitlab.com/consultancy/) are available from [GitLab.com](http://www.gitlab.com/).
26 26
27 -The [issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) is only for obvious bugs or misbehavior in the latest [stable or development release of GitLab](MAINTENANCE.md). When submitting an issue please conform to the issue submission guidelines listed below. Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue. 27 +The [issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) is only for obvious errors in the latest [stable or development release of GitLab](MAINTENANCE.md).
  28 +If something is wrong but it is not a regression compared to older versions of GitLab please do not open an issue but a feature request.
  29 +When submitting an issue please conform to the issue submission guidelines listed below.
  30 +Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue.
28 31
29 -Do not use the issue tracker for feature requests. We have a specific [feedback and suggestions forum](http://feedback.gitlab.com) for this purpose. 32 +Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose.
30 33
31 Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there. 34 Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
32 35
@@ -48,7 +51,7 @@ Please send a merge request with a tested solution or a merge request with a fai @@ -48,7 +51,7 @@ Please send a merge request with a tested solution or a merge request with a fai
48 51
49 ## Merge requests 52 ## Merge requests
50 53
51 -We welcome merge requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a merge request for are listed with the [status 'accepting merge requests' on our feedback forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome. If you want to add a new feature that is not marked it is best to first create a feedback issue (if there isn't one already) and leave a comment asking for it to be marked accepting merge requests. Please include screenshots or wireframes if the feature will also change the UI. 54 +We welcome merge requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a merge request for are listed with the [status 'accepting merge requests' on our feature request forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome. If you want to add a new feature that is not marked it is best to first create a feedback issue (if there isn't one already) and leave a comment asking for it to be marked accepting merge requests. Please include screenshots or wireframes if the feature will also change the UI.
52 55
53 ### Merge request guidelines 56 ### Merge request guidelines
54 57
@@ -64,7 +67,8 @@ If you can, please submit a merge request with the fix or improvements including @@ -64,7 +67,8 @@ If you can, please submit a merge request with the fix or improvements including
64 1. The MR title should describes the change you want to make 67 1. The MR title should describes the change you want to make
65 1. The MR description should give a motive for your change and the method you used to achieve it 68 1. The MR description should give a motive for your change and the method you used to achieve it
66 1. If the MR changes the UI it should include before and after screenshots 69 1. If the MR changes the UI it should include before and after screenshots
67 -1. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) and/or [feedback items](http://feedback.gitlab.com/) from the merge request description and leave a comment on them with a link back to the MR 70 +1. If the MR changes CSS classes please include the list of affected pages `grep css-class ./app -R`
  71 +1. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) and/or [feature requests](http://feedback.gitlab.com/) from the merge request description and leave a comment on them with a link back to the MR
68 1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submittion 72 1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submittion
69 1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md). 73 1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md).
70 74
@@ -74,6 +78,14 @@ Please keep the change in a single MR **as small as possible**. If you want to c @@ -74,6 +78,14 @@ Please keep the change in a single MR **as small as possible**. If you want to c
74 78
75 For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). Please ensure that your merge request meets the following contribution acceptance criteria. 79 For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). Please ensure that your merge request meets the following contribution acceptance criteria.
76 80
  81 +**Please format your merge request description as follows:**
  82 +
  83 +1. What does this MR do?
  84 +2. Are there points in the code the reviewer needs to double check?
  85 +3. Why was this MR needed?
  86 +4. What are the relevant issue numbers / [Feature requests](http://feedback.gitlab.com/)?
  87 +5. Screenshots (If appropiate)
  88 +
77 ## Contribution acceptance criteria 89 ## Contribution acceptance criteria
78 90
79 1. The change is as small as possible (see the above paragraph for details) 91 1. The change is as small as possible (see the above paragraph for details)
@@ -132,6 +132,9 @@ gem "gitlab-flowdock-git-hook", "~> 0.4.2" @@ -132,6 +132,9 @@ gem "gitlab-flowdock-git-hook", "~> 0.4.2"
132 # Gemnasium integration 132 # Gemnasium integration
133 gem "gemnasium-gitlab-service", "~> 0.2" 133 gem "gemnasium-gitlab-service", "~> 0.2"
134 134
  135 +# Slack integration
  136 +gem "slack-notifier", "~> 0.2.0"
  137 +
135 # d3 138 # d3
136 gem "d3_rails", "~> 3.1.4" 139 gem "d3_rails", "~> 3.1.4"
137 140
@@ -162,8 +165,9 @@ gem "modernizr", "2.6.2" @@ -162,8 +165,9 @@ gem "modernizr", "2.6.2"
162 gem "raphael-rails", "~> 2.1.2" 165 gem "raphael-rails", "~> 2.1.2"
163 gem 'bootstrap-sass', '~> 3.0' 166 gem 'bootstrap-sass', '~> 3.0'
164 gem "font-awesome-rails", '~> 3.2' 167 gem "font-awesome-rails", '~> 3.2'
165 -gem "gemoji", "~> 1.3.0" 168 +gem "gitlab_emoji", "~> 0.0.1.1"
166 gem "gon", '~> 5.0.0' 169 gem "gon", '~> 5.0.0'
  170 +gem 'nprogress-rails'
167 171
168 group :development do 172 group :development do
169 gem "annotate", "~> 2.6.0.beta2" 173 gem "annotate", "~> 2.6.0.beta2"
@@ -214,7 +218,6 @@ group :development, :test do @@ -214,7 +218,6 @@ group :development, :test do
214 # PhantomJS driver for Capybara 218 # PhantomJS driver for Capybara
215 gem 'poltergeist', '~> 1.4.1' 219 gem 'poltergeist', '~> 1.4.1'
216 220
217 - gem 'spork', '~> 1.0rc'  
218 gem 'jasmine', '2.0.0.rc5' 221 gem 'jasmine', '2.0.0.rc5'
219 222
220 gem "spring", '1.1.1' 223 gem "spring", '1.1.1'
@@ -128,6 +128,8 @@ GEM @@ -128,6 +128,8 @@ GEM
128 mail (~> 2.2) 128 mail (~> 2.2)
129 email_validator (1.4.0) 129 email_validator (1.4.0)
130 activemodel 130 activemodel
  131 + emoji (1.0.1)
  132 + json
131 enumerize (0.7.0) 133 enumerize (0.7.0)
132 activesupport (>= 3.2) 134 activesupport (>= 3.2)
133 equalizer (0.0.8) 135 equalizer (0.0.8)
@@ -165,7 +167,6 @@ GEM @@ -165,7 +167,6 @@ GEM
165 formatador (0.2.4) 167 formatador (0.2.4)
166 gemnasium-gitlab-service (0.2.1) 168 gemnasium-gitlab-service (0.2.1)
167 rugged (~> 0.19) 169 rugged (~> 0.19)
168 - gemoji (1.3.1)  
169 gherkin-ruby (0.3.1) 170 gherkin-ruby (0.3.1)
170 racc 171 racc
171 github-markdown (0.5.5) 172 github-markdown (0.5.5)
@@ -190,6 +191,8 @@ GEM @@ -190,6 +191,8 @@ GEM
190 charlock_holmes (~> 0.6.6) 191 charlock_holmes (~> 0.6.6)
191 escape_utils (~> 0.2.4) 192 escape_utils (~> 0.2.4)
192 mime-types (~> 1.19) 193 mime-types (~> 1.19)
  194 + gitlab_emoji (0.0.1.1)
  195 + emoji (~> 1.0.1)
193 gitlab_git (5.7.1) 196 gitlab_git (5.7.1)
194 activesupport (~> 4.0.0) 197 activesupport (~> 4.0.0)
195 charlock_holmes (~> 0.6.9) 198 charlock_holmes (~> 0.6.9)
@@ -297,6 +300,7 @@ GEM @@ -297,6 +300,7 @@ GEM
297 net-ssh (>= 1.99.1) 300 net-ssh (>= 1.99.1)
298 net-ssh (2.7.0) 301 net-ssh (2.7.0)
299 nokogiri (1.5.10) 302 nokogiri (1.5.10)
  303 + nprogress-rails (0.1.2.3)
300 oauth (0.4.7) 304 oauth (0.4.7)
301 oauth2 (0.8.1) 305 oauth2 (0.8.1)
302 faraday (~> 0.8) 306 faraday (~> 0.8)
@@ -468,6 +472,7 @@ GEM @@ -468,6 +472,7 @@ GEM
468 rack-protection (~> 1.4) 472 rack-protection (~> 1.4)
469 tilt (~> 1.3, >= 1.3.4) 473 tilt (~> 1.3, >= 1.3.4)
470 six (0.2.0) 474 six (0.2.0)
  475 + slack-notifier (0.2.0)
471 slim (2.0.2) 476 slim (2.0.2)
472 temple (~> 0.6.6) 477 temple (~> 0.6.6)
473 tilt (>= 1.3.3, < 2.1) 478 tilt (>= 1.3.3, < 2.1)
@@ -479,7 +484,6 @@ GEM @@ -479,7 +484,6 @@ GEM
479 capybara (>= 2.0.0) 484 capybara (>= 2.0.0)
480 railties (>= 3) 485 railties (>= 3)
481 spinach (>= 0.4) 486 spinach (>= 0.4)
482 - spork (1.0.0rc4)  
483 spring (1.1.1) 487 spring (1.1.1)
484 spring-commands-rspec (1.0.1) 488 spring-commands-rspec (1.0.1)
485 spring (>= 0.9.1) 489 spring (>= 0.9.1)
@@ -591,12 +595,12 @@ DEPENDENCIES @@ -591,12 +595,12 @@ DEPENDENCIES
591 font-awesome-rails (~> 3.2) 595 font-awesome-rails (~> 3.2)
592 foreman 596 foreman
593 gemnasium-gitlab-service (~> 0.2) 597 gemnasium-gitlab-service (~> 0.2)
594 - gemoji (~> 1.3.0)  
595 github-markup (~> 0.7.4)! 598 github-markup (~> 0.7.4)!
596 gitlab-flowdock-git-hook (~> 0.4.2) 599 gitlab-flowdock-git-hook (~> 0.4.2)
597 gitlab-gollum-lib (~> 1.1.0) 600 gitlab-gollum-lib (~> 1.1.0)
598 gitlab-grack (~> 2.0.0.pre) 601 gitlab-grack (~> 2.0.0.pre)
599 gitlab-linguist (~> 3.0.0) 602 gitlab-linguist (~> 3.0.0)
  603 + gitlab_emoji (~> 0.0.1.1)
600 gitlab_git (~> 5.7.1) 604 gitlab_git (~> 5.7.1)
601 gitlab_meta (= 6.0) 605 gitlab_meta (= 6.0)
602 gitlab_omniauth-ldap (= 1.0.4) 606 gitlab_omniauth-ldap (= 1.0.4)
@@ -620,6 +624,7 @@ DEPENDENCIES @@ -620,6 +624,7 @@ DEPENDENCIES
620 minitest (~> 4.7.0) 624 minitest (~> 4.7.0)
621 modernizr (= 2.6.2) 625 modernizr (= 2.6.2)
622 mysql2 626 mysql2
  627 + nprogress-rails
623 omniauth (~> 1.1.3) 628 omniauth (~> 1.1.3)
624 omniauth-github 629 omniauth-github
625 omniauth-google-oauth2 630 omniauth-google-oauth2
@@ -652,9 +657,9 @@ DEPENDENCIES @@ -652,9 +657,9 @@ DEPENDENCIES
652 simplecov 657 simplecov
653 sinatra 658 sinatra
654 six 659 six
  660 + slack-notifier (~> 0.2.0)
655 slim 661 slim
656 spinach-rails 662 spinach-rails
657 - spork (~> 1.0rc)  
658 spring (= 1.1.1) 663 spring (= 1.1.1)
659 spring-commands-rspec (= 1.0.1) 664 spring-commands-rspec (= 1.0.1)
660 spring-commands-spinach (= 1.0.0) 665 spring-commands-spinach (= 1.0.0)
@@ -24,8 +24,6 @@ Below we describe the contributing process to GitLab for two reasons. So that co @@ -24,8 +24,6 @@ Below we describe the contributing process to GitLab for two reasons. So that co
24 - Monitors for new merge requests (at least once a week) 24 - Monitors for new merge requests (at least once a week)
25 - Manages their work queue by looking at issues and merge requests assigned to them 25 - Manages their work queue by looking at issues and merge requests assigned to them
26 - Close fixed issues (via commit messages or manually) 26 - Close fixed issues (via commit messages or manually)
27 -- Codes [new features](http://feedback.gitlab.com/forums/176466-general/filters/top)!  
28 -- Response guidelines  
29 - Be kind to people trying to contribute. Be aware that people can be a non-native or a native English speaker, they might not understand thing or they might be very sensitive to how your word things. Use emoji to express your feelings (heart, star, smile, etc.). Some good tips about giving feedback to merge requests is in the [Thoughtbot code review guide](https://github.com/thoughtbot/guides/tree/master/code-review). 27 - Be kind to people trying to contribute. Be aware that people can be a non-native or a native English speaker, they might not understand thing or they might be very sensitive to how your word things. Use emoji to express your feelings (heart, star, smile, etc.). Some good tips about giving feedback to merge requests is in the [Thoughtbot code review guide](https://github.com/thoughtbot/guides/tree/master/code-review).
30 28
31 ## Priorities of the issue team 29 ## Priorities of the issue team
@@ -73,7 +71,7 @@ Thanks for the issue report. Please reformat your issue to conform to the issue @@ -73,7 +71,7 @@ Thanks for the issue report. Please reformat your issue to conform to the issue
73 71
74 ### Feature requests 72 ### Feature requests
75 73
76 -Thanks for your interest in GitLab. We don't use the issue tracker for feature requests. Please use http://feedback.gitlab.com/ for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information. 74 +Thank you for your interest in improving GitLab. We don't use the issue tracker for feature requests. Things that are wrong but are not a regression compared to older versions of GitLab are considered feature requests and not issues. Please the [feature request forum](http://feedback.gitlab.com/) for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information.
77 75
78 ### Issue report for old version 76 ### Issue report for old version
79 77
1 -web: bundle exec unicorn_rails -p $PORT -E development 1 +web: bundle exec unicorn_rails -p $PORT -E development -c config/unicorn_development.rb
2 worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell 2 worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell
@@ -71,7 +71,7 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl @@ -71,7 +71,7 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl
71 71
72 It is recommended to follow a monthly upgrade schedule. Security releases come out when needed. For more information about the release process see the documentation for [monthly](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/release/monthly.md) and [security](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/release/security.md) releases. 72 It is recommended to follow a monthly upgrade schedule. Security releases come out when needed. For more information about the release process see the documentation for [monthly](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/release/monthly.md) and [security](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/release/security.md) releases.
73 73
74 -* Features that will be in the next releases are listed on [the feedback and suggestions forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). 74 +* Features that will be in the next releases are listed on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457).
75 75
76 ### Run in production mode 76 ### Run in production mode
77 77
@@ -96,14 +96,9 @@ or start each component separately @@ -96,14 +96,9 @@ or start each component separately
96 96
97 ### Run the tests 97 ### Run the tests
98 98
99 -* Seed the database  
100 -  
101 - bundle exec rake db:setup RAILS_ENV=test  
102 - bundle exec rake db:seed_fu RAILS_ENV=test  
103 -  
104 * Run all tests 99 * Run all tests
105 100
106 - bundle exec rake gitlab:test RAILS_ENV=test 101 + bundle exec rake test
107 102
108 * [RSpec](http://rspec.info/) unit and functional tests 103 * [RSpec](http://rspec.info/) unit and functional tests
109 104
@@ -134,22 +129,4 @@ or start each component separately @@ -134,22 +129,4 @@ or start each component separately
134 129
135 ### Getting help 130 ### Getting help
136 131
137 -* [Maintenance policy](MAINTENANCE.md) specifies what versions are supported.  
138 -  
139 -* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) contains solutions to common problems.  
140 -  
141 -* [Mailing list](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and has resolved it. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix.  
142 -  
143 -* [Feedback and suggestions forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab.  
144 -  
145 -* [Contributing guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) describes how to submit merge requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed.  
146 -  
147 -* [Support subscription](http://www.gitlab.com/subscription/) connects you to the knowledge of GitLab experts that will resolve your issues and answer your questions.  
148 -  
149 -* [Consultancy](http://www.gitlab.com/consultancy/) from the GitLab experts for installations, upgrades and customizations.  
150 -  
151 -* [#gitlab IRC channel](http://www.freenode.net/) on Freenode to get in touch with other GitLab users and get help, it's managed by James Newton (newton), Drew Blessing (dblessing), and Sam Gleske (sag47).  
152 -  
153 -* [Book](http://www.packtpub.com/gitlab-repository-management/book) written by GitLab enthusiast Jonathan M. Hethey is unofficial but it offers a good overview.  
154 -  
155 -* [Gitter chat room](https://gitter.im/gitlabhq/gitlabhq#) here you can ask questions when you need help. 132 +Please see [Getting help for GitLab](https://www.gitlab.com/getting-help/) on our website for the many options to get help.
1 -6.7.0.pre 1 +6.8.0.pre
app/assets/javascripts/application.js
@@ -1,31 +0,0 @@ @@ -1,31 +0,0 @@
1 -// This is a manifest file that'll be compiled into including all the files listed below.  
2 -// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically  
3 -// be included in the compiled file accessible from http://example.com/assets/application.js  
4 -// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the  
5 -// the compiled file.  
6 -//  
7 -//= require jquery  
8 -//= require jquery.ui.all  
9 -//= require jquery_ujs  
10 -//= require jquery.cookie  
11 -//= require jquery.endless-scroll  
12 -//= require jquery.highlight  
13 -//= require jquery.history  
14 -//= require jquery.waitforimages  
15 -//= require jquery.atwho  
16 -//= require jquery.scrollto  
17 -//= require jquery.blockUI  
18 -//= require turbolinks  
19 -//= require jquery.turbolinks  
20 -//= require bootstrap  
21 -//= require modernizr  
22 -//= require select2  
23 -//= require raphael  
24 -//= require g.raphael-min  
25 -//= require g.bar-min  
26 -//= require branch-graph  
27 -//= require highlightjs.min  
28 -//= require ace/ace  
29 -//= require_tree .  
30 -//= require d3  
31 -//= require underscore  
app/assets/javascripts/application.js.coffee 0 → 100644
@@ -0,0 +1,154 @@ @@ -0,0 +1,154 @@
  1 +# This is a manifest file that'll be compiled into including all the files listed below.
  2 +# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
  3 +# be included in the compiled file accessible from http://example.com/assets/application.js
  4 +# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
  5 +# the compiled file.
  6 +#
  7 +#= require jquery
  8 +#= require jquery.ui.all
  9 +#= require jquery_ujs
  10 +#= require jquery.cookie
  11 +#= require jquery.endless-scroll
  12 +#= require jquery.highlight
  13 +#= require jquery.history
  14 +#= require jquery.waitforimages
  15 +#= require jquery.atwho
  16 +#= require jquery.scrollto
  17 +#= require jquery.blockUI
  18 +#= require turbolinks
  19 +#= require jquery.turbolinks
  20 +#= require bootstrap
  21 +#= require modernizr
  22 +#= require select2
  23 +#= require raphael
  24 +#= require g.raphael-min
  25 +#= require g.bar-min
  26 +#= require branch-graph
  27 +#= require highlightjs.min
  28 +#= require ace/ace
  29 +#= require d3
  30 +#= require underscore
  31 +#= require nprogress
  32 +#= require nprogress-turbolinks
  33 +#= require_tree .
  34 +
  35 +window.slugify = (text) ->
  36 + text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
  37 +
  38 +window.ajaxGet = (url) ->
  39 + $.ajax({type: "GET", url: url, dataType: "script"})
  40 +
  41 +window.showAndHide = (selector) ->
  42 +
  43 +window.errorMessage = (message) ->
  44 + ehtml = $("<p>")
  45 + ehtml.addClass("error_message")
  46 + ehtml.html(message)
  47 + ehtml
  48 +
  49 +window.split = (val) ->
  50 + return val.split( /,\s*/ )
  51 +
  52 +window.extractLast = (term) ->
  53 + return split( term ).pop()
  54 +
  55 +# Disable button if text field is empty
  56 +window.disableButtonIfEmptyField = (field_selector, button_selector) ->
  57 + field = $(field_selector)
  58 + closest_submit = field.closest("form").find(button_selector)
  59 +
  60 + closest_submit.disable() if field.val() is ""
  61 +
  62 + field.on "input", ->
  63 + if $(@).val() is ""
  64 + closest_submit.disable()
  65 + else
  66 + closest_submit.enable()
  67 +
  68 +window.sanitize = (str) ->
  69 + return str.replace(/<(?:.|\n)*?>/gm, '')
  70 +
  71 +window.linkify = (str) ->
  72 + exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig
  73 + return str.replace(exp,"<a href='$1'>$1</a>")
  74 +
  75 +window.simpleFormat = (str) ->
  76 + linkify(sanitize(str).replace(/\n/g, '<br />'))
  77 +
  78 +window.unbindEvents = ->
  79 + $(document).unbind('scroll')
  80 + $(document).off('scroll')
  81 +
  82 +document.addEventListener("page:fetch", unbindEvents)
  83 +
  84 +$ ->
  85 + # Click a .one_click_select field, select the contents
  86 + $(".one_click_select").on 'click', -> $(@).select()
  87 +
  88 + $('.remove-row').bind 'ajax:success', ->
  89 + $(this).closest('li').fadeOut()
  90 +
  91 + # Initialize select2 selects
  92 + $('select.select2').select2(width: 'resolve', dropdownAutoWidth: true)
  93 +
  94 + # Initialize tooltips
  95 + $('.has_tooltip').tooltip()
  96 +
  97 + # Bottom tooltip
  98 + $('.has_bottom_tooltip').tooltip(placement: 'bottom')
  99 +
  100 + # Form submitter
  101 + $('.trigger-submit').on 'change', ->
  102 + $(@).parents('form').submit()
  103 +
  104 + $("abbr.timeago").timeago()
  105 + $('.js-timeago').timeago()
  106 +
  107 + # Flash
  108 + if (flash = $(".flash-container")).length > 0
  109 + flash.click -> $(@).fadeOut()
  110 + flash.show()
  111 + setTimeout (-> flash.fadeOut()), 5000
  112 +
  113 + # Disable form buttons while a form is submitting
  114 + $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
  115 + buttons = $('[type="submit"]', @)
  116 +
  117 + switch e.type
  118 + when 'ajax:beforeSend', 'submit'
  119 + buttons.disable()
  120 + else
  121 + buttons.enable()
  122 +
  123 + # Show/Hide the profile menu when hovering the account box
  124 + $('.account-box').hover -> $(@).toggleClass('hover')
  125 +
  126 + # Focus search field by pressing 's' key
  127 + $(document).keypress (e) ->
  128 + # Don't do anything if typing in an input
  129 + return if $(e.target).is(":input")
  130 +
  131 + switch e.which
  132 + when 115
  133 + $("#search").focus()
  134 + e.preventDefault()
  135 + when 63
  136 + new Shortcuts()
  137 + e.preventDefault()
  138 +
  139 +
  140 + # Commit show suppressed diff
  141 + $(".diff-content").on "click", ".supp_diff_link", ->
  142 + $(@).next('table').show()
  143 + $(@).remove()
  144 +
  145 +(($) ->
  146 + # Disable an element and add the 'disabled' Bootstrap class
  147 + $.fn.extend disable: ->
  148 + $(@).attr('disabled', 'disabled').addClass('disabled')
  149 +
  150 + # Enable an element and remove the 'disabled' Bootstrap class
  151 + $.fn.extend enable: ->
  152 + $(@).removeAttr('disabled').removeClass('disabled')
  153 +
  154 +)(jQuery)
app/assets/javascripts/gfm_auto_complete.js.coffee
@@ -6,7 +6,6 @@ GitLab.GfmAutoComplete = @@ -6,7 +6,6 @@ GitLab.GfmAutoComplete =
6 dataSource: '' 6 dataSource: ''
7 # Emoji 7 # Emoji
8 Emoji: 8 Emoji:
9 - assetBase: ''  
10 template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>' 9 template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>'
11 10
12 # Team Members 11 # Team Members
@@ -27,7 +26,7 @@ GitLab.GfmAutoComplete = @@ -27,7 +26,7 @@ GitLab.GfmAutoComplete =
27 tpl: @Emoji.template 26 tpl: @Emoji.template
28 callbacks: 27 callbacks:
29 before_save: (emojis) => 28 before_save: (emojis) =>
30 - $.map emojis, (em) => name: em, insert: em+ ':', image: "#{@Emoji.assetBase}/#{em}.png" 29 + $.map emojis, (em) => name: em.name, insert: em.name+ ':', image: em.path
31 30
32 # Team Members 31 # Team Members
33 input.atwho 32 input.atwho
app/assets/javascripts/main.js.coffee
@@ -1,128 +0,0 @@ @@ -1,128 +0,0 @@
1 -window.slugify = (text) ->  
2 - text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()  
3 -  
4 -window.ajaxGet = (url) ->  
5 - $.ajax({type: "GET", url: url, dataType: "script"})  
6 -  
7 -window.showAndHide = (selector) ->  
8 -  
9 -window.errorMessage = (message) ->  
10 - ehtml = $("<p>")  
11 - ehtml.addClass("error_message")  
12 - ehtml.html(message)  
13 - ehtml  
14 -  
15 -window.split = (val) ->  
16 - return val.split( /,\s*/ )  
17 -  
18 -window.extractLast = (term) ->  
19 - return split( term ).pop()  
20 -  
21 -# Disable button if text field is empty  
22 -window.disableButtonIfEmptyField = (field_selector, button_selector) ->  
23 - field = $(field_selector)  
24 - closest_submit = field.closest("form").find(button_selector)  
25 -  
26 - closest_submit.disable() if field.val() is ""  
27 -  
28 - field.on "input", ->  
29 - if $(@).val() is ""  
30 - closest_submit.disable()  
31 - else  
32 - closest_submit.enable()  
33 -  
34 -window.sanitize = (str) ->  
35 - return str.replace(/<(?:.|\n)*?>/gm, '')  
36 -  
37 -window.linkify = (str) ->  
38 - exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig  
39 - return str.replace(exp,"<a href='$1'>$1</a>")  
40 -  
41 -window.simpleFormat = (str) ->  
42 - linkify(sanitize(str).replace(/\n/g, '<br />'))  
43 -  
44 -window.startSpinner = ->  
45 - $('.turbolink-spinner').fadeIn()  
46 -  
47 -window.stopSpinner = ->  
48 - $('.turbolink-spinner').fadeOut()  
49 -  
50 -window.unbindEvents = ->  
51 - $(document).unbind('scroll')  
52 - $(document).off('scroll')  
53 -  
54 -document.addEventListener("page:fetch", startSpinner)  
55 -document.addEventListener("page:fetch", unbindEvents)  
56 -document.addEventListener("page:change", stopSpinner)  
57 -  
58 -$ ->  
59 - # Click a .one_click_select field, select the contents  
60 - $(".one_click_select").on 'click', -> $(@).select()  
61 -  
62 - $('.remove-row').bind 'ajax:success', ->  
63 - $(this).closest('li').fadeOut()  
64 -  
65 - # Initialize select2 selects  
66 - $('select.select2').select2(width: 'resolve', dropdownAutoWidth: true)  
67 -  
68 - # Initialize tooltips  
69 - $('.has_tooltip').tooltip()  
70 -  
71 - # Bottom tooltip  
72 - $('.has_bottom_tooltip').tooltip(placement: 'bottom')  
73 -  
74 - # Form submitter  
75 - $('.trigger-submit').on 'change', ->  
76 - $(@).parents('form').submit()  
77 -  
78 - $("abbr.timeago").timeago()  
79 - $('.js-timeago').timeago()  
80 -  
81 - # Flash  
82 - if (flash = $(".flash-container")).length > 0  
83 - flash.click -> $(@).fadeOut()  
84 - flash.show()  
85 - setTimeout (-> flash.fadeOut()), 5000  
86 -  
87 - # Disable form buttons while a form is submitting  
88 - $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->  
89 - buttons = $('[type="submit"]', @)  
90 -  
91 - switch e.type  
92 - when 'ajax:beforeSend', 'submit'  
93 - buttons.disable()  
94 - else  
95 - buttons.enable()  
96 -  
97 - # Show/Hide the profile menu when hovering the account box  
98 - $('.account-box').hover -> $(@).toggleClass('hover')  
99 -  
100 - # Focus search field by pressing 's' key  
101 - $(document).keypress (e) ->  
102 - # Don't do anything if typing in an input  
103 - return if $(e.target).is(":input")  
104 -  
105 - switch e.which  
106 - when 115  
107 - $("#search").focus()  
108 - e.preventDefault()  
109 - when 63  
110 - new Shortcuts()  
111 - e.preventDefault()  
112 -  
113 -  
114 - # Commit show suppressed diff  
115 - $(".diff-content").on "click", ".supp_diff_link", ->  
116 - $(@).next('table').show()  
117 - $(@).remove()  
118 -  
119 -(($) ->  
120 - # Disable an element and add the 'disabled' Bootstrap class  
121 - $.fn.extend disable: ->  
122 - $(@).attr('disabled', 'disabled').addClass('disabled')  
123 -  
124 - # Enable an element and remove the 'disabled' Bootstrap class  
125 - $.fn.extend enable: ->  
126 - $(@).removeAttr('disabled').removeClass('disabled')  
127 -  
128 -)(jQuery)  
app/assets/stylesheets/application.scss
@@ -7,6 +7,8 @@ @@ -7,6 +7,8 @@
7 *= require select2 7 *= require select2
8 *= require highlightjs.min 8 *= require highlightjs.min
9 *= require_self 9 *= require_self
  10 + *= require nprogress
  11 + *= require nprogress-bootstrap
10 */ 12 */
11 13
12 @import "main/variables.scss"; 14 @import "main/variables.scss";
app/assets/stylesheets/generic/common.scss
@@ -355,3 +355,7 @@ table { @@ -355,3 +355,7 @@ table {
355 @media (max-width: $screen-xs-max) { 355 @media (max-width: $screen-xs-max) {
356 .container .content { margin-top: 20px; } 356 .container .content { margin-top: 20px; }
357 } 357 }
  358 +
  359 +.wiki .highlight, .note-body .highlight {
  360 + margin-bottom: 9px;
  361 +}
app/assets/stylesheets/generic/typography.scss
@@ -88,9 +88,6 @@ a:focus { @@ -88,9 +88,6 @@ a:focus {
88 .wiki { 88 .wiki {
89 @include md-typography; 89 @include md-typography;
90 90
91 - font-size: 14px;  
92 - line-height: 1.6;  
93 -  
94 /* Link to current header. */ 91 /* Link to current header. */
95 h1, h2, h3, h4, h5, h6 { 92 h1, h2, h3, h4, h5, h6 {
96 position: relative; 93 position: relative;
@@ -120,3 +117,11 @@ a:focus { @@ -120,3 +117,11 @@ a:focus {
120 .md { 117 .md {
121 @include md-typography; 118 @include md-typography;
122 } 119 }
  120 +
  121 +/**
  122 + * Textareas intended for GFM
  123 + *
  124 + */
  125 +textarea.js-gfm-input {
  126 + font-family: $monospace_font;
  127 +}
app/assets/stylesheets/main/mixins.scss
@@ -84,6 +84,9 @@ @@ -84,6 +84,9 @@
84 } 84 }
85 85
86 @mixin md-typography { 86 @mixin md-typography {
  87 + font-size: 14px;
  88 + line-height: 1.6;
  89 +
87 img { 90 img {
88 max-width: 100%; 91 max-width: 100%;
89 } 92 }
app/assets/stylesheets/print.scss 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +/* Generic print styles */
  2 +header, nav, nav.main-nav, nav.navbar-collapse, nav.navbar-collapse.collapse {display: none!important;}
  3 +.profiler-results {display: none;}
  4 +
  5 +/* Styles targeted specifically at printing files */
  6 +.tree-ref-holder, .tree-holder .breadcrumb, .blob-commit-info {display: none;}
  7 +.file-title {display: none;}
  8 +.file-holder {border: none;}
  9 +
  10 +.wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; }
  11 +.wiki h1 {font-size: 30px;}
  12 +.wiki h2 {font-size: 22px;}
  13 +.wiki h3 {font-size: 18px; font-weight: bold; }
app/assets/stylesheets/sections/dashboard.scss
@@ -61,7 +61,7 @@ @@ -61,7 +61,7 @@
61 } 61 }
62 62
63 .project-row, .group-row { 63 .project-row, .group-row {
64 - padding: 10px 12px !important; 64 + padding: 8px 12px !important;
65 font-size: 14px; 65 font-size: 14px;
66 line-height: 24px; 66 line-height: 24px;
67 67
app/assets/stylesheets/sections/diff.scss
@@ -62,6 +62,29 @@ @@ -62,6 +62,29 @@
62 font-size: 12px; 62 font-size: 12px;
63 } 63 }
64 } 64 }
  65 +
  66 + .text-file-parallel div {
  67 + display: inline-block;
  68 + padding-bottom: 16px;
  69 + }
  70 + .diff-side {
  71 + overflow-x: scroll;
  72 + width: 508px;
  73 + height: 700px;
  74 + }
  75 + .diff-side.diff-side-left{
  76 + overflow-y:hidden;
  77 + }
  78 + .diff-side table, td.diff-middle table {
  79 + height: 700px;
  80 + }
  81 + .diff-middle {
  82 + width: 114px;
  83 + vertical-align: top;
  84 + height: 700px;
  85 + overflow: hidden
  86 + }
  87 +
65 .old_line, .new_line, .diff_line { 88 .old_line, .new_line, .diff_line {
66 margin: 0px; 89 margin: 0px;
67 padding: 0px; 90 padding: 0px;
@@ -125,8 +148,6 @@ @@ -125,8 +148,6 @@
125 } 148 }
126 &.parallel { 149 &.parallel {
127 display: table-cell; 150 display: table-cell;
128 - overflow: hidden;  
129 - width: 50%;  
130 } 151 }
131 } 152 }
132 } 153 }
app/assets/stylesheets/sections/events.scss
@@ -42,7 +42,7 @@ @@ -42,7 +42,7 @@
42 } 42 }
43 } 43 }
44 44
45 - padding: 14px 0px; 45 + padding: 12px 0px;
46 border-bottom: 1px solid #eee; 46 border-bottom: 1px solid #eee;
47 .event-title { 47 .event-title {
48 color: #333; 48 color: #333;
@@ -63,6 +63,10 @@ @@ -63,6 +63,10 @@
63 color: #666; 63 color: #666;
64 margin-top: 5px; 64 margin-top: 5px;
65 65
  66 + .md {
  67 + font-size: 13px;
  68 + }
  69 +
66 pre { 70 pre {
67 border: none; 71 border: none;
68 background: #f9f9f9; 72 background: #f9f9f9;
app/assets/stylesheets/sections/header.scss
@@ -273,3 +273,9 @@ header { @@ -273,3 +273,9 @@ header {
273 } 273 }
274 } 274 }
275 } 275 }
  276 +
  277 +@media (max-width: $screen-xs-max) {
  278 + #nprogress .spinner {
  279 + right: 35px !important;
  280 + }
  281 +}
app/assets/stylesheets/sections/notes.scss
@@ -27,11 +27,15 @@ ul.notes { @@ -27,11 +27,15 @@ ul.notes {
27 27
28 .discussion-last-update, 28 .discussion-last-update,
29 .note-last-update { 29 .note-last-update {
30 - font-style: italic; 30 + &:before {
  31 + content: "\00b7";
  32 + }
  33 + font-size: 13px;
31 } 34 }
32 .author { 35 .author {
33 - color: $style_color; 36 + color: #555;
34 font-weight: bold; 37 font-weight: bold;
  38 + font-size: 14px;
35 &:hover { 39 &:hover {
36 color: $primary_color; 40 color: $primary_color;
37 } 41 }
app/controllers/passwords_controller.rb 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +class PasswordsController < Devise::PasswordsController
  2 +
  3 + def create
  4 + email = resource_params[:email]
  5 + resource_found = resource_class.find_by_email(email)
  6 + if resource_found && resource_found.ldap_user?
  7 + flash[:alert] = "Cannot reset password for LDAP user."
  8 + respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name)) and return
  9 + end
  10 +
  11 + self.resource = resource_class.send_reset_password_instructions(resource_params)
  12 + if successfully_sent?(resource)
  13 + respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name))
  14 + else
  15 + respond_with(resource)
  16 + end
  17 + end
  18 +end
app/controllers/profiles/keys_controller.rb
@@ -41,7 +41,7 @@ class Profiles::KeysController &lt; ApplicationController @@ -41,7 +41,7 @@ class Profiles::KeysController &lt; ApplicationController
41 begin 41 begin
42 user = User.find_by_username(params[:username]) 42 user = User.find_by_username(params[:username])
43 if user.present? 43 if user.present?
44 - render text: user.all_ssh_keys.join("\n") 44 + render text: user.all_ssh_keys.join("\n"), content_type: "text/plain"
45 else 45 else
46 render_404 and return 46 render_404 and return
47 end 47 end
app/controllers/projects/issues_controller.rb
@@ -76,7 +76,7 @@ class Projects::IssuesController &lt; Projects::ApplicationController @@ -76,7 +76,7 @@ class Projects::IssuesController &lt; Projects::ApplicationController
76 end 76 end
77 77
78 def update 78 def update
79 - @issue.update_attributes(params[:issue].merge(author_id_of_changes: current_user.id)) 79 + @issue.update_attributes(params[:issue])
80 @issue.reset_events_cache 80 @issue.reset_events_cache
81 81
82 respond_to do |format| 82 respond_to do |format|
app/controllers/projects/merge_requests_controller.rb
@@ -109,7 +109,7 @@ class Projects::MergeRequestsController &lt; Projects::ApplicationController @@ -109,7 +109,7 @@ class Projects::MergeRequestsController &lt; Projects::ApplicationController
109 params[:merge_request].delete(:source_project_id) 109 params[:merge_request].delete(:source_project_id)
110 params[:merge_request].delete(:target_project_id) 110 params[:merge_request].delete(:target_project_id)
111 111
112 - if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id)) 112 + if @merge_request.update_attributes(params[:merge_request])
113 @merge_request.reset_events_cache 113 @merge_request.reset_events_cache
114 114
115 respond_to do |format| 115 respond_to do |format|
app/controllers/projects/milestones_controller.rb
@@ -38,7 +38,6 @@ class Projects::MilestonesController &lt; Projects::ApplicationController @@ -38,7 +38,6 @@ class Projects::MilestonesController &lt; Projects::ApplicationController
38 38
39 def create 39 def create
40 @milestone = @project.milestones.new(params[:milestone]) 40 @milestone = @project.milestones.new(params[:milestone])
41 - @milestone.author_id_of_changes = current_user.id  
42 41
43 if @milestone.save 42 if @milestone.save
44 redirect_to project_milestone_path(@project, @milestone) 43 redirect_to project_milestone_path(@project, @milestone)
@@ -48,7 +47,7 @@ class Projects::MilestonesController &lt; Projects::ApplicationController @@ -48,7 +47,7 @@ class Projects::MilestonesController &lt; Projects::ApplicationController
48 end 47 end
49 48
50 def update 49 def update
51 - @milestone.update_attributes(params[:milestone].merge(author_id_of_changes: current_user.id)) 50 + @milestone.update_attributes(params[:milestone])
52 51
53 respond_to do |format| 52 respond_to do |format|
54 format.js 53 format.js
app/controllers/projects_controller.rb
@@ -123,11 +123,20 @@ class ProjectsController &lt; ApplicationController @@ -123,11 +123,20 @@ class ProjectsController &lt; ApplicationController
123 end 123 end
124 124
125 def autocomplete_sources 125 def autocomplete_sources
  126 + note_type = params['type']
  127 + note_id = params['type_id']
  128 + participating = if note_type && note_id
  129 + participants_in(note_type, note_id)
  130 + else
  131 + []
  132 + end
  133 + team_members = sorted(@project.team.members)
  134 + participants = team_members + participating
126 @suggestions = { 135 @suggestions = {
127 - emojis: Emoji.names, 136 + emojis: Emoji.names.map { |e| { name: e, path: view_context.image_url("emoji/#{e}.png") } },
128 issues: @project.issues.select([:iid, :title, :description]), 137 issues: @project.issues.select([:iid, :title, :description]),
129 mergerequests: @project.merge_requests.select([:iid, :title, :description]), 138 mergerequests: @project.merge_requests.select([:iid, :title, :description]),
130 - members: @project.team.members.sort_by(&:username).map { |user| { username: user.username, name: user.name } } 139 + members: participants.uniq
131 } 140 }
132 141
133 respond_to do |format| 142 respond_to do |format|
@@ -162,4 +171,25 @@ class ProjectsController &lt; ApplicationController @@ -162,4 +171,25 @@ class ProjectsController &lt; ApplicationController
162 def user_layout 171 def user_layout
163 current_user ? "projects" : "public_projects" 172 current_user ? "projects" : "public_projects"
164 end 173 end
  174 +
  175 + def participants_in(type, id)
  176 + users = case type
  177 + when "Issue"
  178 + issue = @project.issues.find_by_iid(id)
  179 + issue ? issue.participants : []
  180 + when "MergeRequest"
  181 + merge_request = @project.merge_requests.find_by_iid(id)
  182 + merge_request ? merge_request.participants : []
  183 + when "Commit"
  184 + author_ids = Note.for_commit_id(id).pluck(:author_id).uniq
  185 + User.where(id: author_ids)
  186 + else
  187 + []
  188 + end
  189 + sorted(users)
  190 + end
  191 +
  192 + def sorted(users)
  193 + users.uniq.sort_by(&:username).map { |user| { username: user.username, name: user.name } }
  194 + end
165 end 195 end
app/finders/base_finder.rb
@@ -47,9 +47,9 @@ class BaseFinder @@ -47,9 +47,9 @@ class BaseFinder
47 [] 47 []
48 end 48 end
49 elsif current_user && params[:authorized_only].presence 49 elsif current_user && params[:authorized_only].presence
50 - klass.of_projects(current_user.authorized_projects) 50 + klass.of_projects(current_user.authorized_projects).references(:project)
51 else 51 else
52 - klass.of_projects(Project.accessible_to(current_user)) 52 + klass.of_projects(Project.accessible_to(current_user)).references(:project)
53 end 53 end
54 end 54 end
55 55
app/helpers/commits_helper.rb
@@ -105,8 +105,80 @@ module CommitsHelper @@ -105,8 +105,80 @@ module CommitsHelper
105 branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe 105 branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
106 end 106 end
107 107
108 - def get_old_file(project, commit, diff)  
109 - project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id 108 + def parallel_diff_lines(project, commit, diff, file)
  109 + old_file = project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id
  110 + deleted_lines = {}
  111 + added_lines = {}
  112 + each_diff_line(diff, 0) do |line, type, line_code, line_new, line_old|
  113 + if type == "old"
  114 + deleted_lines[line_old] = { line_code: line_code, type: type, line: line }
  115 + elsif type == "new"
  116 + added_lines[line_new] = { line_code: line_code, type: type, line: line }
  117 + end
  118 + end
  119 + max_length = old_file ? old_file.sloc + added_lines.length : file.sloc
  120 +
  121 + offset1 = 0
  122 + offset2 = 0
  123 + old_lines = []
  124 + new_lines = []
  125 +
  126 + max_length.times do |line_index|
  127 + line_index1 = line_index - offset1
  128 + line_index2 = line_index - offset2
  129 + deleted_line = deleted_lines[line_index1 + 1]
  130 + added_line = added_lines[line_index2 + 1]
  131 + old_line = old_file.lines[line_index1] if old_file
  132 + new_line = file.lines[line_index2]
  133 +
  134 + if deleted_line && added_line
  135 + elsif deleted_line
  136 + new_line = nil
  137 + offset2 += 1
  138 + elsif added_line
  139 + old_line = nil
  140 + offset1 += 1
  141 + end
  142 +
  143 + old_lines[line_index] = DiffLine.new
  144 + new_lines[line_index] = DiffLine.new
  145 +
  146 + # old
  147 + if line_index == 0 && diff.new_file
  148 + old_lines[line_index].type = :file_created
  149 + old_lines[line_index].content = 'File was created'
  150 + elsif deleted_line
  151 + old_lines[line_index].type = :deleted
  152 + old_lines[line_index].content = old_line
  153 + old_lines[line_index].num = line_index1 + 1
  154 + old_lines[line_index].code = deleted_line[:line_code]
  155 + elsif old_line
  156 + old_lines[line_index].type = :no_change
  157 + old_lines[line_index].content = old_line
  158 + old_lines[line_index].num = line_index1 + 1
  159 + else
  160 + old_lines[line_index].type = :added
  161 + end
  162 +
  163 + # new
  164 + if line_index == 0 && diff.deleted_file
  165 + new_lines[line_index].type = :file_deleted
  166 + new_lines[line_index].content = "File was deleted"
  167 + elsif added_line
  168 + new_lines[line_index].type = :added
  169 + new_lines[line_index].num = line_index2 + 1
  170 + new_lines[line_index].content = new_line
  171 + new_lines[line_index].code = added_line[:line_code]
  172 + elsif new_line
  173 + new_lines[line_index].type = :no_change
  174 + new_lines[line_index].num = line_index2 + 1
  175 + new_lines[line_index].content = new_line
  176 + else
  177 + new_lines[line_index].type = :deleted
  178 + end
  179 + end
  180 +
  181 + return old_lines, new_lines
110 end 182 end
111 183
112 protected 184 protected
app/helpers/merge_requests_helper.rb
@@ -20,7 +20,7 @@ module MergeRequestsHelper @@ -20,7 +20,7 @@ module MergeRequestsHelper
20 target_project_id: target_project.id, 20 target_project_id: target_project.id,
21 source_branch: event.branch_name, 21 source_branch: event.branch_name,
22 target_branch: target_project.repository.root_ref, 22 target_branch: target_project.repository.root_ref,
23 - title: event.branch_name.titleize 23 + title: event.branch_name.humanize
24 } 24 }
25 end 25 end
26 26
app/helpers/tree_helper.rb
@@ -40,7 +40,7 @@ module TreeHelper @@ -40,7 +40,7 @@ module TreeHelper
40 # Returns boolean 40 # Returns boolean
41 def markup?(filename) 41 def markup?(filename)
42 filename.downcase.end_with?(*%w(.textile .rdoc .org .creole 42 filename.downcase.end_with?(*%w(.textile .rdoc .org .creole
43 - .mediawiki .rst .asciidoc .pod)) 43 + .mediawiki .rst .adoc .asciidoc .pod))
44 end 44 end
45 45
46 def gitlab_markdown?(filename) 46 def gitlab_markdown?(filename)
app/mailers/emails/merge_requests.rb
@@ -29,11 +29,11 @@ module Emails @@ -29,11 +29,11 @@ module Emails
29 subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) 29 subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
30 end 30 end
31 31
32 - def merged_merge_request_email(recipient_id, merge_request_id) 32 + def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
33 @merge_request = MergeRequest.find(merge_request_id) 33 @merge_request = MergeRequest.find(merge_request_id)
34 @project = @merge_request.project 34 @project = @merge_request.project
35 @target_url = project_merge_request_url(@project, @merge_request) 35 @target_url = project_merge_request_url(@project, @merge_request)
36 - mail(from: sender(@merge_request.author_id_of_changes), 36 + mail(from: sender(updated_by_user_id),
37 to: recipient(recipient_id), 37 to: recipient(recipient_id),
38 subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) 38 subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
39 end 39 end
app/models/concerns/issuable.rb
@@ -37,8 +37,6 @@ module Issuable @@ -37,8 +37,6 @@ module Issuable
37 allow_nil: true, 37 allow_nil: true,
38 prefix: true 38 prefix: true
39 39
40 - attr_accessor :author_id_of_changes  
41 -  
42 attr_mentionable :title, :description 40 attr_mentionable :title, :description
43 end 41 end
44 42
app/models/diff_line.rb 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +class DiffLine
  2 + attr_accessor :type, :content, :num, :code
  3 +end
app/models/event.rb
@@ -47,14 +47,6 @@ class Event &lt; ActiveRecord::Base @@ -47,14 +47,6 @@ class Event &lt; ActiveRecord::Base
47 scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent } 47 scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
48 48
49 class << self 49 class << self
50 - def determine_action(record)  
51 - if [Issue, MergeRequest].include? record.class  
52 - Event::CREATED  
53 - elsif record.kind_of? Note  
54 - Event::COMMENTED  
55 - end  
56 - end  
57 -  
58 def create_ref_event(project, user, ref, action = 'add', prefix = 'refs/heads') 50 def create_ref_event(project, user, ref, action = 'add', prefix = 'refs/heads')
59 commit = project.repository.commit(ref.target) 51 commit = project.repository.commit(ref.target)
60 52
app/models/issue.rb
@@ -30,8 +30,7 @@ class Issue &lt; ActiveRecord::Base @@ -30,8 +30,7 @@ class Issue &lt; ActiveRecord::Base
30 scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } 30 scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
31 31
32 attr_accessible :title, :assignee_id, :position, :description, 32 attr_accessible :title, :assignee_id, :position, :description,
33 - :milestone_id, :label_list, :author_id_of_changes,  
34 - :state_event 33 + :milestone_id, :label_list, :state_event
35 34
36 acts_as_taggable_on :labels 35 acts_as_taggable_on :labels
37 36
app/models/merge_request.rb
@@ -38,7 +38,7 @@ class MergeRequest &lt; ActiveRecord::Base @@ -38,7 +38,7 @@ class MergeRequest &lt; ActiveRecord::Base
38 38
39 delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil 39 delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil
40 40
41 - attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :author_id_of_changes, :state_event, :description 41 + attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :state_event, :description
42 42
43 attr_accessor :should_remove_source_branch 43 attr_accessor :should_remove_source_branch
44 44
app/models/milestone.rb
@@ -16,8 +16,7 @@ @@ -16,8 +16,7 @@
16 class Milestone < ActiveRecord::Base 16 class Milestone < ActiveRecord::Base
17 include InternalId 17 include InternalId
18 18
19 - attr_accessible :title, :description, :due_date, :state_event, :author_id_of_changes  
20 - attr_accessor :author_id_of_changes 19 + attr_accessible :title, :description, :due_date, :state_event
21 20
22 belongs_to :project 21 belongs_to :project
23 has_many :issues 22 has_many :issues
@@ -89,6 +88,6 @@ class Milestone &lt; ActiveRecord::Base @@ -89,6 +88,6 @@ class Milestone &lt; ActiveRecord::Base
89 end 88 end
90 89
91 def author_id 90 def author_id
92 - author_id_of_changes 91 + nil
93 end 92 end
94 end 93 end
app/models/note.rb
@@ -199,7 +199,8 @@ class Note &lt; ActiveRecord::Base @@ -199,7 +199,8 @@ class Note &lt; ActiveRecord::Base
199 def downvote? 199 def downvote?
200 votable? && (note.start_with?('-1') || 200 votable? && (note.start_with?('-1') ||
201 note.start_with?(':-1:') || 201 note.start_with?(':-1:') ||
202 - note.start_with?(':thumbsdown:') 202 + note.start_with?(':thumbsdown:') ||
  203 + note.start_with?(':thumbs_down_sign:')
203 ) 204 )
204 end 205 end
205 206
@@ -249,7 +250,8 @@ class Note &lt; ActiveRecord::Base @@ -249,7 +250,8 @@ class Note &lt; ActiveRecord::Base
249 def upvote? 250 def upvote?
250 votable? && (note.start_with?('+1') || 251 votable? && (note.start_with?('+1') ||
251 note.start_with?(':+1:') || 252 note.start_with?(':+1:') ||
252 - note.start_with?(':thumbsup:') 253 + note.start_with?(':thumbsup:') ||
  254 + note.start_with?(':thumbs_up_sign:')
253 ) 255 )
254 end 256 end
255 257
app/models/project.rb
@@ -56,6 +56,7 @@ class Project &lt; ActiveRecord::Base @@ -56,6 +56,7 @@ class Project &lt; ActiveRecord::Base
56 has_one :flowdock_service, dependent: :destroy 56 has_one :flowdock_service, dependent: :destroy
57 has_one :assembla_service, dependent: :destroy 57 has_one :assembla_service, dependent: :destroy
58 has_one :gemnasium_service, dependent: :destroy 58 has_one :gemnasium_service, dependent: :destroy
  59 + has_one :slack_service, dependent: :destroy
59 has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" 60 has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
60 has_one :forked_from_project, through: :forked_project_link 61 has_one :forked_from_project, through: :forked_project_link
61 # Merge Requests for target project should be removed with it 62 # Merge Requests for target project should be removed with it
@@ -304,7 +305,7 @@ class Project &lt; ActiveRecord::Base @@ -304,7 +305,7 @@ class Project &lt; ActiveRecord::Base
304 end 305 end
305 306
306 def available_services_names 307 def available_services_names
307 - %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium) 308 + %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack)
308 end 309 end
309 310
310 def gitlab_ci? 311 def gitlab_ci?
app/models/project_services/slack_message.rb 0 → 100644
@@ -0,0 +1,95 @@ @@ -0,0 +1,95 @@
  1 +require 'slack-notifier'
  2 +
  3 +class SlackMessage
  4 + def initialize(params)
  5 + @after = params.fetch(:after)
  6 + @before = params.fetch(:before)
  7 + @commits = params.fetch(:commits, [])
  8 + @project_name = params.fetch(:project_name)
  9 + @project_url = params.fetch(:project_url)
  10 + @ref = params.fetch(:ref).gsub('refs/heads/', '')
  11 + @username = params.fetch(:user_name)
  12 + end
  13 +
  14 + def compose
  15 + format(message)
  16 + end
  17 +
  18 + private
  19 +
  20 + attr_reader :after
  21 + attr_reader :before
  22 + attr_reader :commits
  23 + attr_reader :project_name
  24 + attr_reader :project_url
  25 + attr_reader :ref
  26 + attr_reader :username
  27 +
  28 + def message
  29 + if new_branch?
  30 + new_branch_message
  31 + elsif removed_branch?
  32 + removed_branch_message
  33 + else
  34 + push_message << commit_messages
  35 + end
  36 + end
  37 +
  38 + def format(string)
  39 + Slack::Notifier::LinkFormatter.format(string)
  40 + end
  41 +
  42 + def new_branch_message
  43 + "#{username} pushed new branch #{branch_link} to #{project_link}"
  44 + end
  45 +
  46 + def removed_branch_message
  47 + "#{username} removed branch #{ref} from #{project_link}"
  48 + end
  49 +
  50 + def push_message
  51 + "#{username} pushed to branch #{branch_link} of #{project_link} (#{compare_link})"
  52 + end
  53 +
  54 + def commit_messages
  55 + commits.each_with_object('') do |commit, str|
  56 + str << compose_commit_message(commit)
  57 + end
  58 + end
  59 +
  60 + def compose_commit_message(commit)
  61 + id = commit.fetch(:id)[0..5]
  62 + message = commit.fetch(:message)
  63 + url = commit.fetch(:url)
  64 +
  65 + "\n - #{message} ([#{id}](#{url}))"
  66 + end
  67 +
  68 + def new_branch?
  69 + before =~ /000000/
  70 + end
  71 +
  72 + def removed_branch?
  73 + after =~ /000000/
  74 + end
  75 +
  76 + def branch_url
  77 + "#{project_url}/commits/#{ref}"
  78 + end
  79 +
  80 + def compare_url
  81 + "#{project_url}/compare/#{before}...#{after}"
  82 + end
  83 +
  84 + def branch_link
  85 + "[#{ref}](#{branch_url})"
  86 + end
  87 +
  88 + def project_link
  89 + "[#{project_name}](#{project_url})"
  90 + end
  91 +
  92 + def compare_link
  93 + "[Compare changes](#{compare_url})"
  94 + end
  95 +end
app/models/project_services/slack_service.rb 0 → 100644
@@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
  1 +# == Schema Information
  2 +#
  3 +# Table name: services
  4 +#
  5 +# id :integer not null, primary key
  6 +# type :string(255)
  7 +# title :string(255)
  8 +# token :string(255)
  9 +# project_id :integer not null
  10 +# created_at :datetime not null
  11 +# updated_at :datetime not null
  12 +# active :boolean default(FALSE), not null
  13 +# project_url :string(255)
  14 +# subdomain :string(255)
  15 +# room :string(255)
  16 +# api_key :string(255)
  17 +#
  18 +
  19 +class SlackService < Service
  20 + attr_accessible :room
  21 + attr_accessible :subdomain
  22 +
  23 + validates :room, presence: true, if: :activated?
  24 + validates :subdomain, presence: true, if: :activated?
  25 + validates :token, presence: true, if: :activated?
  26 +
  27 + def title
  28 + 'Slack'
  29 + end
  30 +
  31 + def description
  32 + 'A team communication tool for the 21st century'
  33 + end
  34 +
  35 + def to_param
  36 + 'slack'
  37 + end
  38 +
  39 + def fields
  40 + [
  41 + { type: 'text', name: 'subdomain', placeholder: '' },
  42 + { type: 'text', name: 'token', placeholder: '' },
  43 + { type: 'text', name: 'room', placeholder: 'Ex. #general' },
  44 + ]
  45 + end
  46 +
  47 + def execute(push_data)
  48 + message = SlackMessage.new(push_data.merge(
  49 + project_url: project_url,
  50 + project_name: project_name
  51 + ))
  52 +
  53 + notifier = Slack::Notifier.new(subdomain, token)
  54 + notifier.channel = room
  55 + notifier.username = 'GitLab'
  56 + notifier.ping(message.compose)
  57 + end
  58 +
  59 + private
  60 +
  61 + def project_name
  62 + project.name_with_namespace.gsub(/\s/, '')
  63 + end
  64 +
  65 + def project_url
  66 + project.web_url
  67 + end
  68 +end
app/models/user.rb
@@ -204,7 +204,7 @@ class User &lt; ActiveRecord::Base @@ -204,7 +204,7 @@ class User &lt; ActiveRecord::Base
204 end 204 end
205 205
206 def search query 206 def search query
207 - where("name LIKE :query OR email LIKE :query OR username LIKE :query", query: "%#{query}%") 207 + where("lower(name) LIKE :query OR lower(email) LIKE :query OR lower(username) LIKE :query", query: "%#{query.downcase}%")
208 end 208 end
209 209
210 def by_username_or_id(name_or_id) 210 def by_username_or_id(name_or_id)
@@ -249,7 +249,7 @@ class User &lt; ActiveRecord::Base @@ -249,7 +249,7 @@ class User &lt; ActiveRecord::Base
249 def namespace_uniq 249 def namespace_uniq
250 namespace_name = self.username 250 namespace_name = self.username
251 if Namespace.find_by(path: namespace_name) 251 if Namespace.find_by(path: namespace_name)
252 - self.errors.add :username, "already exist" 252 + self.errors.add :username, "already exists"
253 end 253 end
254 end 254 end
255 255
app/observers/activity_observer.rb
@@ -1,39 +0,0 @@ @@ -1,39 +0,0 @@
1 -class ActivityObserver < BaseObserver  
2 - observe :issue, :note, :milestone  
3 -  
4 - def after_create(record)  
5 - event_author_id = record.author_id  
6 -  
7 - if record.kind_of?(Note)  
8 - # Skip system notes, like status changes and cross-references.  
9 - return true if record.system?  
10 -  
11 - # Skip wall notes to prevent spamming of dashboard  
12 - return true if record.noteable_type.blank?  
13 - end  
14 -  
15 - if event_author_id  
16 - create_event(record, Event.determine_action(record))  
17 - end  
18 - end  
19 -  
20 - def after_close(record, transition)  
21 - create_event(record, Event::CLOSED)  
22 - end  
23 -  
24 - def after_reopen(record, transition)  
25 - create_event(record, Event::REOPENED)  
26 - end  
27 -  
28 - protected  
29 -  
30 - def create_event(record, status)  
31 - Event.create(  
32 - project: record.project,  
33 - target_id: record.id,  
34 - target_type: record.class.name,  
35 - action: status,  
36 - author_id: current_user.id  
37 - )  
38 - end  
39 -end  
app/observers/base_observer.rb
@@ -3,6 +3,10 @@ class BaseObserver &lt; ActiveRecord::Observer @@ -3,6 +3,10 @@ class BaseObserver &lt; ActiveRecord::Observer
3 NotificationService.new 3 NotificationService.new
4 end 4 end
5 5
  6 + def event_service
  7 + EventCreateService.new
  8 + end
  9 +
6 def log_info message 10 def log_info message
7 Gitlab::AppLogger.info message 11 Gitlab::AppLogger.info message
8 end 12 end
app/observers/issue_observer.rb
1 class IssueObserver < BaseObserver 1 class IssueObserver < BaseObserver
2 def after_create(issue) 2 def after_create(issue)
3 notification.new_issue(issue, current_user) 3 notification.new_issue(issue, current_user)
  4 + event_service.open_issue(issue, current_user)
4 issue.create_cross_references!(issue.project, current_user) 5 issue.create_cross_references!(issue.project, current_user)
5 execute_hooks(issue) 6 execute_hooks(issue)
6 end 7 end
7 8
8 def after_close(issue, transition) 9 def after_close(issue, transition)
9 notification.close_issue(issue, current_user) 10 notification.close_issue(issue, current_user)
  11 + event_service.close_issue(issue, current_user)
10 create_note(issue) 12 create_note(issue)
11 execute_hooks(issue) 13 execute_hooks(issue)
12 end 14 end
13 15
14 def after_reopen(issue, transition) 16 def after_reopen(issue, transition)
  17 + event_service.reopen_issue(issue, current_user)
15 create_note(issue) 18 create_note(issue)
16 execute_hooks(issue) 19 execute_hooks(issue)
17 end 20 end
app/observers/merge_request_observer.rb
1 -class MergeRequestObserver < ActivityObserver  
2 - observe :merge_request  
3 - 1 +class MergeRequestObserver < BaseObserver
4 def after_create(merge_request) 2 def after_create(merge_request)
5 - if merge_request.author_id  
6 - create_event(merge_request, Event.determine_action(merge_request))  
7 - end  
8 - 3 + event_service.open_mr(merge_request, current_user)
9 notification.new_merge_request(merge_request, current_user) 4 notification.new_merge_request(merge_request, current_user)
10 merge_request.create_cross_references!(merge_request.project, current_user) 5 merge_request.create_cross_references!(merge_request.project, current_user)
11 execute_hooks(merge_request) 6 execute_hooks(merge_request)
12 end 7 end
13 8
14 def after_close(merge_request, transition) 9 def after_close(merge_request, transition)
15 - create_event(merge_request, Event::CLOSED) 10 + event_service.close_mr(merge_request, current_user)
16 notification.close_mr(merge_request, current_user) 11 notification.close_mr(merge_request, current_user)
17 create_note(merge_request) 12 create_note(merge_request)
18 execute_hooks(merge_request) 13 execute_hooks(merge_request)
19 end 14 end
20 15
21 def after_reopen(merge_request, transition) 16 def after_reopen(merge_request, transition)
22 - create_event(merge_request, Event::REOPENED) 17 + event_service.reopen_mr(merge_request, current_user)
23 create_note(merge_request) 18 create_note(merge_request)
24 execute_hooks(merge_request) 19 execute_hooks(merge_request)
25 merge_request.reload_code 20 merge_request.reload_code
@@ -33,16 +28,6 @@ class MergeRequestObserver &lt; ActivityObserver @@ -33,16 +28,6 @@ class MergeRequestObserver &lt; ActivityObserver
33 execute_hooks(merge_request) 28 execute_hooks(merge_request)
34 end 29 end
35 30
36 - def create_event(record, status)  
37 - Event.create(  
38 - project: record.target_project,  
39 - target_id: record.id,  
40 - target_type: record.class.name,  
41 - action: status,  
42 - author_id: current_user.id  
43 - )  
44 - end  
45 -  
46 private 31 private
47 32
48 # Create merge request note with service comment like 'Status changed to closed' 33 # Create merge request note with service comment like 'Status changed to closed'
app/observers/milestone_observer.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class MilestoneObserver < BaseObserver
  2 + def after_create(milestone)
  3 + event_service.open_milestone(milestone, current_user)
  4 + end
  5 +
  6 + def after_close(milestone, transition)
  7 + event_service.close_milestone(milestone, current_user)
  8 + end
  9 +
  10 + def after_reopen(milestone, transition)
  11 + event_service.reopen_milestone(milestone, current_user)
  12 + end
  13 +end
app/observers/note_observer.rb
@@ -2,6 +2,12 @@ class NoteObserver &lt; BaseObserver @@ -2,6 +2,12 @@ class NoteObserver &lt; BaseObserver
2 def after_create(note) 2 def after_create(note)
3 notification.new_note(note) 3 notification.new_note(note)
4 4
  5 + # Skip system notes, like status changes and cross-references.
  6 + # Skip wall notes to prevent spamming of dashboard
  7 + if note.noteable_type.present? && !note.system
  8 + event_service.leave_note(note, current_user)
  9 + end
  10 +
5 unless note.system? 11 unless note.system?
6 # Create a cross-reference note if this Note contains GFM that names an 12 # Create a cross-reference note if this Note contains GFM that names an
7 # issue, merge request, or commit. 13 # issue, merge request, or commit.
app/services/event_create_service.rb 0 → 100644
@@ -0,0 +1,64 @@ @@ -0,0 +1,64 @@
  1 +# EventCreateService class
  2 +#
  3 +# Used for creating events feed on dashboard after certain user action
  4 +#
  5 +# Ex.
  6 +# EventCreateService.new.new_issue(issue, current_user)
  7 +#
  8 +class EventCreateService
  9 + def open_issue(issue, current_user)
  10 + create_event(issue, current_user, Event::CREATED)
  11 + end
  12 +
  13 + def close_issue(issue, current_user)
  14 + create_event(issue, current_user, Event::CLOSED)
  15 + end
  16 +
  17 + def reopen_issue(issue, current_user)
  18 + create_event(issue, current_user, Event::REOPENED)
  19 + end
  20 +
  21 + def open_mr(merge_request, current_user)
  22 + create_event(merge_request, current_user, Event::CREATED)
  23 + end
  24 +
  25 + def close_mr(merge_request, current_user)
  26 + create_event(merge_request, current_user, Event::CLOSED)
  27 + end
  28 +
  29 + def reopen_mr(merge_request, current_user)
  30 + create_event(merge_request, current_user, Event::REOPENED)
  31 + end
  32 +
  33 + def merge_mr(merge_request, current_user)
  34 + create_event(merge_request, current_user, Event::MERGED)
  35 + end
  36 +
  37 + def open_milestone(milestone, current_user)
  38 + create_event(milestone, current_user, Event::CREATED)
  39 + end
  40 +
  41 + def close_milestone(milestone, current_user)
  42 + create_event(milestone, current_user, Event::CLOSED)
  43 + end
  44 +
  45 + def reopen_milestone(milestone, current_user)
  46 + create_event(milestone, current_user, Event::REOPENED)
  47 + end
  48 +
  49 + def leave_note(note, current_user)
  50 + create_event(note, current_user, Event::COMMENTED)
  51 + end
  52 +
  53 + private
  54 +
  55 + def create_event(record, current_user, status)
  56 + Event.create(
  57 + project: record.project,
  58 + target_id: record.id,
  59 + target_type: record.class.name,
  60 + action: status,
  61 + author_id: current_user.id
  62 + )
  63 + end
  64 +end
app/services/merge_requests/auto_merge_service.rb
@@ -9,11 +9,10 @@ module MergeRequests @@ -9,11 +9,10 @@ module MergeRequests
9 merge_request.lock 9 merge_request.lock
10 10
11 if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message) 11 if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message)
12 - merge_request.author_id_of_changes = current_user.id  
13 merge_request.merge 12 merge_request.merge
14 13
15 - notification.merge_mr(merge_request)  
16 - create_merge_event(merge_request) 14 + notification.merge_mr(merge_request, current_user)
  15 + create_merge_event(merge_request, current_user)
17 execute_project_hooks(merge_request) 16 execute_project_hooks(merge_request)
18 17
19 true 18 true
app/services/merge_requests/base_merge_service.rb
@@ -7,14 +7,8 @@ module MergeRequests @@ -7,14 +7,8 @@ module MergeRequests
7 NotificationService.new 7 NotificationService.new
8 end 8 end
9 9
10 - def create_merge_event(merge_request)  
11 - Event.create(  
12 - project: merge_request.target_project,  
13 - target_id: merge_request.id,  
14 - target_type: merge_request.class.name,  
15 - action: Event::MERGED,  
16 - author_id: merge_request.author_id_of_changes  
17 - ) 10 + def create_merge_event(merge_request, current_user)
  11 + EventCreateService.new.merge_mr(merge_request, current_user)
18 end 12 end
19 13
20 def execute_project_hooks(merge_request) 14 def execute_project_hooks(merge_request)
app/services/merge_requests/merge_service.rb
@@ -7,11 +7,10 @@ module MergeRequests @@ -7,11 +7,10 @@ module MergeRequests
7 # to target branch 7 # to target branch
8 class MergeService < BaseMergeService 8 class MergeService < BaseMergeService
9 def execute(merge_request, current_user, commit_message) 9 def execute(merge_request, current_user, commit_message)
10 - merge_request.author_id_of_changes = current_user.id  
11 merge_request.merge 10 merge_request.merge
12 11
13 - notification.merge_mr(merge_request)  
14 - create_merge_event(merge_request) 12 + notification.merge_mr(merge_request, current_user)
  13 + create_merge_event(merge_request, current_user)
15 execute_project_hooks(merge_request) 14 execute_project_hooks(merge_request)
16 15
17 true 16 true
app/services/notification_service.rb
@@ -86,12 +86,12 @@ class NotificationService @@ -86,12 +86,12 @@ class NotificationService
86 # * merge_request assignee if their notification level is not Disabled 86 # * merge_request assignee if their notification level is not Disabled
87 # * project team members with notification level higher then Participating 87 # * project team members with notification level higher then Participating
88 # 88 #
89 - def merge_mr(merge_request) 89 + def merge_mr(merge_request, current_user)
90 recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project) 90 recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project)
91 recipients = recipients.concat(project_watchers(merge_request.target_project)).uniq 91 recipients = recipients.concat(project_watchers(merge_request.target_project)).uniq
92 92
93 recipients.each do |recipient| 93 recipients.each do |recipient|
94 - mailer.merged_merge_request_email(recipient.id, merge_request.id) 94 + mailer.merged_merge_request_email(recipient.id, merge_request.id, current_user.id)
95 end 95 end
96 end 96 end
97 97
@@ -111,6 +111,7 @@ class NotificationService @@ -111,6 +111,7 @@ class NotificationService
111 111
112 # ignore gitlab service messages 112 # ignore gitlab service messages
113 return true if note.note =~ /\A_Status changed to closed_/ 113 return true if note.note =~ /\A_Status changed to closed_/
  114 + return true if note.note =~ /\A_mentioned in / && note.system == true
114 115
115 opts = { noteable_type: note.noteable_type, project_id: note.project_id } 116 opts = { noteable_type: note.noteable_type, project_id: note.project_id }
116 117
app/views/devise/sessions/_oauth_providers.html.haml
@@ -2,10 +2,12 @@ @@ -2,10 +2,12 @@
2 - if providers.present? 2 - if providers.present?
3 %hr 3 %hr
4 %div{:'data-no-turbolink' => 'data-no-turbolink'} 4 %div{:'data-no-turbolink' => 'data-no-turbolink'}
5 - %span Sign in with: &nbsp; 5 + %span Sign in with*: &nbsp;
6 - providers.each do |provider| 6 - providers.each do |provider|
7 %span 7 %span
8 - if default_providers.include?(provider) 8 - if default_providers.include?(provider)
9 = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) 9 = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
10 - else 10 - else
11 = link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn" 11 = link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn"
  12 + %br
  13 + %small * Make sure your email address is public
app/views/layouts/_head.html.haml
@@ -4,7 +4,8 @@ @@ -4,7 +4,8 @@
4 = "#{title} | " if defined?(title) 4 = "#{title} | " if defined?(title)
5 GitLab 5 GitLab
6 = favicon_link_tag 'favicon.ico' 6 = favicon_link_tag 'favicon.ico'
7 - = stylesheet_link_tag "application" 7 + = stylesheet_link_tag "application", :media => "all"
  8 + = stylesheet_link_tag "print", :media => "print"
8 = javascript_include_tag "application" 9 = javascript_include_tag "application"
9 = csrf_meta_tags 10 = csrf_meta_tags
10 = include_gon 11 = include_gon
app/views/layouts/_head_panel.html.haml
@@ -15,10 +15,6 @@ @@ -15,10 +15,6 @@
15 .navbar-collapse.collapse 15 .navbar-collapse.collapse
16 %ul.nav.navbar-nav 16 %ul.nav.navbar-nav
17 %li.hidden-sm.hidden-xs 17 %li.hidden-sm.hidden-xs
18 - %a  
19 - %div.hide.turbolink-spinner  
20 - %i.icon-refresh.icon-spin  
21 - %li.hidden-sm.hidden-xs  
22 = render "layouts/search" 18 = render "layouts/search"
23 %li.visible-sm.visible-xs 19 %li.visible-sm.visible-xs
24 = link_to search_path, title: "Search", class: 'has_bottom_tooltip', 'data-original-title' => 'Search area' do 20 = link_to search_path, title: "Search", class: 'has_bottom_tooltip', 'data-original-title' => 'Search area' do
app/views/layouts/_init_auto_complete.html.haml
1 :javascript 1 :javascript
2 - GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project)}"  
3 - GitLab.GfmAutoComplete.Emoji.assetBase = "#{Gitlab.config.gitlab.relative_url_root + '/assets/emoji'}" 2 + GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project, type: @noteable.class, type_id: params[:id])}"
4 GitLab.GfmAutoComplete.setup(); 3 GitLab.GfmAutoComplete.setup();
app/views/layouts/_public_head_panel.html.haml
@@ -8,11 +8,15 @@ @@ -8,11 +8,15 @@
8 %span.separator 8 %span.separator
9 %h1.title= title 9 %h1.title= title
10 10
11 - .pull-right 11 + %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"}
  12 + %span.sr-only Toggle navigation
  13 + %i.icon-reorder
  14 +
  15 + .pull-right.hidden-xs
12 = link_to "Sign in", new_session_path(:user), class: 'btn btn-sign-in btn-new' 16 = link_to "Sign in", new_session_path(:user), class: 'btn btn-sign-in btn-new'
13 17
14 - %ul.nav.navbar-nav  
15 - %li  
16 - %a  
17 - %div.hide.turbolink-spinner  
18 - %i.icon-refresh.icon-spin 18 + .navbar-collapse.collapse
  19 + %ul.nav.navbar-nav
  20 + %li.visible-xs
  21 + = link_to "Sign in", new_session_path(:user)
  22 +
app/views/layouts/public_projects.html.haml
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 %body{class: "#{app_theme} application", :'data-page' => body_data_page} 4 %body{class: "#{app_theme} application", :'data-page' => body_data_page}
5 = render "layouts/broadcast" 5 = render "layouts/broadcast"
6 = render "layouts/public_head_panel", title: project_title(@project) 6 = render "layouts/public_head_panel", title: project_title(@project)
7 - %nav.main-nav 7 + %nav.main-nav.navbar-collapse.collapse
8 .container= render 'layouts/nav/project' 8 .container= render 'layouts/nav/project'
9 .container 9 .container
10 .content= yield 10 .content= yield
app/views/notify/repository_push_email.html.haml
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 %li 7 %li
8 #{commit.short_id} - #{commit.title} 8 #{commit.short_id} - #{commit.title}
9 9
10 -%h4 Diff: 10 +%h4 Changes:
11 - @diffs.each do |diff| 11 - @diffs.each do |diff|
12 %li 12 %li
13 %strong 13 %strong
@@ -23,6 +23,6 @@ @@ -23,6 +23,6 @@
23 %br 23 %br
24 24
25 - if @compare.timeout 25 - if @compare.timeout
26 - %h5 Huge diff. To prevent performance issues it was hidden 26 + %h5 To prevent performance issues changes are hidden
27 - elsif @compare.commits_over_limit? 27 - elsif @compare.commits_over_limit?
28 - %h5 Diff for big amount of commits is disabled 28 + %h5 Changes are not shown due to large amount of commits
app/views/notify/repository_push_email.text.haml
@@ -6,7 +6,7 @@ Commits: @@ -6,7 +6,7 @@ Commits:
6 #{commit.short_id} - #{truncate(commit.title, length: 40)} 6 #{commit.short_id} - #{truncate(commit.title, length: 40)}
7 \ 7 \
8 \ 8 \
9 -Diff: 9 +Changes:
10 - @diffs.each do |diff| 10 - @diffs.each do |diff|
11 \ 11 \
12 \===================================== 12 \=====================================
@@ -22,4 +22,4 @@ Diff: @@ -22,4 +22,4 @@ Diff:
22 - if @compare.timeout 22 - if @compare.timeout
23 Huge diff. To prevent performance issues it was hidden 23 Huge diff. To prevent performance issues it was hidden
24 - elsif @compare.commits_over_limit? 24 - elsif @compare.commits_over_limit?
25 - Diff for big amount of commits is disabled 25 + Changes are not shown due to large amount of commits
app/views/projects/commits/_parallel_view.html.haml
1 / Side-by-side diff view 1 / Side-by-side diff view
2 -- old_file = get_old_file(project, @commit, diff)  
3 -- deleted_lines = {}  
4 -- added_lines = {}  
5 -- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|  
6 - - if type == "old"  
7 - - deleted_lines[line_old] = { line_code: line_code, type: type, line: line }  
8 - - elsif type == "new"  
9 - - added_lines[line_new] = { line_code: line_code, type: type, line: line }  
10 -  
11 -- max_length = old_file.sloc + added_lines.length if old_file  
12 -- max_length ||= file.sloc  
13 -- offset1 = 0  
14 -- offset2 = 0 2 +- old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file)
  3 +- num_lines = old_lines.length
15 4
16 %div.text-file-parallel 5 %div.text-file-parallel
17 - %table{ style: "table-layout: fixed;" }  
18 - - max_length.times do |line_index|  
19 - - line_index1 = line_index - offset1  
20 - - line_index2 = line_index - offset2  
21 - - deleted_line = deleted_lines[line_index1 + 1]  
22 - - added_line = added_lines[line_index2 + 1]  
23 - - old_line = old_file.lines[line_index1] if old_file  
24 - - new_line = file.lines[line_index2] 6 + %div.diff-side.diff-side-left
  7 + %table
  8 + - old_lines.each do |line|
  9 +
  10 + %tr.line_holder.parallel
  11 + - if line.type == :file_created
  12 + %td.line_content.parallel= "File was created"
  13 + - elsif line.type == :deleted
  14 + %td.line_content{class: "parallel noteable_line old #{line.code}", "line_code" => line.code }= line.content
  15 + - else line.type == :no_change
  16 + %td.line_content.parallel= line.content
  17 +
  18 + %div.diff-middle
  19 + %table
  20 + - num_lines.times do |index|
  21 + %tr
  22 + - if old_lines[index].type == :deleted
  23 + %td.old_line.old= old_lines[index].num
  24 + - else
  25 + %td.old_line= old_lines[index].num
  26 +
  27 + %td.diff_line=""
25 28
26 - - if deleted_line && added_line  
27 - - elsif deleted_line  
28 - - new_line = nil  
29 - - offset2 += 1  
30 - - elsif added_line  
31 - - old_line = nil  
32 - - offset1 += 1 29 + - if new_lines[index].type == :added
  30 + %td.new_line.new= new_lines[index].num
  31 + - else
  32 + %td.new_line= new_lines[index].num
33 33
34 - %tr.line_holder.parallel  
35 - - if line_index == 0 && diff.new_file  
36 - %td.line_content.parallel= "File was created"  
37 - %td.old_line= ""  
38 - - elsif deleted_line  
39 - %td.line_content{class: "parallel noteable_line old #{deleted_line[:line_code]}", "line_code" => deleted_line[:line_code] }= old_line  
40 - %td.old_line.old  
41 - = line_index1 + 1  
42 - - if @comments_allowed  
43 - =# render "projects/notes/diff_note_link", line_code: deleted_line[:line_code]  
44 - - elsif old_line  
45 - %td.line_content.parallel= old_line  
46 - %td.old_line= line_index1 + 1  
47 - - else  
48 - %td.line_content.parallel= ""  
49 - %td.old_line= "" 34 + %div.diff-side.diff-side-right
  35 + %table
  36 + - new_lines.each do |line|
50 37
51 - %td.diff_line= "" 38 + %tr.line_holder.parallel
  39 + - if line.type == :file_deleted
  40 + %td.line_content.parallel= "File was deleted"
  41 + - elsif line.type == :added
  42 + %td.line_content{class: "parallel noteable_line new #{line.code}", "line_code" => line.code }= line.content
  43 + - else line.type == :no_change
  44 + %td.line_content.parallel= line.content
52 45
53 - - if diff.deleted_file && line_index == 0  
54 - %td.new_line= ""  
55 - %td.line_content.parallel= "File was deleted"  
56 - - elsif added_line  
57 - %td.new_line.new  
58 - = line_index2 + 1  
59 - - if @comments_allowed  
60 - =# render "projects/notes/diff_note_link", line_code: added_line[:line_code]  
61 - %td.line_content{class: "parallel noteable_line new #{added_line[:line_code]}", "line_code" => added_line[:line_code] }= new_line  
62 - - elsif new_line  
63 - %td.new_line= line_index2 + 1  
64 - %td.line_content.parallel= new_line  
65 - - else  
66 - %td.new_line= ""  
67 - %td.line_content.parallel= "" 46 +:javascript
  47 + $('.diff-side-right').on('scroll', function(){
  48 + $('.diff-side-left, .diff-middle').scrollTop($(this).scrollTop());
  49 + $('.diff-side-left').scrollLeft($(this).scrollLeft());
  50 + });
68 51
69 - - if @reply_allowed  
70 - - comments1 = []  
71 - - comments2 = []  
72 - - comments1 = @line_notes.select { |n| n.line_code == deleted_line[:line_code] }.sort_by(&:created_at) if deleted_line  
73 - - comments2 = @line_notes.select { |n| n.line_code == added_line[:line_code] }.sort_by(&:created_at) if added_line  
74 - - unless comments1.empty? && comments2.empty?  
75 - = render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2, line1: deleted_line, line2: added_line  
76 \ No newline at end of file 52 \ No newline at end of file
  53 + $('.diff-side-left').on('scroll', function(){
  54 + $('.diff-side-right, .diff-middle').scrollTop($(this).scrollTop()); // might never be relevant
  55 + $('.diff-side-right').scrollLeft($(this).scrollLeft());
  56 + });
app/views/projects/commits/_text_file.html.haml
1 - too_big = diff.diff.lines.count > 1000 1 - too_big = diff.diff.lines.count > 1000
2 - if too_big 2 - if too_big
3 - %a.supp_diff_link Diff suppressed. Click to show 3 + %a.supp_diff_link Changes suppressed. Click to show
4 4
5 %table.text-file{class: "#{'hide' if too_big}"} 5 %table.text-file{class: "#{'hide' if too_big}"}
6 - each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line| 6 - each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|
app/views/projects/compare/show.html.haml
@@ -18,17 +18,17 @@ @@ -18,17 +18,17 @@
18 - else 18 - else
19 %ul.well-list= render Commit.decorate(@commits), project: @project 19 %ul.well-list= render Commit.decorate(@commits), project: @project
20 20
21 - %h4 Diff 21 + %h4 Changes
22 - if @diffs.present? 22 - if @diffs.present?
23 = render "projects/commits/diffs", diffs: @diffs, project: @project 23 = render "projects/commits/diffs", diffs: @diffs, project: @project
24 - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE 24 - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
25 .bs-callout.bs-callout-danger 25 .bs-callout.bs-callout-danger
26 %h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits. 26 %h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
27 - %p To preserve performance the line diff is not shown. 27 + %p To preserve performance the line changes are not shown.
28 - elsif @timeout 28 - elsif @timeout
29 .bs-callout.bs-callout-danger 29 .bs-callout.bs-callout-danger
30 - %h4 Diff for this comparison is extremely large.  
31 - %p Use command line to browse diff for this comparison. 30 + %h4 Number of changed files for this comparison is extremely large.
  31 + %p Use command line to browse through changes for this comparison.
32 32
33 33
34 - else 34 - else
app/views/projects/deploy_keys/show.html.haml
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 Deploy key: 2 Deploy key:
3 = @key.title 3 = @key.title
4 %small 4 %small
5 - created at 5 + created on
6 = @key.created_at.stamp("Aug 21, 2011") 6 = @key.created_at.stamp("Aug 21, 2011")
7 .back-link 7 .back-link
8 = link_to project_deploy_keys_path(@project) do 8 = link_to project_deploy_keys_path(@project) do
app/views/projects/issues/_form.html.haml
1 %div.issue-form-holder 1 %div.issue-form-holder
2 %h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}" 2 %h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}"
3 %hr 3 %hr
4 - - if @repository.contribution_guide && !@issue.persisted? 4 + - if !@repository.empty? && @repository.contribution_guide && !@issue.persisted?
5 - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name)) 5 - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
6 .alert.alert-info.col-sm-10.col-sm-offset-2 6 .alert.alert-info.col-sm-10.col-sm-offset-2
7 ="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe 7 ="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe
app/views/projects/merge_requests/_form.html.haml
1 -- if @repository.contribution_guide && !@merge_request.persisted?  
2 - - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))  
3 - .alert.alert-info.col-sm-10.col-sm-offset-2  
4 - ="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe  
5 = form_for [@project, @merge_request], html: { class: "merge-request-form form-horizontal" } do |f| 1 = form_for [@project, @merge_request], html: { class: "merge-request-form form-horizontal" } do |f|
6 - -if @merge_request.errors.any?  
7 - .alert.alert-danger  
8 - %ul  
9 - - @merge_request.errors.full_messages.each do |msg|  
10 - %li= msg 2 + .row
  3 + .col-sm-2
  4 + .col-sm-10
  5 + - if @repository.contribution_guide && !@merge_request.persisted?
  6 + - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
  7 + .alert.alert-info
  8 + Please review the
  9 + %strong #{link_to "guidelines for contribution", contribution_guide_url}
  10 + to this repository.
  11 +
  12 + -if @merge_request.errors.any?
  13 + .alert.alert-danger
  14 + - @merge_request.errors.full_messages.each do |msg|
  15 + %div= msg
11 16
12 .merge-request-branches 17 .merge-request-branches
13 .form-group 18 .form-group
@@ -47,24 +52,6 @@ @@ -47,24 +52,6 @@
47 = f.text_area :description, class: "form-control js-gfm-input", rows: 14 52 = f.text_area :description, class: "form-control js-gfm-input", rows: 14
48 %p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. 53 %p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
49 54
50 - %hr  
51 - .form-group  
52 - .merge-request-assignee  
53 - = f.label :assignee_id, class: 'control-label' do  
54 - %i.icon-user  
55 - Assign to  
56 - .col-sm-10  
57 - = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id)  
58 - &nbsp;  
59 - = link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'  
60 - .form-group  
61 - .merge-request-milestone  
62 - = f.label :milestone_id, class: 'control-label' do  
63 - %i.icon-time  
64 - Milestone  
65 - .col-sm-10= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2'})  
66 -  
67 -  
68 .form-actions 55 .form-actions
69 - if @merge_request.new_record? 56 - if @merge_request.new_record?
70 = f.submit 'Submit merge request', class: "btn btn-create" 57 = f.submit 'Submit merge request', class: "btn btn-create"
@@ -96,7 +83,3 @@ @@ -96,7 +83,3 @@
96 target_branch.on("change", function() { 83 target_branch.on("change", function() {
97 $.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() }); 84 $.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
98 }); 85 });
99 - $('.assign-to-me-link').on('click', function(e){  
100 - $('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");  
101 - e.preventDefault();  
102 - });  
app/views/projects/merge_requests/_show.html.haml
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 %li.diffs-tab{data: {action: 'diffs'}} 20 %li.diffs-tab{data: {action: 'diffs'}}
21 = link_to diffs_project_merge_request_path(@project, @merge_request) do 21 = link_to diffs_project_merge_request_path(@project, @merge_request) do
22 %i.icon-list-alt 22 %i.icon-list-alt
23 - Diff 23 + Changes
24 24
25 - content_for :note_actions do 25 - content_for :note_actions do
26 - if can?(current_user, :modify_merge_request, @merge_request) 26 - if can?(current_user, :modify_merge_request, @merge_request)
app/views/projects/merge_requests/branch_from.js.haml
@@ -3,5 +3,5 @@ @@ -3,5 +3,5 @@
3 var mrTitle = $('#merge_request_title'); 3 var mrTitle = $('#merge_request_title');
4 4
5 if(mrTitle.val().length == 0) { 5 if(mrTitle.val().length == 0) {
6 - mrTitle.val("#{params[:ref].titleize}"); 6 + mrTitle.val("#{params[:ref].humanize}");
7 } 7 }
app/views/projects/merge_requests/show/_diffs.html.haml
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 - else 5 - else
6 .bs-callout.bs-callout-warning 6 .bs-callout.bs-callout-warning
7 %h4 7 %h4
8 - Diff for this comparison is extremely large. 8 + Changes view for this comparison is extremely large.
9 %p 9 %p
10 You can 10 You can
11 = link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request, format: :diff), class: "vlink" 11 = link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request, format: :diff), class: "vlink"
app/views/projects/merge_requests/show/_mr_accept.html.haml
1 - unless @allowed_to_merge 1 - unless @allowed_to_merge
2 - .bs-callout  
3 - %strong You don't have permission to merge this MR 2 + - if @project.archived?
  3 + .bs-callout.bs-callout-warning
  4 + %strong Archived projects cannot be committed to!
  5 + - else
  6 + .bs-callout
  7 + .automerge_widget.cannot_be_merged.hide
  8 + %strong This can't be merged automatically, even if it could be merged you don't have the permission to do so.
  9 + .automerge_widget.can_be_merged.hide
  10 + %strong This can be merged automatically but you don't have the permission to do so.
4 11
5 12
6 - if @show_merge_controls 13 - if @show_merge_controls
app/views/projects/merge_requests/show/_mr_box.html.haml
@@ -29,13 +29,13 @@ @@ -29,13 +29,13 @@
29 %span 29 %span
30 %i.icon-remove 30 %i.icon-remove
31 Closed by #{link_to_member(@project, @merge_request.closed_event.author)} 31 Closed by #{link_to_member(@project, @merge_request.closed_event.author)}
32 - #{time_ago_with_tooltip(@merge_request.closed_event.created_at)}. 32 + #{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
33 - if @merge_request.merged? 33 - if @merge_request.merged?
34 .alert.alert-info 34 .alert.alert-info
35 %span 35 %span
36 %i.icon-ok 36 %i.icon-ok
37 Merged by #{link_to_member(@project, @merge_request.merge_event.author)} 37 Merged by #{link_to_member(@project, @merge_request.merge_event.author)}
38 - #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}. 38 + #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
39 - if !@closes_issues.empty? && @merge_request.open? 39 - if !@closes_issues.empty? && @merge_request.open?
40 .alert.alert-info.alert-info 40 .alert.alert-info.alert-info
41 %span 41 %span
app/views/projects/notes/_note.html.haml
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 %i.icon-thumbs-up 23 %i.icon-thumbs-up
24 \+1 24 \+1
25 - if note.downvote? 25 - if note.downvote?
26 - %span.vote.downvote.label.label-error 26 + %span.vote.downvote.label.label-danger
27 %i.icon-thumbs-down 27 %i.icon-thumbs-down
28 \-1 28 \-1
29 29
app/views/projects/show.html.haml
@@ -9,8 +9,8 @@ @@ -9,8 +9,8 @@
9 .col-md-3.project-side.hidden-sm 9 .col-md-3.project-side.hidden-sm
10 .clearfix 10 .clearfix
11 - if @project.archived? 11 - if @project.archived?
12 - .alert  
13 - %h5 12 + .alert.alert-warning
  13 + %h4
14 %i.icon-warning-sign 14 %i.icon-warning-sign
15 Archived project! 15 Archived project!
16 %p Repository is read-only 16 %p Repository is read-only
config/application.rb
@@ -19,7 +19,7 @@ module Gitlab @@ -19,7 +19,7 @@ module Gitlab
19 # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 19 # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
20 20
21 # Activate observers that should always be running. 21 # Activate observers that should always be running.
22 - config.active_record.observers = :activity_observer, 22 + config.active_record.observers = :milestone_observer,
23 :project_activity_cache_observer, 23 :project_activity_cache_observer,
24 :issue_observer, 24 :issue_observer,
25 :key_observer, 25 :key_observer,
config/environments/production.rb
@@ -33,6 +33,12 @@ Gitlab::Application.configure do @@ -33,6 +33,12 @@ Gitlab::Application.configure do
33 # See everything in the log (default is :info) 33 # See everything in the log (default is :info)
34 # config.log_level = :debug 34 # config.log_level = :debug
35 35
  36 + # Suppress 'Rendered template ...' messages in the log
  37 + # source: http://stackoverflow.com/a/16369363
  38 + %w{render_template render_partial render_collection}.each do |event|
  39 + ActiveSupport::Notifications.unsubscribe "#{event}.action_view"
  40 + end
  41 +
36 # Prepend all log lines with the following tags 42 # Prepend all log lines with the following tags
37 # config.log_tags = [ :subdomain, :uuid ] 43 # config.log_tags = [ :subdomain, :uuid ]
38 44
config/initializers/gemoji.rb
@@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
1 -# Workaround for https://github.com/github/gemoji/pull/18  
2 -require 'gemoji'  
3 -Gitlab::Application.config.assets.paths << Emoji.images_path  
config/routes.rb
@@ -167,7 +167,7 @@ Gitlab::Application.routes.draw do @@ -167,7 +167,7 @@ Gitlab::Application.routes.draw do
167 167
168 resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create] 168 resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create]
169 169
170 - devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations } 170 + devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations , passwords: :passwords}
171 171
172 # 172 #
173 # Project Area 173 # Project Area
config/unicorn_development.rb 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 +worker_processes 2
  2 +timeout 30
db/fixtures/development/11_keys.rb
@@ -2,7 +2,7 @@ Gitlab::Seeder.quiet do @@ -2,7 +2,7 @@ Gitlab::Seeder.quiet do
2 User.first(30).each_with_index do |user, i| 2 User.first(30).each_with_index do |user, i|
3 Key.seed(:id, [ 3 Key.seed(:id, [
4 { 4 {
5 - id: i, 5 + id: i + 1,
6 title: "Sample key #{i}", 6 title: "Sample key #{i}",
7 key: "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt#{i + 100}6k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", 7 key: "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt#{i + 100}6k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
8 user_id: user.id, 8 user_id: user.id,
doc/README.md 0 → 100644
@@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
  1 +## The GitLab Documentation covers the following subjects
  2 +
  3 ++ [API](api/README.md)
  4 ++ [Development](development/README.md)
  5 ++ [Install](install/README.md)
  6 ++ [Integration](external-issue-tracker/README.md)
  7 ++ [Legal](legal/README.md)
  8 ++ [Markdown](markdown/markdown.md)
  9 ++ [Permissions](permissions/permissions.md)
  10 ++ [Public access](public_access/public_access.md)
  11 ++ [Raketasks](raketasks/README.md)
  12 ++ [Release](release/README.md)
  13 ++ [Security](security/README.md)
  14 ++ [System hooks](system_hooks/system_hooks.md)
  15 ++ [Update](update/README.md)
  16 ++ [Web hooks](web_hooks/web_hooks.md)
  17 ++ [Workflow](workflow/workflow.md)
doc/api/README.md
1 # GitLab API 1 # GitLab API
2 2
  3 +## End-points
  4 +
  5 ++ [Users](users.md)
  6 ++ [Session](session.md)
  7 ++ [Projects](projects.md)
  8 ++ [Project Snippets](project_snippets.md)
  9 ++ [Repositories](repositories.md)
  10 ++ [Repository Files](repository_files.md)
  11 ++ [Commits](commits.md)
  12 ++ [Merge Requests](merge_requests.md)
  13 ++ [Issues](issues.md)
  14 ++ [Milestones](milestones.md)
  15 ++ [Notes](notes.md)
  16 ++ [Deploy Keys](deploy_keys.md)
  17 ++ [System Hooks](system_hooks.md)
  18 ++ [Groups](groups.md)
  19 +
  20 +## Clients
  21 +
  22 ++ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP
  23 ++ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby
  24 ++ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python
  25 ++ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java
  26 +
  27 +## Introduction
  28 +
3 All API requests require authentication. You need to pass a `private_token` parameter by url or header. If passed as header, the header name must be "PRIVATE-TOKEN" (capital and with dash instead of underscore). You can find or reset your private token in your profile. 29 All API requests require authentication. You need to pass a `private_token` parameter by url or header. If passed as header, the header name must be "PRIVATE-TOKEN" (capital and with dash instead of underscore). You can find or reset your private token in your profile.
4 30
5 If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401: 31 If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401:
@@ -117,30 +143,3 @@ Issue @@ -117,30 +143,3 @@ Issue
117 143
118 So if you want to get issue with api you use `http://host/api/v3/.../issues/:id.json` 144 So if you want to get issue with api you use `http://host/api/v3/.../issues/:id.json`
119 But when you want to create a link to web page - use `http:://host/project/issues/:iid.json` 145 But when you want to create a link to web page - use `http:://host/project/issues/:iid.json`
120 -  
121 -  
122 -  
123 -## Contents  
124 -  
125 -+ [Users](users.md)  
126 -+ [Session](session.md)  
127 -+ [Projects](projects.md)  
128 -+ [Project Snippets](project_snippets.md)  
129 -+ [Repositories](repositories.md)  
130 -+ [Repository Files](repository_files.md)  
131 -+ [Commits](commits.md)  
132 -+ [Merge Requests](merge_requests.md)  
133 -+ [Issues](issues.md)  
134 -+ [Milestones](milestones.md)  
135 -+ [Notes](notes.md)  
136 -+ [Deploy Keys](deploy_keys.md)  
137 -+ [System Hooks](system_hooks.md)  
138 -+ [Groups](groups.md)  
139 -  
140 -  
141 -## Clients  
142 -  
143 -+ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP  
144 -+ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby  
145 -+ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python  
146 -+ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java  
doc/api/merge_requests.md
@@ -150,6 +150,7 @@ Parameters: @@ -150,6 +150,7 @@ Parameters:
150 + `target_branch` - The target branch 150 + `target_branch` - The target branch
151 + `assignee_id` - Assignee user ID 151 + `assignee_id` - Assignee user ID
152 + `title` - Title of MR 152 + `title` - Title of MR
  153 ++ `state_event` - New state (close|reopen|merge)
153 154
154 ```json 155 ```json
155 156
@@ -210,3 +211,44 @@ Parameters: @@ -210,3 +211,44 @@ Parameters:
210 "note":"text1" 211 "note":"text1"
211 } 212 }
212 ``` 213 ```
  214 +
  215 +
  216 +## Get the comments on a MR
  217 +
  218 +Gets all the comments associated with a merge request.
  219 +
  220 +```
  221 +GET /projects/:id/merge_request/:merge_request_id/comments
  222 +```
  223 +
  224 +Parameters:
  225 +
  226 ++ `id` (required) - The ID of a project
  227 ++ `merge_request_id` (required) - ID of merge request
  228 +
  229 +```json
  230 +[
  231 + {
  232 + "note":"this is the 1st comment on the 2merge merge request",
  233 + "author":{
  234 + "id":11,
  235 + "username":"admin",
  236 + "email":"admin@local.host",
  237 + "name":"Administrator",
  238 + "state":"active",
  239 + "created_at":"2014-03-06T08:17:35.000Z"
  240 + }
  241 + },
  242 + {
  243 + "note":"_Status changed to closed_",
  244 + "author":{
  245 + "id":11,
  246 + "username":"admin",
  247 + "email":"admin@local.host",
  248 + "name":"Administrator",
  249 + "state":"active",
  250 + "created_at":"2014-03-06T08:17:35.000Z"
  251 + }
  252 + }
  253 +]
  254 +```
doc/api/projects.md
@@ -621,3 +621,29 @@ Parameters: @@ -621,3 +621,29 @@ Parameters:
621 + query (required) - A string contained in the project name 621 + query (required) - A string contained in the project name
622 + per_page (optional) - number of projects to return per page 622 + per_page (optional) - number of projects to return per page
623 + page (optional) - the page to retrieve 623 + page (optional) - the page to retrieve
  624 +
  625 +
  626 +## Labels
  627 +
  628 +### List project labels
  629 +
  630 +Get a list of project labels.
  631 +
  632 +```
  633 +GET /projects/:id/labels
  634 +```
  635 +
  636 +Parameters:
  637 +
  638 ++ `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
  639 +
  640 +```json
  641 +[
  642 + {
  643 + "name":"featute"
  644 + },
  645 + {
  646 + "name": "bug"
  647 + }
  648 +]
  649 +```
doc/api/system_hooks.md
1 -All methods require admin authorization.  
2 -  
3 -The url endpoint of the system hooks can be configured in [the admin area under hooks](/admin/hooks).  
4 -  
5 -## List system hooks  
6 -  
7 -Get list of system hooks  
8 -  
9 -```  
10 -GET /hooks  
11 -```  
12 -  
13 -Parameters:  
14 -  
15 -+ **none**  
16 -  
17 -```json  
18 -[  
19 - {  
20 - "id":3,  
21 - "url":"http://example.com/hook",  
22 - "created_at":"2013-10-02T10:15:31Z"  
23 - }  
24 -]  
25 -```  
26 -  
27 -## Add new system hook hook  
28 -  
29 -```  
30 -POST /hooks  
31 -```  
32 -  
33 -Parameters:  
34 -  
35 -+ `url` (required) - The hook URL  
36 -  
37 -  
38 -## Test system hook  
39 -  
40 -```  
41 -GET /hooks/:id  
42 -```  
43 -  
44 -Parameters:  
45 -  
46 -+ `id` (required) - The ID of hook  
47 -  
48 -```json  
49 -{  
50 - "event_name":"project_create",  
51 - "name":"Ruby",  
52 - "path":"ruby",  
53 - "project_id":1,  
54 - "owner_name":"Someone",  
55 - "owner_email":"example@gitlabhq.com"  
56 -}  
57 -```  
58 -  
59 -## Delete system hook  
60 -  
61 -Deletes a system hook. This is an idempotent API function and returns `200 Ok` even if the hook  
62 -is not available. If the hook is deleted it is also returned as JSON.  
63 -  
64 -```  
65 -DELETE /hooks/:id  
66 -```  
67 -  
68 -Parameters:  
69 -  
70 -+ `id` (required) - The ID of hook 1 +All methods require admin authorization.
  2 +
  3 +The url endpoint of the system hooks can be configured in [the admin area under hooks](/admin/hooks).
  4 +
  5 +## List system hooks
  6 +
  7 +Get list of system hooks
  8 +
  9 +```
  10 +GET /hooks
  11 +```
  12 +
  13 +Parameters:
  14 +
  15 ++ **none**
  16 +
  17 +```json
  18 +[
  19 + {
  20 + "id":3,
  21 + "url":"http://example.com/hook",
  22 + "created_at":"2013-10-02T10:15:31Z"
  23 + }
  24 +]
  25 +```
  26 +
  27 +## Add new system hook hook
  28 +
  29 +```
  30 +POST /hooks
  31 +```
  32 +
  33 +Parameters:
  34 +
  35 ++ `url` (required) - The hook URL
  36 +
  37 +
  38 +## Test system hook
  39 +
  40 +```
  41 +GET /hooks/:id
  42 +```
  43 +
  44 +Parameters:
  45 +
  46 ++ `id` (required) - The ID of hook
  47 +
  48 +```json
  49 +{
  50 + "event_name":"project_create",
  51 + "name":"Ruby",
  52 + "path":"ruby",
  53 + "project_id":1,
  54 + "owner_name":"Someone",
  55 + "owner_email":"example@gitlabhq.com"
  56 +}
  57 +```
  58 +
  59 +## Delete system hook
  60 +
  61 +Deletes a system hook. This is an idempotent API function and returns `200 Ok` even if the hook
  62 +is not available. If the hook is deleted it is also returned as JSON.
  63 +
  64 +```
  65 +DELETE /hooks/:id
  66 +```
  67 +
  68 +Parameters:
  69 +
  70 ++ `id` (required) - The ID of hook
doc/development/README.md 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 ++ [Architecture](architecture.md)
  2 ++ [Shell commands](shell_commands.md)
doc/development/architecture.md
@@ -18,7 +18,7 @@ New releases are generally around the same time as GitLab CE releases with excep @@ -18,7 +18,7 @@ New releases are generally around the same time as GitLab CE releases with excep
18 18
19 # System Layout 19 # System Layout
20 20
21 -When referring to ~git in the picures it means the home directory of the git user which is typically /home/git. 21 +When referring to ~git in the pictures it means the home directory of the git user which is typically /home/git.
22 22
23 GitLab is primarily installed within the `/home/git` user home directory as `git` user. 23 GitLab is primarily installed within the `/home/git` user home directory as `git` user.
24 Within the home directory is where the gitlabhq server software resides as well as the repositories (though the repository location is configurable). 24 Within the home directory is where the gitlabhq server software resides as well as the repositories (though the repository location is configurable).
@@ -28,7 +28,7 @@ To serve repositories over SSH there&#39;s an add-on application called gitlab-shell @@ -28,7 +28,7 @@ To serve repositories over SSH there&#39;s an add-on application called gitlab-shell
28 28
29 ## Components 29 ## Components
30 30
31 -![GitLab Diagram Overview](resources/gitlab_diagram_overview.png "GitLab Diagram Overview") 31 +![GitLab Diagram Overview](resources/gitlab_diagram_overview.png)
32 32
33 A typical install of GitLab will be on Ubuntu Linux or RHEL/CentOS. 33 A typical install of GitLab will be on Ubuntu Linux or RHEL/CentOS.
34 It uses Nginx or Apache as a web front end to proxypass the Unicorn web server. 34 It uses Nginx or Apache as a web front end to proxypass the Unicorn web server.
@@ -180,4 +180,4 @@ bundle exec rake gitlab:check RAILS_ENV=production @@ -180,4 +180,4 @@ bundle exec rake gitlab:check RAILS_ENV=production
180 ``` 180 ```
181 181
182 Note: It is recommended to log into the `git` user using `sudo -i -u git` or `sudo su - git`. 182 Note: It is recommended to log into the `git` user using `sudo -i -u git` or `sudo su - git`.
183 -While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL. 183 -While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL.
  184 +While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL.
184 \ No newline at end of file 185 \ No newline at end of file
doc/development/shell_commands.md
1 # Guidelines for shell commands in the GitLab codebase 1 # Guidelines for shell commands in the GitLab codebase
2 2
  3 +## References
  4 +
  5 +- [Google Ruby Security Reviewer's Guide](https://code.google.com/p/ruby-security/wiki/Guide)
  6 +- [OWASP Command Injection](https://www.owasp.org/index.php/Command_Injection)
  7 +- [Ruby on Rails Security Guide Command Line Injection](http://guides.rubyonrails.org/security.html#command-line-injection)
  8 +
3 ## Use File and FileUtils instead of shell commands 9 ## Use File and FileUtils instead of shell commands
4 10
5 Sometimes we invoke basic Unix commands via the shell when there is also a Ruby API for doing it. 11 Sometimes we invoke basic Unix commands via the shell when there is also a Ruby API for doing it.
doc/install/README.md 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 ++ [Installation](installation.md)
  2 ++ [Requirements](requirements.md)
  3 ++ [Structure](structure.md)
  4 ++ [Database MySQL](database_mysql.md)
doc/install/database_mysql.md
@@ -6,6 +6,9 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se @@ -6,6 +6,9 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
6 6
7 # Install the database packages 7 # Install the database packages
8 sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev 8 sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
  9 +
  10 + # Ensure you have MySQL version 5.5.14 or later
  11 + mysql --version
9 12
10 # Pick a database root password (can be anything), type it and press enter 13 # Pick a database root password (can be anything), type it and press enter
11 # Retype the database root password and press enter 14 # Retype the database root password and press enter
@@ -23,6 +26,10 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se @@ -23,6 +26,10 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
23 # change $password in the command below to a real password you pick 26 # change $password in the command below to a real password you pick
24 mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password'; 27 mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password';
25 28
  29 + # Ensure you can use the InnoDB engine which is necessary to support long indexes.
  30 + # If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off"
  31 + mysql> SET storage_engine=INNODB;
  32 +
26 # Create the GitLab production database 33 # Create the GitLab production database
27 mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; 34 mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
28 35
doc/install/installation.md
@@ -128,7 +128,7 @@ GitLab Shell is an ssh access and repository management software developed speci @@ -128,7 +128,7 @@ GitLab Shell is an ssh access and repository management software developed speci
128 cd /home/git 128 cd /home/git
129 129
130 # Clone gitlab shell 130 # Clone gitlab shell
131 - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-shell.git -b v1.8.0 131 + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-shell.git -b v1.9.1
132 132
133 cd gitlab-shell 133 cd gitlab-shell
134 134
@@ -173,7 +173,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da @@ -173,7 +173,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
173 ## Clone the Source 173 ## Clone the Source
174 174
175 # Clone GitLab repository 175 # Clone GitLab repository
176 - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 6-6-stable gitlab 176 + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 6-7-stable gitlab
177 177
178 # Go to gitlab dir 178 # Go to gitlab dir
179 cd /home/git/gitlab 179 cd /home/git/gitlab
doc/integration/external-issue-tracker.md
@@ -5,3 +5,5 @@ GitLab has a great issue tracker but you can also use an external issue tracker @@ -5,3 +5,5 @@ GitLab has a great issue tracker but you can also use an external issue tracker
5 - textual references to PROJECT-1234 in comments, commit messages get turned into HTML links to the corresponding JIRA issue. 5 - textual references to PROJECT-1234 in comments, commit messages get turned into HTML links to the corresponding JIRA issue.
6 6
7 ![jira screenshot](jira-intergration-points.png) 7 ![jira screenshot](jira-intergration-points.png)
  8 +
  9 +You can configure the integration in the gitlab.yml configuration file.
doc/legal/README.md 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 ++ [Corporate contributor license agreement](corporate_contributor_license_agreement.md)
  2 ++ [Individual contributor license agreement](individual_contributor_license_agreement.md)
doc/permissions/permissions.md
@@ -38,7 +38,7 @@ If a user is a GitLab administrator they receive all permissions. @@ -38,7 +38,7 @@ If a user is a GitLab administrator they receive all permissions.
38 |------|-----|--------|---------|------|-----| 38 |------|-----|--------|---------|------|-----|
39 |Browse group|✓|✓|✓|✓|✓| 39 |Browse group|✓|✓|✓|✓|✓|
40 |Edit group|||||✓| 40 |Edit group|||||✓|
41 -|create project in group|||||✓| 41 +|Create project in group|||||✓|
42 |Manage group members|||||✓| 42 |Manage group members|||||✓|
43 |Remove group|||||✓| 43 |Remove group|||||✓|
44 44
doc/raketasks/README.md 0 → 100644
@@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
  1 ++ [Backup restore](backup_restore.md)
  2 ++ [Cleanup](cleanup.md)
  3 ++ [Features](features.md)
  4 ++ [Maintenance](maintenance.md)
  5 ++ [User management](user_management.md)
  6 ++ [Web hooks](web_hooks.md)
doc/release/README.md 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 ++ [Monthly](monthly.md)
  2 ++ [Security](security.md)
doc/release/monthly.md
@@ -58,19 +58,17 @@ Check if changed since last release (~22nd of last month depending on when last @@ -58,19 +58,17 @@ Check if changed since last release (~22nd of last month depending on when last
58 58
59 After making the release branch new commits are cherry-picked from master. When the release gets closer we get more selective what is cherry-picked. The days of the month are approximately as follows: 59 After making the release branch new commits are cherry-picked from master. When the release gets closer we get more selective what is cherry-picked. The days of the month are approximately as follows:
60 60
61 -* 17th: feature freeze (stop merging new features in master)  
62 -* 18th: UI freeze (stop merging changes to the user interface)  
63 -* 19th: code freeze (stop merging non-essential code improvements)  
64 -* 20th: release candidate 1 (VERSION x.x.0.rc1, tag and tweet about x.x.0.rc1)  
65 -* 21st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems) 61 +* 1-7th: official merge window (see contributing guide)
  62 +* 8-14th: work on bugfixes, sponsored features and GitLab EE
  63 +* 15th: code freeze (stop merging into master except essential bugfixes)
  64 +* 18th: release candidate 1 (VERSION x.x.0.rc1, tag and tweet about x.x.0.rc1, release on GitLab Cloud)
  65 +* 20st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems)
66 * 22nd: release (VERSION x.x.0, create x-x-stable branch, tag, blog and tweet) 66 * 22nd: release (VERSION x.x.0, create x-x-stable branch, tag, blog and tweet)
67 * 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems) 67 * 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems)
68 -* 24-end of month: release Enterprise Edition and upgrade GitLab Cloud  
69 -* 1-7th: official merge window (see contributing guide)  
70 -* 8-16th: bugfixes and sponsored features 68 +* 24-end of month: release GitLab EE and GitLab CI
71 69
72 # Write a blog post 70 # Write a blog post
73 71
74 * Mention what GitLab is on the second line: GitLab is open source software to collaborate on code. 72 * Mention what GitLab is on the second line: GitLab is open source software to collaborate on code.
75 * Select and thank the the Most Valuable Person (MVP) of this release. 73 * Select and thank the the Most Valuable Person (MVP) of this release.
76 -* Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.  
77 \ No newline at end of file 74 \ No newline at end of file
  75 +* Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.
doc/security/README.md 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 ++ [Password length limits](password_length_limits.md)
  2 ++ [Rack attack](rack_attack.md)
doc/ssh/deploy_keys.md 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +Deploy keys allow read-only access one or multiple projects with a single SSH key.
  2 +
  3 +This is really useful for cloning repositories to your Continuous Integration (CI) server.
  4 +By using a deploy keys you don't have to setup a dummy user account.
  5 +
  6 +If you are a project master or owner you can add a deploy key in the project settings under the section Deploy Keys.
  7 +Press the 'New Deploy Key' button and upload a public ssh key.
  8 +After this the machine that uses the corresponding private key has read-only access to the project.
  9 +
  10 +You can't add the same deploy key twice with the 'New Deploy Key' option.
  11 +If you want to add the same key to another project please enable it in the list that says 'Deploy keys from projects available to you'.
  12 +You need to be the owner of the deploy key to see it in this list.
doc/update/4.2-to-5.0.md
1 # From 4.2 to 5.0 1 # From 4.2 to 5.0
2 2
3 ## Warning 3 ## Warning
4 -GitLab 5.0 is affected by critical security vulnerability CVE-2013-4490. Please update to GitLab 5.4 immediately. 4 +GitLab 5.0 is affected by critical security vulnerability CVE-2013-4490.
5 5
6 ## Important changes 6 ## Important changes
7 7
doc/update/5.0-to-5.1.md
1 # From 5.0 to 5.1 1 # From 5.0 to 5.1
2 2
3 ## Warning 3 ## Warning
4 -GitLab 5.1 is affected by critical security vulnerability CVE-2013-4490. Please [update to GitLab 5.4 immediately](5.1-to-5.4.md). 4 +GitLab 5.1 is affected by critical security vulnerability CVE-2013-4490.
5 5
6 ## Release notes: 6 ## Release notes:
7 7
doc/update/5.1-to-5.2.md
1 # From 5.1 to 5.2 1 # From 5.1 to 5.2
2 2
3 ## Warning 3 ## Warning
4 -GitLab 5.2 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 5.4 directly](5.1-to-5.4.md). 4 +GitLab 5.2 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
5 5
6 ### 0. Backup 6 ### 0. Backup
7 7
doc/update/5.1-to-5.4.md
1 # From 5.1 to 5.4 1 # From 5.1 to 5.4
2 Also works starting from 5.2. 2 Also works starting from 5.2.
3 3
4 -## Notice  
5 -Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 5.4.  
6 -  
7 ### 0. Backup 4 ### 0. Backup
8 5
9 It's useful to make a backup just in case things go south: 6 It's useful to make a backup just in case things go south:
doc/update/5.1-to-6.0.md
1 # From 5.1 to 6.0 1 # From 5.1 to 6.0
2 2
3 ## Warning 3 ## Warning
4 -GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 6.2 immediately](6.0-to-6.2.md). 4 +GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
5 5
6 ### Deprecations 6 ### Deprecations
7 7
doc/update/5.2-to-5.3.md
1 # From 5.2 to 5.3 1 # From 5.2 to 5.3
2 2
3 ## Warning 3 ## Warning
4 -GitLab 5.3 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 5.4 directly](5.1-to-5.4.md). 4 +GitLab 5.3 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
5 5
6 ### 0. Backup 6 ### 0. Backup
7 7
doc/update/5.3-to-5.4.md
1 # From 5.3 to 5.4 1 # From 5.3 to 5.4
2 2
3 -## Notice  
4 -Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 5.4.  
5 -  
6 ### 0. Backup 3 ### 0. Backup
7 4
8 It's useful to make a backup just in case things go south: 5 It's useful to make a backup just in case things go south:
doc/update/5.4-to-6.0.md
1 # From 5.4 to 6.0 1 # From 5.4 to 6.0
2 2
3 ## Warning 3 ## Warning
4 -GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 6.2 immediately](6.0-to-6.2.md). 4 +GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
5 5
6 ### Deprecations 6 ### Deprecations
7 7
doc/update/6.0-to-6.1.md
1 # From 6.0 to 6.1 1 # From 6.0 to 6.1
2 2
3 ## Warning 3 ## Warning
4 -GitLab 6.1 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 6.2 directly](6.0-to-6.2.md). 4 +GitLab 6.1 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
5 5
6 # In 6.1 we remove a lot of deprecated code. 6 # In 6.1 we remove a lot of deprecated code.
7 # You should update to 6.0 before installing 6.1 so all the necessary conversions are run. 7 # You should update to 6.0 before installing 6.1 so all the necessary conversions are run.
doc/update/6.0-to-6.5.md
@@ -1,130 +0,0 @@ @@ -1,130 +0,0 @@
1 -# From 6.0 to 6.5  
2 -  
3 -# In 6.1 we remove a lot of deprecated code.  
4 -# You should update to 6.0 before installing 6.1 or higher so all the necessary conversions are run.  
5 -  
6 -### Deprecations  
7 -  
8 -#### Global issue numbers  
9 -  
10 -As of 6.1 issue numbers are project specific. This means all issues are renumbered and get a new number in their url. If you use an old issue number url and the issue number does not exist yet you are redirected to the new one. This conversion does not trigger if the old number already exists for this project, this is unlikely but will happen with old issues and large projects.  
11 -  
12 -### 0. Backup  
13 -  
14 -It's useful to make a backup just in case things go south:  
15 -(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)  
16 -  
17 -```bash  
18 -cd /home/git/gitlab  
19 -sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production  
20 -```  
21 -  
22 -### 1. Stop server  
23 -  
24 - sudo service gitlab stop  
25 -  
26 -### 2. Get latest code  
27 -  
28 -```bash  
29 -cd /home/git/gitlab  
30 -sudo -u git -H git fetch --all  
31 -sudo -u git -H git checkout 6-5-stable  
32 -# For GitLab Enterprise Edition: sudo -u git -H git checkout 6-5-stable-ee  
33 -```  
34 -  
35 -  
36 -### 3. Install additional packages  
37 -  
38 -```bash  
39 -# Add support for lograte for better log file handling  
40 -sudo apt-get install logrotate  
41 -```  
42 -  
43 -### 4. Update gitlab-shell  
44 -  
45 -```bash  
46 -cd /home/git/gitlab-shell  
47 -sudo -u git -H git fetch  
48 -sudo -u git -H git checkout v1.8.0 # Addresses multiple critical security vulnerabilities  
49 -```  
50 -  
51 -### 5. Install libs, migrations, etc.  
52 -  
53 -```bash  
54 -cd /home/git/gitlab  
55 -  
56 -# MySQL installations (note: the line below states '--without ... postgres')  
57 -sudo -u git -H bundle install --without development test postgres --deployment  
58 -  
59 -# PostgreSQL installations (note: the line below states '--without ... mysql')  
60 -sudo -u git -H bundle install --without development test mysql --deployment  
61 -  
62 -  
63 -sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production  
64 -sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production  
65 -sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production  
66 -sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production  
67 -sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production  
68 -```  
69 -  
70 -### 6. Update config files  
71 -  
72 -TIP: to see what changed in gitlab.yml.example in this release use next command:  
73 -  
74 -```  
75 -git diff 6-0-stable:config/gitlab.yml.example 6-5-stable:config/gitlab.yml.example  
76 -```  
77 -  
78 -* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-5-stable/config/gitlab.yml.example but with your settings.  
79 -* Make `/home/git/gitlab/config/unicorn.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-5-stable/config/unicorn.rb.example but with your settings.  
80 -* Copy rack attack middleware config  
81 -  
82 -```bash  
83 -sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb  
84 -```  
85 -  
86 -* Set up logrotate  
87 -  
88 -```bash  
89 -sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab  
90 -```  
91 -  
92 -### 7. Update Init script  
93 -  
94 -```bash  
95 -sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab  
96 -```  
97 -  
98 -### 8. Start application  
99 -  
100 - sudo service gitlab start  
101 - sudo service nginx restart  
102 -  
103 -### 9. Check application status  
104 -  
105 -Check if GitLab and its environment are configured correctly:  
106 -  
107 - cd /home/git/gitlab  
108 - sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production  
109 -  
110 -To make sure you didn't miss anything run a more thorough check with:  
111 -  
112 - sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production  
113 -  
114 -If all items are green, then congratulations upgrade complete!  
115 -  
116 -## Things went south? Revert to previous version (6.0)  
117 -  
118 -### 1. Revert the code to the previous version  
119 -Follow the [`upgrade guide from 5.4 to 6.0`](5.4-to-6.0.md), except for the database migration  
120 -(The backup is already migrated to the previous version)  
121 -  
122 -### 2. Restore from the backup:  
123 -  
124 -```bash  
125 -cd /home/git/gitlab  
126 -sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production  
127 -```  
128 -  
129 -## Login issues after upgrade?  
130 -If running in https mode, be sure to read [Can't Verify csrf token authenticity](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide#cant-verify-csrf-token-authenticitycant-get-past-login-pageredirected-to-login-page)  
doc/update/6.0-to-6.7.md 0 → 100644
@@ -0,0 +1,145 @@ @@ -0,0 +1,145 @@
  1 +# From 6.0 to 6.7
  2 +
  3 +# In 6.1 we remove a lot of deprecated code.
  4 +# You should update to 6.0 before installing 6.1 or higher so all the necessary conversions are run.
  5 +
  6 +### Deprecations
  7 +
  8 +#### Global issue numbers
  9 +
  10 +As of 6.1 issue numbers are project specific. This means all issues are renumbered and get a new number in their url. If you use an old issue number url and the issue number does not exist yet you are redirected to the new one. This conversion does not trigger if the old number already exists for this project, this is unlikely but will happen with old issues and large projects.
  11 +
  12 +### 0. Backup
  13 +
  14 +It's useful to make a backup just in case things go south:
  15 +(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
  16 +
  17 +```bash
  18 +cd /home/git/gitlab
  19 +sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
  20 +```
  21 +
  22 +### 1. Stop server
  23 +
  24 + sudo service gitlab stop
  25 +
  26 +### 2. Get latest code
  27 +
  28 +```bash
  29 +cd /home/git/gitlab
  30 +sudo -u git -H git fetch --all
  31 +```
  32 +
  33 +For Gitlab Community Edition:
  34 +
  35 +```bash
  36 +sudo -u git -H git checkout 6-7-stable
  37 +```
  38 +
  39 +OR
  40 +
  41 +For GitLab Enterprise Edition:
  42 +
  43 +```bash
  44 +sudo -u git -H git checkout 6-7-stable-ee
  45 +```
  46 +
  47 +
  48 +### 3. Install additional packages
  49 +
  50 +```bash
  51 +# Add support for lograte for better log file handling
  52 +sudo apt-get install logrotate
  53 +```
  54 +
  55 +### 4. Update gitlab-shell
  56 +
  57 +```bash
  58 +cd /home/git/gitlab-shell
  59 +sudo -u git -H git fetch
  60 +sudo -u git -H git checkout v1.9.1 # Addresses multiple critical security vulnerabilities
  61 +```
  62 +
  63 +### 5. Install libs, migrations, etc.
  64 +
  65 +```bash
  66 +cd /home/git/gitlab
  67 +
  68 +# MySQL installations (note: the line below states '--without ... postgres')
  69 +sudo -u git -H bundle install --without development test postgres --deployment
  70 +
  71 +# PostgreSQL installations (note: the line below states '--without ... mysql')
  72 +sudo -u git -H bundle install --without development test mysql --deployment
  73 +
  74 +
  75 +# Run database migrations
  76 +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
  77 +
  78 +# Enable internal issue IDs (introduced in GitLab 6.1)
  79 +sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production
  80 +
  81 +# Clean up assets and cache
  82 +sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
  83 +```
  84 +
  85 +### 6. Update config files
  86 +
  87 +TIP: to see what changed in gitlab.yml.example in this release use next command:
  88 +
  89 +```
  90 +git diff 6-0-stable:config/gitlab.yml.example 6-7-stable:config/gitlab.yml.example
  91 +```
  92 +
  93 +* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-7-stable/config/gitlab.yml.example but with your settings.
  94 +* Make `/home/git/gitlab/config/unicorn.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-7-stable/config/unicorn.rb.example but with your settings.
  95 +* Copy rack attack middleware config
  96 +
  97 +```bash
  98 +sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
  99 +```
  100 +
  101 +* Set up logrotate
  102 +
  103 +```bash
  104 +sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
  105 +```
  106 +
  107 +### 7. Update Init script
  108 +
  109 +```bash
  110 +sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
  111 +```
  112 +
  113 +### 8. Start application
  114 +
  115 + sudo service gitlab start
  116 + sudo service nginx restart
  117 +
  118 +### 9. Check application status
  119 +
  120 +Check if GitLab and its environment are configured correctly:
  121 +
  122 + cd /home/git/gitlab
  123 + sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
  124 +
  125 +To make sure you didn't miss anything run a more thorough check with:
  126 +
  127 + sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
  128 +
  129 +If all items are green, then congratulations upgrade complete!
  130 +
  131 +## Things went south? Revert to previous version (6.0)
  132 +
  133 +### 1. Revert the code to the previous version
  134 +Follow the [`upgrade guide from 5.4 to 6.0`](5.4-to-6.0.md), except for the database migration
  135 +(The backup is already migrated to the previous version)
  136 +
  137 +### 2. Restore from the backup:
  138 +
  139 +```bash
  140 +cd /home/git/gitlab
  141 +sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
  142 +```
  143 +
  144 +## Login issues after upgrade?
  145 +If running in https mode, be sure to read [Can't Verify csrf token authenticity](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide#cant-verify-csrf-token-authenticitycant-get-past-login-pageredirected-to-login-page)
doc/update/6.1-to-6.2.md
1 # From 6.1 to 6.2 1 # From 6.1 to 6.2
2 2
3 -## Notice  
4 -Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 6.2.  
5 -  
6 # You should update to 6.1 before installing 6.2 so all the necessary conversions are run. 3 # You should update to 6.1 before installing 6.2 so all the necessary conversions are run.
7 4
8 ### 0. Backup 5 ### 0. Backup
doc/update/6.6-to-6.7.md
@@ -37,7 +37,7 @@ sudo -u git -H git checkout 6-7-stable-ee @@ -37,7 +37,7 @@ sudo -u git -H git checkout 6-7-stable-ee
37 ```bash 37 ```bash
38 cd /home/git/gitlab-shell 38 cd /home/git/gitlab-shell
39 sudo -u git -H git fetch 39 sudo -u git -H git fetch
40 -sudo -u git -H git checkout v1.8.0 40 +sudo -u git -H git checkout v1.9.1
41 ``` 41 ```
42 42
43 ### 4. Install libs, migrations, etc. 43 ### 4. Install libs, migrations, etc.
doc/update/README.md 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 ++ [The indivual upgrade guides](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update)
  2 ++ [Uprader](upgrader.md)
  3 ++ [Ruby](ruby.md)
  4 ++ [Patch versions](patch_versions.md)
  5 ++ [MySQL to Postgress](mysql_to_postgress.md)
doc/web_hooks/web_hooks.md
@@ -5,6 +5,7 @@ Project web hooks allow you to trigger an URL if new code is pushed or a new iss @@ -5,6 +5,7 @@ Project web hooks allow you to trigger an URL if new code is pushed or a new iss
5 You can configure web hook to listen for specific events like pushes, issues, merge requests. 5 You can configure web hook to listen for specific events like pushes, issues, merge requests.
6 GitLab will send POST request with data to web hook URL. 6 GitLab will send POST request with data to web hook URL.
7 Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. 7 Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server.
  8 +If you send a web hook to an SSL endpoint [the certificate will not be verified](https://gitlab.com/gitlab-org/gitlab-ce/blob/ccd617e58ea71c42b6b073e692447d0fe3c00be6/app/models/web_hook.rb#L35) since many people use self-signed certificates.
8 9
9 --- 10 ---
10 11
features/project/service.feature
@@ -37,6 +37,12 @@ Feature: Project Services @@ -37,6 +37,12 @@ Feature: Project Services
37 And I fill Assembla settings 37 And I fill Assembla settings
38 Then I should see Assembla service settings saved 38 Then I should see Assembla service settings saved
39 39
  40 + Scenario: Activate Slack service
  41 + When I visit project "Shop" services page
  42 + And I click Slack service link
  43 + And I fill Slack settings
  44 + Then I should see Slack service settings saved
  45 +
40 Scenario: Activate email on push service 46 Scenario: Activate email on push service
41 When I visit project "Shop" services page 47 When I visit project "Shop" services page
42 And I click email on push service link 48 And I click email on push service link
features/steps/dashboard/dashboard.rb
@@ -25,7 +25,7 @@ class Dashboard &lt; Spinach::FeatureSteps @@ -25,7 +25,7 @@ class Dashboard &lt; Spinach::FeatureSteps
25 find("#merge_request_target_project_id").value.should == @project.id.to_s 25 find("#merge_request_target_project_id").value.should == @project.id.to_s
26 find("#merge_request_source_branch").value.should == "new_design" 26 find("#merge_request_source_branch").value.should == "new_design"
27 find("#merge_request_target_branch").value.should == "master" 27 find("#merge_request_target_branch").value.should == "master"
28 - find("#merge_request_title").value.should == "New Design" 28 + find("#merge_request_title").value.should == "New design"
29 end 29 end
30 30
31 Given 'user with name "John Doe" joined project "Shop"' do 31 Given 'user with name "John Doe" joined project "Shop"' do
features/steps/project/redirects.rb
@@ -4,7 +4,7 @@ class Spinach::Features::ProjectRedirects &lt; Spinach::FeatureSteps @@ -4,7 +4,7 @@ class Spinach::Features::ProjectRedirects &lt; Spinach::FeatureSteps
4 include SharedProject 4 include SharedProject
5 5
6 step 'public project "Community"' do 6 step 'public project "Community"' do
7 - create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC 7 + create :project, :public, name: 'Community'
8 end 8 end
9 9
10 step 'private project "Enterprise"' do 10 step 'private project "Enterprise"' do
features/steps/project/services.rb
@@ -100,4 +100,22 @@ class ProjectServices &lt; Spinach::FeatureSteps @@ -100,4 +100,22 @@ class ProjectServices &lt; Spinach::FeatureSteps
100 step 'I should see email on push service settings saved' do 100 step 'I should see email on push service settings saved' do
101 find_field('Recipients').value.should == 'qa@company.name' 101 find_field('Recipients').value.should == 'qa@company.name'
102 end 102 end
  103 +
  104 + step 'I click Slack service link' do
  105 + click_link 'Slack'
  106 + end
  107 +
  108 + step 'I fill Slack settings' do
  109 + check 'Active'
  110 + fill_in 'Subdomain', with: 'gitlab'
  111 + fill_in 'Room', with: '#gitlab'
  112 + fill_in 'Token', with: 'verySecret'
  113 + click_button 'Save'
  114 + end
  115 +
  116 + step 'I should see Slack service settings saved' do
  117 + find_field('Subdomain').value.should == 'gitlab'
  118 + find_field('Room').value.should == '#gitlab'
  119 + find_field('Token').value.should == 'verySecret'
  120 + end
103 end 121 end
features/steps/public/projects.rb
@@ -4,7 +4,7 @@ class Spinach::Features::PublicProjectsFeature &lt; Spinach::FeatureSteps @@ -4,7 +4,7 @@ class Spinach::Features::PublicProjectsFeature &lt; Spinach::FeatureSteps
4 include SharedProject 4 include SharedProject
5 5
6 step 'public empty project "Empty Public Project"' do 6 step 'public empty project "Empty Public Project"' do
7 - create :empty_project, name: 'Empty Public Project', visibility_level: Gitlab::VisibilityLevel::PUBLIC 7 + create :empty_project, :public, name: 'Empty Public Project'
8 end 8 end
9 9
10 step 'I should see project "Empty Public Project"' do 10 step 'I should see project "Empty Public Project"' do
features/steps/shared/project.rb
@@ -79,7 +79,7 @@ module SharedProject @@ -79,7 +79,7 @@ module SharedProject
79 end 79 end
80 80
81 step 'internal project "Internal"' do 81 step 'internal project "Internal"' do
82 - create :project, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL 82 + create :project, :internal, name: 'Internal'
83 end 83 end
84 84
85 step 'I should see project "Internal"' do 85 step 'I should see project "Internal"' do
@@ -91,7 +91,7 @@ module SharedProject @@ -91,7 +91,7 @@ module SharedProject
91 end 91 end
92 92
93 step 'public project "Community"' do 93 step 'public project "Community"' do
94 - create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC 94 + create :project, :public, name: 'Community'
95 end 95 end
96 96
97 step 'I should see project "Community"' do 97 step 'I should see project "Community"' do
@@ -112,14 +112,14 @@ module SharedProject @@ -112,14 +112,14 @@ module SharedProject
112 step '"John Doe" is authorized to internal project "Internal"' do 112 step '"John Doe" is authorized to internal project "Internal"' do
113 user = user_exists("John Doe", username: "john_doe") 113 user = user_exists("John Doe", username: "john_doe")
114 project = Project.find_by(name: "Internal") 114 project = Project.find_by(name: "Internal")
115 - project ||= create :project, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL 115 + project ||= create :project, :internal, name: 'Internal'
116 project.team << [user, :master] 116 project.team << [user, :master]
117 end 117 end
118 118
119 step '"John Doe" is authorized to public project "Community"' do 119 step '"John Doe" is authorized to public project "Community"' do
120 user = user_exists("John Doe", username: "john_doe") 120 user = user_exists("John Doe", username: "john_doe")
121 project = Project.find_by(name: "Community") 121 project = Project.find_by(name: "Community")
122 - project ||= create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC 122 + project ||= create :project, :public, name: 'Community'
123 project.team << [user, :master] 123 project.team << [user, :master]
124 end 124 end
125 end 125 end
lib/api/entities.rb
@@ -187,5 +187,9 @@ module API @@ -187,5 +187,9 @@ module API
187 end 187 end
188 end 188 end
189 end 189 end
  190 +
  191 + class Label < Grape::Entity
  192 + expose :name
  193 + end
190 end 194 end
191 end 195 end
lib/api/internal.rb
1 module API 1 module API
2 # Internal access API 2 # Internal access API
3 class Internal < Grape::API 3 class Internal < Grape::API
4 -  
5 - DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }  
6 - PUSH_COMMANDS = %w{ git-receive-pack }  
7 -  
8 namespace 'internal' do 4 namespace 'internal' do
9 - #  
10 - # Check if ssh key has access to project code 5 + # Check if git command is allowed to project
11 # 6 #
12 # Params: 7 # Params:
13 - # key_id - SSH Key id 8 + # key_id - ssh key id for Git over SSH
  9 + # user_id - user id for Git over HTTP
14 # project - project path with namespace 10 # project - project path with namespace
15 # action - git action (git-upload-pack or git-receive-pack) 11 # action - git action (git-upload-pack or git-receive-pack)
16 # ref - branch name 12 # ref - branch name
@@ -22,43 +18,25 @@ module API @@ -22,43 +18,25 @@ module API
22 # the wiki repository as well. 18 # the wiki repository as well.
23 project_path = params[:project] 19 project_path = params[:project]
24 project_path.gsub!(/\.wiki/,'') if project_path =~ /\.wiki/ 20 project_path.gsub!(/\.wiki/,'') if project_path =~ /\.wiki/
25 -  
26 - key = Key.find(params[:key_id])  
27 project = Project.find_with_namespace(project_path) 21 project = Project.find_with_namespace(project_path)
28 - git_cmd = params[:action]  
29 return false unless project 22 return false unless project
30 23
31 -  
32 - if key.is_a? DeployKey  
33 - key.projects.include?(project) && DOWNLOAD_COMMANDS.include?(git_cmd)  
34 - else  
35 - user = key.user  
36 -  
37 - return false if user.blocked?  
38 -  
39 - if Gitlab.config.ldap.enabled  
40 - if user.ldap_user?  
41 - # Check if LDAP user exists and match LDAP user_filter  
42 - unless Gitlab::LDAP::Access.new.allowed?(user)  
43 - return false  
44 - end  
45 - end  
46 - end  
47 -  
48 - action = case git_cmd  
49 - when *DOWNLOAD_COMMANDS  
50 - then :download_code  
51 - when *PUSH_COMMANDS  
52 - then  
53 - if project.protected_branch?(params[:ref])  
54 - :push_code_to_protected_branches  
55 - else  
56 - :push_code  
57 - end  
58 - end  
59 -  
60 - user.can?(action, project)  
61 - end 24 + actor = if params[:key_id]
  25 + Key.find(params[:key_id])
  26 + elsif params[:user_id]
  27 + User.find(params[:user_id])
  28 + end
  29 +
  30 + return false unless actor
  31 +
  32 + Gitlab::GitAccess.new.allowed?(
  33 + actor,
  34 + params[:action],
  35 + project,
  36 + params[:ref],
  37 + params[:oldrev],
  38 + params[:newrev]
  39 + )
62 end 40 end
63 41
64 # 42 #
lib/api/merge_requests.rb
@@ -125,6 +125,22 @@ module API @@ -125,6 +125,22 @@ module API
125 end 125 end
126 end 126 end
127 127
  128 + # Get a merge request's comments
  129 + #
  130 + # Parameters:
  131 + # id (required) - The ID of a project
  132 + # merge_request_id (required) - ID of MR
  133 + # Examples:
  134 + # GET /projects/:id/merge_request/:merge_request_id/comments
  135 + #
  136 + get ":id/merge_request/:merge_request_id/comments" do
  137 + merge_request = user_project.merge_requests.find(params[:merge_request_id])
  138 +
  139 + authorize! :read_merge_request, merge_request
  140 +
  141 + present paginate(merge_request.notes), with: Entities::MRNote
  142 + end
  143 +
128 # Post comment to merge request 144 # Post comment to merge request
129 # 145 #
130 # Parameters: 146 # Parameters:
lib/api/projects.rb
@@ -215,6 +215,17 @@ module API @@ -215,6 +215,17 @@ module API
215 @users = paginate @users 215 @users = paginate @users
216 present @users, with: Entities::User 216 present @users, with: Entities::User
217 end 217 end
  218 +
  219 + # Get a project labels
  220 + #
  221 + # Parameters:
  222 + # id (required) - The ID of a project
  223 + # Example Request:
  224 + # GET /projects/:id/labels
  225 + get ':id/labels' do
  226 + @labels = user_project.issues_labels
  227 + present @labels, with: Entities::Label
  228 + end
218 end 229 end
219 end 230 end
220 end 231 end
lib/gitlab/backend/grack_auth.rb
1 require_relative 'shell_env' 1 require_relative 'shell_env'
2 -require_relative 'grack_helpers'  
3 2
4 module Grack 3 module Grack
5 class Auth < Rack::Auth::Basic 4 class Auth < Rack::Auth::Basic
6 - include Helpers  
7 5
8 - attr_accessor :user, :project, :ref, :env 6 + attr_accessor :user, :project, :env
9 7
10 def call(env) 8 def call(env)
11 @env = env 9 @env = env
@@ -24,14 +22,16 @@ module Grack @@ -24,14 +22,16 @@ module Grack
24 22
25 @env['SCRIPT_NAME'] = "" 23 @env['SCRIPT_NAME'] = ""
26 24
27 - auth! 25 + if project
  26 + auth!
  27 + else
  28 + render_not_found
  29 + end
28 end 30 end
29 31
30 private 32 private
31 33
32 def auth! 34 def auth!
33 - return render_not_found unless project  
34 -  
35 if @auth.provided? 35 if @auth.provided?
36 return bad_request unless @auth.basic? 36 return bad_request unless @auth.basic?
37 37
@@ -40,12 +40,8 @@ module Grack @@ -40,12 +40,8 @@ module Grack
40 40
41 # Allow authentication for GitLab CI service 41 # Allow authentication for GitLab CI service
42 # if valid token passed 42 # if valid token passed
43 - if login == "gitlab-ci-token" && project.gitlab_ci?  
44 - token = project.gitlab_ci_service.token  
45 -  
46 - if token.present? && token == password && service_name == 'git-upload-pack'  
47 - return @app.call(env)  
48 - end 43 + if gitlab_ci_request?(login, password)
  44 + return @app.call(env)
49 end 45 end
50 46
51 @user = authenticate_user(login, password) 47 @user = authenticate_user(login, password)
@@ -53,23 +49,26 @@ module Grack @@ -53,23 +49,26 @@ module Grack
53 if @user 49 if @user
54 Gitlab::ShellEnv.set_env(@user) 50 Gitlab::ShellEnv.set_env(@user)
55 @env['REMOTE_USER'] = @auth.username 51 @env['REMOTE_USER'] = @auth.username
56 - else  
57 - return unauthorized  
58 end 52 end
59 -  
60 - else  
61 - return unauthorized unless project.public?  
62 end 53 end
63 54
64 - if authorized_git_request? 55 + if authorized_request?
65 @app.call(env) 56 @app.call(env)
66 else 57 else
67 unauthorized 58 unauthorized
68 end 59 end
69 end 60 end
70 61
71 - def authorized_git_request?  
72 - authorize_request(service_name) 62 + def gitlab_ci_request?(login, password)
  63 + if login == "gitlab-ci-token" && project.gitlab_ci?
  64 + token = project.gitlab_ci_service.token
  65 +
  66 + if token.present? && token == password && git_cmd == 'git-upload-pack'
  67 + return true
  68 + end
  69 + end
  70 +
  71 + false
73 end 72 end
74 73
75 def authenticate_user(login, password) 74 def authenticate_user(login, password)
@@ -77,31 +76,31 @@ module Grack @@ -77,31 +76,31 @@ module Grack
77 auth.find(login, password) 76 auth.find(login, password)
78 end 77 end
79 78
80 - def authorize_request(service)  
81 - case service  
82 - when 'git-upload-pack'  
83 - can?(user, :download_code, project)  
84 - when'git-receive-pack'  
85 - refs.each do |ref|  
86 - action = if project.protected_branch?(ref)  
87 - :push_code_to_protected_branches  
88 - else  
89 - :push_code  
90 - end  
91 -  
92 - return false unless can?(user, action, project) 79 + def authorized_request?
  80 + case git_cmd
  81 + when *Gitlab::GitAccess::DOWNLOAD_COMMANDS
  82 + if user
  83 + Gitlab::GitAccess.new.download_allowed?(user, project)
  84 + elsif project.public?
  85 + # Allow clone/fetch for public projects
  86 + true
  87 + else
  88 + false
  89 + end
  90 + when *Gitlab::GitAccess::PUSH_COMMANDS
  91 + if user
  92 + # Skip user authorization on upload request.
  93 + # It will be serverd by update hook in repository
  94 + true
  95 + else
  96 + false
93 end 97 end
94 -  
95 - # Never let git-receive-pack trough unauthenticated; it's  
96 - # harmless but git < 1.8 doesn't like it  
97 - return false if user.nil?  
98 - true  
99 else 98 else
100 false 99 false
101 end 100 end
102 end 101 end
103 102
104 - def service_name 103 + def git_cmd
105 if @request.get? 104 if @request.get?
106 @request.params['service'] 105 @request.params['service']
107 elsif @request.post? 106 elsif @request.post?
@@ -115,28 +114,17 @@ module Grack @@ -115,28 +114,17 @@ module Grack
115 @project ||= project_by_path(@request.path_info) 114 @project ||= project_by_path(@request.path_info)
116 end 115 end
117 116
118 - def refs  
119 - @refs ||= parse_refs  
120 - end  
121 -  
122 - def parse_refs  
123 - input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/  
124 - Zlib::GzipReader.new(@request.body).read  
125 - else  
126 - @request.body.read  
127 - end  
128 -  
129 - # Need to reset seek point  
130 - @request.body.rewind  
131 -  
132 - # Parse refs  
133 - refs = input.force_encoding('ascii-8bit').scan(/refs\/heads\/([\/\w\.-]+)/n).flatten.compact 117 + def project_by_path(path)
  118 + if m = /^([\w\.\/-]+)\.git/.match(path).to_a
  119 + path_with_namespace = m.last
  120 + path_with_namespace.gsub!(/\.wiki$/, '')
134 121
135 - # Cleanup grabare from refs  
136 - # if push to multiple branches  
137 - refs.map do |ref|  
138 - ref.gsub(/00.*/, "") 122 + Project.find_with_namespace(path_with_namespace)
139 end 123 end
140 end 124 end
  125 +
  126 + def render_not_found
  127 + [404, {"Content-Type" => "text/plain"}, ["Not Found"]]
  128 + end
141 end 129 end
142 end 130 end
lib/gitlab/backend/grack_helpers.rb
@@ -1,28 +0,0 @@ @@ -1,28 +0,0 @@
1 -module Grack  
2 - module Helpers  
3 - def project_by_path(path)  
4 - if m = /^([\w\.\/-]+)\.git/.match(path).to_a  
5 - path_with_namespace = m.last  
6 - path_with_namespace.gsub!(/\.wiki$/, '')  
7 -  
8 - Project.find_with_namespace(path_with_namespace)  
9 - end  
10 - end  
11 -  
12 - def render_not_found  
13 - [404, {"Content-Type" => "text/plain"}, ["Not Found"]]  
14 - end  
15 -  
16 - def can?(object, action, subject)  
17 - abilities.allowed?(object, action, subject)  
18 - end  
19 -  
20 - def abilities  
21 - @abilities ||= begin  
22 - abilities = Six.new  
23 - abilities << Ability  
24 - abilities  
25 - end  
26 - end  
27 - end  
28 -end  
lib/gitlab/git_access.rb 0 → 100644
@@ -0,0 +1,74 @@ @@ -0,0 +1,74 @@
  1 +module Gitlab
  2 + class GitAccess
  3 + DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }
  4 + PUSH_COMMANDS = %w{ git-receive-pack }
  5 +
  6 + attr_reader :params, :project, :git_cmd, :user
  7 +
  8 + def allowed?(actor, cmd, project, ref = nil, oldrev = nil, newrev = nil)
  9 + case cmd
  10 + when *DOWNLOAD_COMMANDS
  11 + if actor.is_a? User
  12 + download_allowed?(actor, project)
  13 + elsif actor.is_a? DeployKey
  14 + actor.projects.include?(project)
  15 + elsif actor.is_a? Key
  16 + download_allowed?(actor.user, project)
  17 + else
  18 + raise 'Wrong actor'
  19 + end
  20 + when *PUSH_COMMANDS
  21 + if actor.is_a? User
  22 + push_allowed?(actor, project, ref, oldrev, newrev)
  23 + elsif actor.is_a? DeployKey
  24 + # Deploy key not allowed to push
  25 + return false
  26 + elsif actor.is_a? Key
  27 + push_allowed?(actor.user, project, ref, oldrev, newrev)
  28 + else
  29 + raise 'Wrong actor'
  30 + end
  31 + else
  32 + false
  33 + end
  34 + end
  35 +
  36 + def download_allowed?(user, project)
  37 + if user && user_allowed?(user)
  38 + user.can?(:download_code, project)
  39 + else
  40 + false
  41 + end
  42 + end
  43 +
  44 + def push_allowed?(user, project, ref, oldrev, newrev)
  45 + if user && user_allowed?(user)
  46 + action = if project.protected_branch?(ref)
  47 + :push_code_to_protected_branches
  48 + else
  49 + :push_code
  50 + end
  51 + user.can?(action, project)
  52 + else
  53 + false
  54 + end
  55 + end
  56 +
  57 + private
  58 +
  59 + def user_allowed?(user)
  60 + return false if user.blocked?
  61 +
  62 + if Gitlab.config.ldap.enabled
  63 + if user.ldap_user?
  64 + # Check if LDAP user exists and match LDAP user_filter
  65 + unless Gitlab::LDAP::Access.new.allowed?(user)
  66 + return false
  67 + end
  68 + end
  69 + end
  70 +
  71 + true
  72 + end
  73 + end
  74 +end
lib/gitlab/markdown.rb
@@ -152,7 +152,7 @@ module Gitlab @@ -152,7 +152,7 @@ module Gitlab
152 # 152 #
153 # Returns boolean 153 # Returns boolean
154 def valid_emoji?(emoji) 154 def valid_emoji?(emoji)
155 - Emoji.names.include? emoji 155 + Emoji.find_by_name emoji
156 end 156 end
157 157
158 # Private: Dispatches to a dedicated processing method based on reference 158 # Private: Dispatches to a dedicated processing method based on reference
@@ -166,8 +166,8 @@ module Gitlab @@ -166,8 +166,8 @@ module Gitlab
166 end 166 end
167 167
168 def reference_user(identifier) 168 def reference_user(identifier)
169 - if member = @project.team_members.find { |user| user.username == identifier }  
170 - link_to("@#{identifier}", user_url(identifier), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member 169 + if user = User.find_by_username(identifier)
  170 + link_to("@#{identifier}", user_url(identifier), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}"))
171 end 171 end
172 end 172 end
173 173
lib/gitlab/satellite/satellite.rb
1 module Gitlab 1 module Gitlab
2 - class SatelliteNotExistError < StandardError; end 2 + class SatelliteNotExistError < StandardError
  3 + def initialize(msg = "Satellite doesn't exist")
  4 + super
  5 + end
  6 + end
3 7
4 module Satellite 8 module Satellite
5 class Satellite 9 class Satellite
@@ -17,14 +21,9 @@ module Gitlab @@ -17,14 +21,9 @@ module Gitlab
17 Gitlab::Satellite::Logger.error(message) 21 Gitlab::Satellite::Logger.error(message)
18 end 22 end
19 23
20 - def raise_no_satellite  
21 - raise SatelliteNotExistError.new("Satellite doesn't exist")  
22 - end  
23 -  
24 def clear_and_update! 24 def clear_and_update!
25 - raise_no_satellite unless exists? 25 + raise SatelliteNotExistError unless exists?
26 26
27 - File.exists? path  
28 @repo = nil 27 @repo = nil
29 clear_working_dir! 28 clear_working_dir!
30 delete_heads! 29 delete_heads!
@@ -55,7 +54,7 @@ module Gitlab @@ -55,7 +54,7 @@ module Gitlab
55 # * Changes the current directory to the satellite's working dir 54 # * Changes the current directory to the satellite's working dir
56 # * Yields 55 # * Yields
57 def lock 56 def lock
58 - raise_no_satellite unless exists? 57 + raise SatelliteNotExistError unless exists?
59 58
60 File.open(lock_file, "w+") do |f| 59 File.open(lock_file, "w+") do |f|
61 begin 60 begin
@@ -77,7 +76,7 @@ module Gitlab @@ -77,7 +76,7 @@ module Gitlab
77 end 76 end
78 77
79 def repo 78 def repo
80 - raise_no_satellite unless exists? 79 + raise SatelliteNotExistError unless exists?
81 80
82 @repo ||= Grit::Repo.new(path) 81 @repo ||= Grit::Repo.new(path)
83 end 82 end
lib/gitlab/upgrader.rb
  1 +require_relative "popen"
1 require_relative "version_info" 2 require_relative "version_info"
2 3
3 module Gitlab 4 module Gitlab
lib/support/init.d/gitlab
@@ -40,7 +40,7 @@ test -f /etc/default/gitlab &amp;&amp; . /etc/default/gitlab @@ -40,7 +40,7 @@ test -f /etc/default/gitlab &amp;&amp; . /etc/default/gitlab
40 40
41 # Switch to the app_user if it is not he/she who is running the script. 41 # Switch to the app_user if it is not he/she who is running the script.
42 if [ "$USER" != "$app_user" ]; then 42 if [ "$USER" != "$app_user" ]; then
43 - sudo -u "$app_user" -H -i $0 "$@"; exit; 43 + eval su - "$app_user" -c $(echo \")$0 "$@"$(echo \"); exit;
44 fi 44 fi
45 45
46 # Switch to the gitlab path, exit on failure. 46 # Switch to the gitlab path, exit on failure.
@@ -131,7 +131,7 @@ check_stale_pids(){ @@ -131,7 +131,7 @@ check_stale_pids(){
131 fi 131 fi
132 fi 132 fi
133 if [ "$spid" != "0" -a "$sidekiq_status" != "0" ]; then 133 if [ "$spid" != "0" -a "$sidekiq_status" != "0" ]; then
134 - echo "Removing stale Sidekiq web server pid. This is most likely caused by the Sidekiq crashing the last time it ran." 134 + echo "Removing stale Sidekiq job dispatcher pid. This is most likely caused by Sidekiq crashing the last time it ran."
135 if ! rm "$sidekiq_pid_path"; then 135 if ! rm "$sidekiq_pid_path"; then
136 echo "Unable to remove stale pid, exiting" 136 echo "Unable to remove stale pid, exiting"
137 exit 1 137 exit 1
@@ -149,15 +149,15 @@ exit_if_not_running(){ @@ -149,15 +149,15 @@ exit_if_not_running(){
149 } 149 }
150 150
151 ## Starts Unicorn and Sidekiq if they're not running. 151 ## Starts Unicorn and Sidekiq if they're not running.
152 -start() { 152 +start_gitlab() {
153 check_stale_pids 153 check_stale_pids
154 154
155 if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then 155 if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then
156 echo -n "Starting both the GitLab Unicorn and Sidekiq" 156 echo -n "Starting both the GitLab Unicorn and Sidekiq"
157 elif [ "$web_status" != "0" ]; then 157 elif [ "$web_status" != "0" ]; then
158 - echo -n "Starting GitLab Sidekiq"  
159 - elif [ "$sidekiq_status" != "0" ]; then  
160 echo -n "Starting GitLab Unicorn" 158 echo -n "Starting GitLab Unicorn"
  159 + elif [ "$sidekiq_status" != "0" ]; then
  160 + echo -n "Starting GitLab Sidekiq"
161 fi 161 fi
162 162
163 # Then check if the service is running. If it is: don't start again. 163 # Then check if the service is running. If it is: don't start again.
@@ -167,7 +167,7 @@ start() { @@ -167,7 +167,7 @@ start() {
167 # Remove old socket if it exists 167 # Remove old socket if it exists
168 rm -f "$socket_path"/gitlab.socket 2>/dev/null 168 rm -f "$socket_path"/gitlab.socket 2>/dev/null
169 # Start the web server 169 # Start the web server
170 - RAILS_ENV=$RAILS_ENV script/web start & 170 + RAILS_ENV=$RAILS_ENV script/web start
171 fi 171 fi
172 172
173 # If sidekiq is already running, don't start it again. 173 # If sidekiq is already running, don't start it again.
@@ -184,15 +184,15 @@ start() { @@ -184,15 +184,15 @@ start() {
184 } 184 }
185 185
186 ## Asks the Unicorn and the Sidekiq if they would be so kind as to stop, if not kills them. 186 ## Asks the Unicorn and the Sidekiq if they would be so kind as to stop, if not kills them.
187 -stop() { 187 +stop_gitlab() {
188 exit_if_not_running 188 exit_if_not_running
189 189
190 if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then 190 if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then
191 echo -n "Shutting down both Unicorn and Sidekiq" 191 echo -n "Shutting down both Unicorn and Sidekiq"
192 elif [ "$web_status" = "0" ]; then 192 elif [ "$web_status" = "0" ]; then
193 - echo -n "Shutting down Sidekiq"  
194 - elif [ "$sidekiq_status" = "0" ]; then  
195 echo -n "Shutting down Unicorn" 193 echo -n "Shutting down Unicorn"
  194 + elif [ "$sidekiq_status" = "0" ]; then
  195 + echo -n "Shutting down Sidekiq"
196 fi 196 fi
197 197
198 # If the Unicorn web server is running, tell it to stop; 198 # If the Unicorn web server is running, tell it to stop;
@@ -246,7 +246,7 @@ print_status() { @@ -246,7 +246,7 @@ print_status() {
246 } 246 }
247 247
248 ## Tells unicorn to reload it's config and Sidekiq to restart 248 ## Tells unicorn to reload it's config and Sidekiq to restart
249 -reload(){ 249 +reload_gitlab(){
250 exit_if_not_running 250 exit_if_not_running
251 if [ "$wpid" = "0" ];then 251 if [ "$wpid" = "0" ];then
252 echo "The GitLab Unicorn Web server is not running thus its configuration can't be reloaded." 252 echo "The GitLab Unicorn Web server is not running thus its configuration can't be reloaded."
@@ -263,12 +263,12 @@ reload(){ @@ -263,12 +263,12 @@ reload(){
263 } 263 }
264 264
265 ## Restarts Sidekiq and Unicorn. 265 ## Restarts Sidekiq and Unicorn.
266 -restart(){ 266 +restart_gitlab(){
267 check_status 267 check_status
268 if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then 268 if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then
269 - stop 269 + stop_gitlab
270 fi 270 fi
271 - start 271 + start_gitlab
272 } 272 }
273 273
274 274
@@ -276,16 +276,16 @@ restart(){ @@ -276,16 +276,16 @@ restart(){
276 276
277 case "$1" in 277 case "$1" in
278 start) 278 start)
279 - start 279 + start_gitlab
280 ;; 280 ;;
281 stop) 281 stop)
282 - stop 282 + stop_gitlab
283 ;; 283 ;;
284 restart) 284 restart)
285 - restart 285 + restart_gitlab
286 ;; 286 ;;
287 reload|force-reload) 287 reload|force-reload)
288 - reload 288 + reload_gitlab
289 ;; 289 ;;
290 status) 290 status)
291 print_status 291 print_status
lib/support/nginx/gitlab
@@ -54,6 +54,14 @@ server { @@ -54,6 +54,14 @@ server {
54 proxy_pass http://gitlab; 54 proxy_pass http://gitlab;
55 } 55 }
56 56
  57 + # Enable gzip compression as per rails guide: http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
  58 + location ~ ^/(assets)/ {
  59 + root /home/git/gitlab/public;
  60 + gzip_static on; # to serve pre-gzipped version
  61 + expires max;
  62 + add_header Cache-Control public;
  63 + }
  64 +
57 error_page 502 /502.html; 65 error_page 502 /502.html;
58 } 66 }
59 67
lib/tasks/gitlab/check.rake
@@ -677,7 +677,20 @@ namespace :gitlab do @@ -677,7 +677,20 @@ namespace :gitlab do
677 end 677 end
678 678
679 def filter 679 def filter
680 - Net::LDAP::Filter.present?(ldap_config.uid) 680 + uid_filter = Net::LDAP::Filter.present?(ldap_config.uid)
  681 + if user_filter
  682 + Net::LDAP::Filter.join(uid_filter, user_filter)
  683 + else
  684 + uid_filter
  685 + end
  686 + end
  687 +
  688 + def user_filter
  689 + if ldap_config['user_filter'] && ldap_config.user_filter.present?
  690 + Net::LDAP::Filter.construct(ldap_config.user_filter)
  691 + else
  692 + nil
  693 + end
681 end 694 end
682 695
683 def ldap 696 def ldap
@@ -742,7 +755,7 @@ namespace :gitlab do @@ -742,7 +755,7 @@ namespace :gitlab do
742 end 755 end
743 756
744 def check_gitlab_shell 757 def check_gitlab_shell
745 - required_version = Gitlab::VersionInfo.new(1, 8, 5) 758 + required_version = Gitlab::VersionInfo.new(1, 9, 1)
746 current_version = Gitlab::VersionInfo.parse(gitlab_shell_version) 759 current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
747 760
748 print "GitLab Shell version >= #{required_version} ? ... " 761 print "GitLab Shell version >= #{required_version} ? ... "
script/background_jobs
@@ -6,6 +6,11 @@ sidekiq_pidfile=&quot;$app_root/tmp/pids/sidekiq.pid&quot; @@ -6,6 +6,11 @@ sidekiq_pidfile=&quot;$app_root/tmp/pids/sidekiq.pid&quot;
6 sidekiq_logfile="$app_root/log/sidekiq.log" 6 sidekiq_logfile="$app_root/log/sidekiq.log"
7 gitlab_user=$(ls -l config.ru | awk '{print $3}') 7 gitlab_user=$(ls -l config.ru | awk '{print $3}')
8 8
  9 +function warn
  10 +{
  11 + echo "$@" 1>&2
  12 +}
  13 +
9 function stop 14 function stop
10 { 15 {
11 bundle exec sidekiqctl stop $sidekiq_pidfile >> $sidekiq_logfile 2>&1 16 bundle exec sidekiqctl stop $sidekiq_pidfile >> $sidekiq_logfile 2>&1
@@ -35,6 +40,22 @@ function start_sidekiq @@ -35,6 +40,22 @@ function start_sidekiq
35 bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1 40 bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1
36 } 41 }
37 42
  43 +function load_ok
  44 +{
  45 + sidekiq_pid=$(cat $sidekiq_pidfile)
  46 + if [[ -z $sidekiq_pid ]] ; then
  47 + warn "Could not find a PID in $sidekiq_pidfile"
  48 + exit 0
  49 + fi
  50 +
  51 + if (ps -p $sidekiq_pid -o args | grep '\([0-9]\+\) of \1 busy' 1>&2) ; then
  52 + warn "Too many busy Sidekiq workers"
  53 + exit 1
  54 + fi
  55 +
  56 + exit 0
  57 +}
  58 +
38 case "$1" in 59 case "$1" in
39 stop) 60 stop)
40 stop 61 stop
@@ -51,6 +72,9 @@ case &quot;$1&quot; in @@ -51,6 +72,9 @@ case &quot;$1&quot; in
51 killall) 72 killall)
52 killall 73 killall
53 ;; 74 ;;
  75 + load_ok)
  76 + load_ok
  77 + ;;
54 *) 78 *)
55 - echo "Usage: RAILS_ENV=your_env $0 {stop|start|start_no_deamonize|restart|killall}" 79 + echo "Usage: RAILS_ENV=your_env $0 {stop|start|start_no_deamonize|restart|killall|load_ok}"
56 esac 80 esac
spec/controllers/profile_keys_controller_spec.rb
@@ -24,6 +24,11 @@ describe Profiles::KeysController do @@ -24,6 +24,11 @@ describe Profiles::KeysController do
24 24
25 expect(response.body).to eq("") 25 expect(response.body).to eq("")
26 end 26 end
  27 +
  28 + it "should respond with text/plain content type" do
  29 + get :get_keys, username: user.username
  30 + expect(response.content_type).to eq("text/plain")
  31 + end
27 end 32 end
28 33
29 describe "user with keys" do 34 describe "user with keys" do
@@ -44,6 +49,11 @@ describe Profiles::KeysController do @@ -44,6 +49,11 @@ describe Profiles::KeysController do
44 expect(response.body).not_to eq("") 49 expect(response.body).not_to eq("")
45 expect(response.body).to eq(user.all_ssh_keys.join("\n")) 50 expect(response.body).to eq(user.all_ssh_keys.join("\n"))
46 end 51 end
  52 +
  53 + it "should respond with text/plain content type" do
  54 + get :get_keys, username: user.username
  55 + expect(response.content_type).to eq("text/plain")
  56 + end
47 end 57 end
48 end 58 end
49 end 59 end
spec/factories.rb
@@ -32,6 +32,18 @@ FactoryGirl.define do @@ -32,6 +32,18 @@ FactoryGirl.define do
32 path { name.downcase.gsub(/\s/, '_') } 32 path { name.downcase.gsub(/\s/, '_') }
33 namespace 33 namespace
34 creator 34 creator
  35 +
  36 + trait :public do
  37 + visibility_level Gitlab::VisibilityLevel::PUBLIC
  38 + end
  39 +
  40 + trait :internal do
  41 + visibility_level Gitlab::VisibilityLevel::INTERNAL
  42 + end
  43 +
  44 + trait :private do
  45 + visibility_level Gitlab::VisibilityLevel::PRIVATE
  46 + end
35 end 47 end
36 48
37 # Generates a test repository from the repository stored under `spec/seed_project.tar.gz`. 49 # Generates a test repository from the repository stored under `spec/seed_project.tar.gz`.
@@ -146,6 +158,11 @@ FactoryGirl.define do @@ -146,6 +158,11 @@ FactoryGirl.define do
146 state :reopened 158 state :reopened
147 end 159 end
148 160
  161 + trait :simple do
  162 + source_branch "simple_merge_request"
  163 + target_branch "master"
  164 + end
  165 +
149 factory :closed_merge_request, traits: [:closed] 166 factory :closed_merge_request, traits: [:closed]
150 factory :reopened_merge_request, traits: [:reopened] 167 factory :reopened_merge_request, traits: [:reopened]
151 factory :merge_request_with_diffs, traits: [:with_diffs] 168 factory :merge_request_with_diffs, traits: [:with_diffs]
@@ -161,7 +178,6 @@ FactoryGirl.define do @@ -161,7 +178,6 @@ FactoryGirl.define do
161 factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note] 178 factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note]
162 factory :note_on_merge_request, traits: [:on_merge_request] 179 factory :note_on_merge_request, traits: [:on_merge_request]
163 factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff] 180 factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff]
164 - factory :note_on_merge_request_with_attachment, traits: [:on_merge_request, :with_attachment]  
165 181
166 trait :on_commit do 182 trait :on_commit do
167 project factory: :project 183 project factory: :project
spec/features/notes_on_merge_requests_spec.rb
1 require 'spec_helper' 1 require 'spec_helper'
2 2
3 describe "On a merge request", js: true do 3 describe "On a merge request", js: true do
4 - let!(:project) { create(:project) }  
5 - let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }  
6 - let!(:note) { create(:note_on_merge_request_with_attachment, project: project) } 4 + let!(:merge_request) { create(:merge_request, :simple) }
  5 + let!(:project) { merge_request.source_project }
  6 + let!(:note) { create(:note_on_merge_request, :with_attachment, project: project) }
7 7
8 before do 8 before do
9 - login_as :user  
10 - project.team << [@user, :master]  
11 - 9 + login_as :admin
12 visit project_merge_request_path(project, merge_request) 10 visit project_merge_request_path(project, merge_request)
13 end 11 end
14 12
@@ -134,22 +132,20 @@ describe &quot;On a merge request&quot;, js: true do @@ -134,22 +132,20 @@ describe &quot;On a merge request&quot;, js: true do
134 end 132 end
135 end 133 end
136 134
137 -describe "On a merge request diff", js: true, focus: true do  
138 - let!(:project) { create(:project) }  
139 - let!(:merge_request) { create(:merge_request_with_diffs, source_project: project, target_project: project) } 135 +describe "On a merge request diff", js: true do
  136 + let(:merge_request) { create(:merge_request, :with_diffs, :simple) }
  137 + let(:project) { merge_request.source_project }
140 138
141 before do 139 before do
142 - login_as :user  
143 - project.team << [@user, :master] 140 + login_as :admin
144 visit diffs_project_merge_request_path(project, merge_request) 141 visit diffs_project_merge_request_path(project, merge_request)
145 end 142 end
146 143
147 -  
148 subject { page } 144 subject { page }
149 145
150 describe "when adding a note" do 146 describe "when adding a note" do
151 before do 147 before do
152 - find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185"]').click 148 + find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7"]').click
153 end 149 end
154 150
155 describe "the notes holder" do 151 describe "the notes holder" do
@@ -160,13 +156,13 @@ describe &quot;On a merge request diff&quot;, js: true, focus: true do @@ -160,13 +156,13 @@ describe &quot;On a merge request diff&quot;, js: true, focus: true do
160 156
161 describe "the note form" do 157 describe "the note form" do
162 it "shouldn't add a second form for same row" do 158 it "shouldn't add a second form for same row" do
163 - find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185"]').click 159 + find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7"]').click
164 160
165 - should have_css("tr[id='4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185'] + .js-temp-notes-holder form", count: 1) 161 + should have_css("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7'] + .js-temp-notes-holder form", count: 1)
166 end 162 end
167 163
168 it "should be removed when canceled" do 164 it "should be removed when canceled" do
169 - within(".diff-file form[rel$='4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185']") do 165 + within(".diff-file form[rel$='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7']") do
170 find(".js-close-discussion-note-form").trigger("click") 166 find(".js-close-discussion-note-form").trigger("click")
171 end 167 end
172 168
@@ -176,12 +172,9 @@ describe &quot;On a merge request diff&quot;, js: true, focus: true do @@ -176,12 +172,9 @@ describe &quot;On a merge request diff&quot;, js: true, focus: true do
176 end 172 end
177 173
178 describe "with muliple note forms" do 174 describe "with muliple note forms" do
179 - let!(:project) { create(:project) }  
180 - let!(:merge_request) { create(:merge_request_with_diffs, source_project: project, target_project: project) }  
181 -  
182 before do 175 before do
183 - find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185"]').click  
184 - find('a[data-line-code="342e16cbbd482ac2047dc679b2749d248cc1428f_18_17"]').click 176 + find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7"]').click
  177 + find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_10_10"]').click
185 end 178 end
186 179
187 it { should have_css(".js-temp-notes-holder", count: 2) } 180 it { should have_css(".js-temp-notes-holder", count: 2) }
@@ -189,12 +182,12 @@ describe &quot;On a merge request diff&quot;, js: true, focus: true do @@ -189,12 +182,12 @@ describe &quot;On a merge request diff&quot;, js: true, focus: true do
189 describe "previewing them separately" do 182 describe "previewing them separately" do
190 before do 183 before do
191 # add two separate texts and trigger previews on both 184 # add two separate texts and trigger previews on both
192 - within("tr[id='4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185'] + .js-temp-notes-holder") do  
193 - fill_in "note[note]", with: "One comment on line 185" 185 + within("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7'] + .js-temp-notes-holder") do
  186 + fill_in "note[note]", with: "One comment on line 7"
194 find(".js-note-preview-button").trigger("click") 187 find(".js-note-preview-button").trigger("click")
195 end 188 end
196 - within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do  
197 - fill_in "note[note]", with: "Another comment on line 17" 189 + within("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_10_10'] + .js-temp-notes-holder") do
  190 + fill_in "note[note]", with: "Another comment on line 10"
198 find(".js-note-preview-button").trigger("click") 191 find(".js-note-preview-button").trigger("click")
199 end 192 end
200 end 193 end
@@ -202,14 +195,14 @@ describe &quot;On a merge request diff&quot;, js: true, focus: true do @@ -202,14 +195,14 @@ describe &quot;On a merge request diff&quot;, js: true, focus: true do
202 195
203 describe "posting a note" do 196 describe "posting a note" do
204 before do 197 before do
205 - within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do  
206 - fill_in "note[note]", with: "Another comment on line 17" 198 + within("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_10_10'] + .js-temp-notes-holder") do
  199 + fill_in "note[note]", with: "Another comment on line 10"
207 click_button("Add Comment") 200 click_button("Add Comment")
208 end 201 end
209 end 202 end
210 203
211 it 'should be added as discussion' do 204 it 'should be added as discussion' do
212 - should have_content("Another comment on line 17") 205 + should have_content("Another comment on line 10")
213 should have_css(".notes_holder") 206 should have_css(".notes_holder")
214 should have_css(".notes_holder .note", count: 1) 207 should have_css(".notes_holder .note", count: 1)
215 should have_link("Reply") 208 should have_link("Reply")
spec/features/security/group/internal_group_access_spec.rb
@@ -16,7 +16,7 @@ describe &quot;Group with internal project access&quot; do @@ -16,7 +16,7 @@ describe &quot;Group with internal project access&quot; do
16 group.add_user(reporter, Gitlab::Access::REPORTER) 16 group.add_user(reporter, Gitlab::Access::REPORTER)
17 group.add_user(guest, Gitlab::Access::GUEST) 17 group.add_user(guest, Gitlab::Access::GUEST)
18 18
19 - create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) 19 + create(:project, :internal, group: group)
20 end 20 end
21 21
22 describe "GET /groups/:path" do 22 describe "GET /groups/:path" do
spec/features/security/group/mixed_group_access_spec.rb
@@ -16,8 +16,8 @@ describe &quot;Group access&quot; do @@ -16,8 +16,8 @@ describe &quot;Group access&quot; do
16 group.add_user(reporter, Gitlab::Access::REPORTER) 16 group.add_user(reporter, Gitlab::Access::REPORTER)
17 group.add_user(guest, Gitlab::Access::GUEST) 17 group.add_user(guest, Gitlab::Access::GUEST)
18 18
19 - create(:project, path: "internal_project", group: group, visibility_level: Gitlab::VisibilityLevel::INTERNAL)  
20 - create(:project, path: "public_project", group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) 19 + create(:project, :internal, path: "internal_project", group: group)
  20 + create(:project, :public, path: "public_project", group: group)
21 end 21 end
22 22
23 describe "GET /groups/:path" do 23 describe "GET /groups/:path" do
spec/features/security/group/public_group_access_spec.rb
@@ -16,7 +16,7 @@ describe &quot;Group with public project access&quot; do @@ -16,7 +16,7 @@ describe &quot;Group with public project access&quot; do
16 group.add_user(reporter, Gitlab::Access::REPORTER) 16 group.add_user(reporter, Gitlab::Access::REPORTER)
17 group.add_user(guest, Gitlab::Access::GUEST) 17 group.add_user(guest, Gitlab::Access::GUEST)
18 18
19 - create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) 19 + create(:project, :public, group: group)
20 end 20 end
21 21
22 describe "GET /groups/:path" do 22 describe "GET /groups/:path" do
spec/features/security/project/internal_access_spec.rb
1 require 'spec_helper' 1 require 'spec_helper'
2 2
3 describe "Internal Project Access" do 3 describe "Internal Project Access" do
4 - let(:project) { create(:project) } 4 + let(:project) { create(:project, :internal) }
5 5
6 let(:master) { create(:user) } 6 let(:master) { create(:user) }
7 let(:guest) { create(:user) } 7 let(:guest) { create(:user) }
8 let(:reporter) { create(:user) } 8 let(:reporter) { create(:user) }
9 9
10 before do 10 before do
11 - # internal project  
12 - project.visibility_level = Gitlab::VisibilityLevel::INTERNAL  
13 - project.save!  
14 -  
15 # full access 11 # full access
16 project.team << [master, :master] 12 project.team << [master, :master]
17 13
18 # readonly 14 # readonly
19 project.team << [reporter, :reporter] 15 project.team << [reporter, :reporter]
20 -  
21 end 16 end
22 17
23 describe "Project should be internal" do 18 describe "Project should be internal" do
spec/finders/merge_requests_finder_spec.rb
1 require 'spec_helper' 1 require 'spec_helper'
2 2
3 describe MergeRequestsFinder do 3 describe MergeRequestsFinder do
4 - let(:user) { create :user } 4 + let(:user) { create :user }
5 let(:user2) { create :user } 5 let(:user2) { create :user }
  6 +
6 let(:project1) { create(:project) } 7 let(:project1) { create(:project) }
7 let(:project2) { create(:project) } 8 let(:project2) { create(:project) }
8 - let(:merge_request1) { create(:merge_request, author: user, source_project: project1, target_project: project2) }  
9 - let(:merge_request2) { create(:merge_request, author: user, source_project: project2, target_project: project1) }  
10 - let(:merge_request3) { create(:merge_request, author: user, source_project: project2, target_project: project2) } 9 +
  10 + let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project2) }
  11 + let!(:merge_request2) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) }
  12 + let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2) }
11 13
12 before do 14 before do
13 project1.team << [user, :master] 15 project1.team << [user, :master]
@@ -15,13 +17,7 @@ describe MergeRequestsFinder do @@ -15,13 +17,7 @@ describe MergeRequestsFinder do
15 project2.team << [user2, :developer] 17 project2.team << [user2, :developer]
16 end 18 end
17 19
18 - describe :execute do  
19 - before :each do  
20 - merge_request1  
21 - merge_request2  
22 - merge_request3  
23 - end  
24 - 20 + describe "#execute" do
25 it 'should filter by scope' do 21 it 'should filter by scope' do
26 params = { scope: 'authored', state: 'opened' } 22 params = { scope: 'authored', state: 'opened' }
27 merge_requests = MergeRequestsFinder.new.execute(user, params) 23 merge_requests = MergeRequestsFinder.new.execute(user, params)
spec/finders/projects_finder_spec.rb
@@ -4,10 +4,10 @@ describe ProjectsFinder do @@ -4,10 +4,10 @@ describe ProjectsFinder do
4 let(:user) { create :user } 4 let(:user) { create :user }
5 let(:group) { create :group } 5 let(:group) { create :group }
6 6
7 - let(:project1) { create(:empty_project, group: group, visibility_level: Project::PUBLIC) }  
8 - let(:project2) { create(:empty_project, group: group, visibility_level: Project::INTERNAL) }  
9 - let(:project3) { create(:empty_project, group: group, visibility_level: Project::PRIVATE) }  
10 - let(:project4) { create(:empty_project, group: group, visibility_level: Project::PRIVATE) } 7 + let(:project1) { create(:empty_project, :public, group: group) }
  8 + let(:project2) { create(:empty_project, :internal, group: group) }
  9 + let(:project3) { create(:empty_project, :private, group: group) }
  10 + let(:project4) { create(:empty_project, :private, group: group) }
11 11
12 context 'non authenticated' do 12 context 'non authenticated' do
13 subject { ProjectsFinder.new.execute(nil, group: group) } 13 subject { ProjectsFinder.new.execute(nil, group: group) }
spec/mailers/notify_spec.rb
@@ -229,6 +229,7 @@ describe Notify do @@ -229,6 +229,7 @@ describe Notify do
229 end 229 end
230 230
231 context 'for merge requests' do 231 context 'for merge requests' do
  232 + let(:merge_author) { create(:user) }
232 let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) } 233 let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) }
233 let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: Faker::Lorem.sentence) } 234 let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: Faker::Lorem.sentence) }
234 235
@@ -288,7 +289,30 @@ describe Notify do @@ -288,7 +289,30 @@ describe Notify do
288 it 'contains a link to the merge request' do 289 it 'contains a link to the merge request' do
289 should have_body_text /#{project_merge_request_path project, merge_request}/ 290 should have_body_text /#{project_merge_request_path project, merge_request}/
290 end 291 end
  292 + end
  293 +
  294 + describe 'that are merged' do
  295 + subject { Notify.merged_merge_request_email(recipient.id, merge_request.id, merge_author.id) }
  296 +
  297 + it_behaves_like 'a multiple recipients email'
  298 +
  299 + it 'is sent as the merge author' do
  300 + sender = subject.header[:from].addrs[0]
  301 + sender.display_name.should eq(merge_author.name)
  302 + sender.address.should eq(gitlab_sender)
  303 + end
  304 +
  305 + it 'has the correct subject' do
  306 + should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/
  307 + end
291 308
  309 + it 'contains the new status' do
  310 + should have_body_text /merged/i
  311 + end
  312 +
  313 + it 'contains a link to the merge request' do
  314 + should have_body_text /#{project_merge_request_path project, merge_request}/
  315 + end
292 end 316 end
293 end 317 end
294 end 318 end
spec/models/concerns/issuable_spec.rb
@@ -25,11 +25,6 @@ describe Issue, &quot;Issuable&quot; do @@ -25,11 +25,6 @@ describe Issue, &quot;Issuable&quot; do
25 it { described_class.should respond_to(:assigned) } 25 it { described_class.should respond_to(:assigned) }
26 end 26 end
27 27
28 - it "has an :author_id_of_changes accessor" do  
29 - issue.should respond_to(:author_id_of_changes)  
30 - issue.should respond_to(:author_id_of_changes=)  
31 - end  
32 -  
33 describe ".search" do 28 describe ".search" do
34 let!(:searchable_issue) { create(:issue, title: "Searchable issue") } 29 let!(:searchable_issue) { create(:issue, title: "Searchable issue") }
35 30
spec/models/project_spec.rb
@@ -47,6 +47,7 @@ describe Project do @@ -47,6 +47,7 @@ describe Project do
47 it { should have_many(:hooks).dependent(:destroy) } 47 it { should have_many(:hooks).dependent(:destroy) }
48 it { should have_many(:protected_branches).dependent(:destroy) } 48 it { should have_many(:protected_branches).dependent(:destroy) }
49 it { should have_one(:forked_project_link).dependent(:destroy) } 49 it { should have_one(:forked_project_link).dependent(:destroy) }
  50 + it { should have_one(:slack_service).dependent(:destroy) }
50 end 51 end
51 52
52 describe "Mass assignment" do 53 describe "Mass assignment" do
spec/models/slack_message_spec.rb 0 → 100644
@@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
  1 +require_relative '../../app/models/project_services/slack_message'
  2 +
  3 +describe SlackMessage do
  4 + subject { SlackMessage.new(args) }
  5 +
  6 + let(:args) {
  7 + {
  8 + after: 'after',
  9 + before: 'before',
  10 + project_name: 'project_name',
  11 + ref: 'refs/heads/master',
  12 + user_name: 'user_name',
  13 + project_url: 'url'
  14 + }
  15 + }
  16 +
  17 + context 'push' do
  18 + before do
  19 + args[:commits] = [
  20 + { message: 'message1', url: 'url1', id: 'abcdefghi' },
  21 + { message: 'message2', url: 'url2', id: '123456789' },
  22 + ]
  23 + end
  24 +
  25 + it 'returns a message regarding pushes' do
  26 + subject.compose.should ==
  27 + 'user_name pushed to branch <url/commits/master|master> of ' <<
  28 + '<url|project_name> (<url/compare/before...after|Compare changes>)' <<
  29 + "\n - message1 (<url1|abcdef>)" <<
  30 + "\n - message2 (<url2|123456>)"
  31 + end
  32 + end
  33 +
  34 + context 'new branch' do
  35 + before do
  36 + args[:before] = '000000'
  37 + end
  38 +
  39 + it 'returns a message regarding a new branch' do
  40 + subject.compose.should ==
  41 + 'user_name pushed new branch <url/commits/master|master> to ' <<
  42 + '<url|project_name>'
  43 + end
  44 + end
  45 +
  46 + context 'removed branch' do
  47 + before do
  48 + args[:after] = '000000'
  49 + end
  50 +
  51 + it 'returns a message regarding a removed branch' do
  52 + subject.compose.should ==
  53 + 'user_name removed branch master from <url|project_name>'
  54 + end
  55 + end
  56 +end
spec/models/slack_service_spec.rb 0 → 100644
@@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
  1 +# == Schema Information
  2 +#
  3 +# Table name: services
  4 +#
  5 +# id :integer not null, primary key
  6 +# type :string(255)
  7 +# title :string(255)
  8 +# token :string(255)
  9 +# project_id :integer not null
  10 +# created_at :datetime not null
  11 +# updated_at :datetime not null
  12 +# active :boolean default(FALSE), not null
  13 +# project_url :string(255)
  14 +# subdomain :string(255)
  15 +# room :string(255)
  16 +# api_key :string(255)
  17 +#
  18 +
  19 +require 'spec_helper'
  20 +
  21 +describe SlackService do
  22 + describe "Associations" do
  23 + it { should belong_to :project }
  24 + it { should have_one :service_hook }
  25 + end
  26 +
  27 + describe "Validations" do
  28 + context "active" do
  29 + before do
  30 + subject.active = true
  31 + end
  32 +
  33 + it { should validate_presence_of :room }
  34 + it { should validate_presence_of :subdomain }
  35 + it { should validate_presence_of :token }
  36 + end
  37 + end
  38 +
  39 + describe "Execute" do
  40 + let(:slack) { SlackService.new }
  41 + let(:user) { create(:user) }
  42 + let(:project) { create(:project) }
  43 + let(:sample_data) { GitPushService.new.sample_data(project, user) }
  44 + let(:subdomain) { 'gitlab' }
  45 + let(:token) { 'verySecret' }
  46 + let(:api_url) {
  47 + "https://#{subdomain}.slack.com/services/hooks/incoming-webhook?token=#{token}"
  48 + }
  49 +
  50 + before do
  51 + slack.stub(
  52 + project: project,
  53 + project_id: project.id,
  54 + room: '#gitlab',
  55 + service_hook: true,
  56 + subdomain: subdomain,
  57 + token: token
  58 + )
  59 +
  60 + WebMock.stub_request(:post, api_url)
  61 + end
  62 +
  63 + it "should call Slack API" do
  64 + slack.execute(sample_data)
  65 +
  66 + WebMock.should have_requested(:post, api_url).once
  67 + end
  68 + end
  69 +end
spec/models/user_spec.rb
@@ -292,6 +292,20 @@ describe User do @@ -292,6 +292,20 @@ describe User do
292 end 292 end
293 end 293 end
294 294
  295 + describe 'search' do
  296 + let(:user1) { create(:user, username: 'James', email: 'james@testing.com') }
  297 + let(:user2) { create(:user, username: 'jameson', email: 'jameson@example.com') }
  298 +
  299 + it "should be case insensitive" do
  300 + User.search(user1.username.upcase).to_a.should == [user1]
  301 + User.search(user1.username.downcase).to_a.should == [user1]
  302 + User.search(user2.username.upcase).to_a.should == [user2]
  303 + User.search(user2.username.downcase).to_a.should == [user2]
  304 + User.search(user1.username.downcase).to_a.count.should == 2
  305 + User.search(user2.username.downcase).to_a.count.should == 1
  306 + end
  307 + end
  308 +
295 describe 'by_username_or_id' do 309 describe 'by_username_or_id' do
296 let(:user1) { create(:user, username: 'foo') } 310 let(:user1) { create(:user, username: 'foo') }
297 311
spec/observers/activity_observer_spec.rb
@@ -1,61 +0,0 @@ @@ -1,61 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe ActivityObserver do  
4 - let(:project) { create(:project) }  
5 -  
6 - before { Thread.current[:current_user] = create(:user) }  
7 -  
8 - def self.it_should_be_valid_event  
9 - it { @event.should_not be_nil }  
10 - it { @event.project.should == project }  
11 - end  
12 -  
13 - describe "Issue created" do  
14 - before do  
15 - Issue.observers.enable :activity_observer do  
16 - @issue = create(:issue, project: project)  
17 - @event = Event.last  
18 - end  
19 - end  
20 -  
21 - it_should_be_valid_event  
22 - it { @event.action.should == Event::CREATED }  
23 - it { @event.target.should == @issue }  
24 - end  
25 -  
26 - describe "Issue commented" do  
27 - before do  
28 - Note.observers.enable :activity_observer do  
29 - @issue = create(:issue, project: project)  
30 - @note = create(:note, noteable: @issue, project: project, author: @issue.author)  
31 - @event = Event.last  
32 - end  
33 - end  
34 -  
35 - it_should_be_valid_event  
36 - it { @event.action.should == Event::COMMENTED }  
37 - it { @event.target.should == @note }  
38 - end  
39 -  
40 - describe "Ignore system notes" do  
41 - let(:author) { create(:user) }  
42 - let!(:issue) { create(:issue, project: project) }  
43 - let!(:other) { create(:issue) }  
44 -  
45 - it "should not create events for status change notes" do  
46 - expect do  
47 - Note.observers.enable :activity_observer do  
48 - Note.create_status_change_note(issue, project, author, 'reopened', nil)  
49 - end  
50 - end.to_not change { Event.count }  
51 - end  
52 -  
53 - it "should not create events for cross-reference notes" do  
54 - expect do  
55 - Note.observers.enable :activity_observer do  
56 - Note.create_cross_reference_note(issue, other, author, issue.project)  
57 - end  
58 - end.to_not change { Event.count }  
59 - end  
60 - end  
61 -end  
spec/observers/merge_request_observer_spec.rb
@@ -120,7 +120,7 @@ describe MergeRequestObserver do @@ -120,7 +120,7 @@ describe MergeRequestObserver do
120 end 120 end
121 121
122 before do 122 before do
123 - @merge_request = create(:merge_request, source_project: project, source_project: project) 123 + @merge_request = create(:merge_request, source_project: project, target_project: project)
124 @event = Event.last 124 @event = Event.last
125 end 125 end
126 126
spec/requests/api/issues_spec.rb
@@ -100,16 +100,4 @@ describe API::API do @@ -100,16 +100,4 @@ describe API::API do
100 response.status.should == 405 100 response.status.should == 405
101 end 101 end
102 end 102 end
103 -  
104 - describe "PUT /projects/:id/issues/:issue_id to test observer on close" do  
105 - before { enable_observers }  
106 - after { disable_observers }  
107 -  
108 - it "should create an activity event when an issue is closed" do  
109 - Event.should_receive(:create)  
110 -  
111 - put api("/projects/#{project.id}/issues/#{issue.id}", user),  
112 - state_event: "close"  
113 - end  
114 - end  
115 end 103 end
spec/requests/api/merge_requests_spec.rb
@@ -7,6 +7,7 @@ describe API::API do @@ -7,6 +7,7 @@ describe API::API do
7 let(:user) { create(:user) } 7 let(:user) { create(:user) }
8 let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) } 8 let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
9 let!(:merge_request) { create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, title: "Test") } 9 let!(:merge_request) { create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, title: "Test") }
  10 + let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
10 before { 11 before {
11 project.team << [user, :reporters] 12 project.team << [user, :reporters]
12 } 13 }
@@ -205,4 +206,20 @@ describe API::API do @@ -205,4 +206,20 @@ describe API::API do
205 response.status.should == 404 206 response.status.should == 404
206 end 207 end
207 end 208 end
  209 +
  210 + describe "GET :id/merge_request/:merge_request_id/comments" do
  211 + it "should return merge_request comments" do
  212 + get api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user)
  213 + response.status.should == 200
  214 + json_response.should be_an Array
  215 + json_response.length.should == 1
  216 + json_response.first['note'].should == "a comment on a MR"
  217 + json_response.first['author']['id'].should == user.id
  218 + end
  219 +
  220 + it "should return a 404 error if merge_request_id not found" do
  221 + get api("/projects/#{project.id}/merge_request/999/comments", user)
  222 + response.status.should == 404
  223 + end
  224 + end
208 end 225 end
spec/requests/api/projects_spec.rb
@@ -13,6 +13,7 @@ describe API::API do @@ -13,6 +13,7 @@ describe API::API do
13 let(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') } 13 let(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') }
14 let(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } 14 let(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
15 let(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } 15 let(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) }
  16 + let(:issue_with_labels) { create(:issue, author: user, assignee: user, project: project, :label_list => "label1, label2") }
16 17
17 describe "GET /projects" do 18 describe "GET /projects" do
18 before { project } 19 before { project }
@@ -133,7 +134,7 @@ describe API::API do @@ -133,7 +134,7 @@ describe API::API do
133 end 134 end
134 135
135 it "should set a project as public" do 136 it "should set a project as public" do
136 - project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC }) 137 + project = attributes_for(:project, :public)
137 post api("/projects", user), project 138 post api("/projects", user), project
138 json_response['public'].should be_true 139 json_response['public'].should be_true
139 json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC 140 json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
@@ -147,21 +148,21 @@ describe API::API do @@ -147,21 +148,21 @@ describe API::API do
147 end 148 end
148 149
149 it "should set a project as internal" do 150 it "should set a project as internal" do
150 - project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL }) 151 + project = attributes_for(:project, :internal)
151 post api("/projects", user), project 152 post api("/projects", user), project
152 json_response['public'].should be_false 153 json_response['public'].should be_false
153 json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL 154 json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
154 end 155 end
155 156
156 it "should set a project as internal overriding :public" do 157 it "should set a project as internal overriding :public" do
157 - project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL }) 158 + project = attributes_for(:project, :internal, { public: true })
158 post api("/projects", user), project 159 post api("/projects", user), project
159 json_response['public'].should be_false 160 json_response['public'].should be_false
160 json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL 161 json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
161 end 162 end
162 163
163 it "should set a project as private" do 164 it "should set a project as private" do
164 - project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) 165 + project = attributes_for(:project, :private)
165 post api("/projects", user), project 166 post api("/projects", user), project
166 json_response['public'].should be_false 167 json_response['public'].should be_false
167 json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE 168 json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
@@ -215,7 +216,7 @@ describe API::API do @@ -215,7 +216,7 @@ describe API::API do
215 end 216 end
216 217
217 it "should set a project as public" do 218 it "should set a project as public" do
218 - project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC }) 219 + project = attributes_for(:project, :public)
219 post api("/projects/user/#{user.id}", admin), project 220 post api("/projects/user/#{user.id}", admin), project
220 json_response['public'].should be_true 221 json_response['public'].should be_true
221 json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC 222 json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
@@ -229,21 +230,21 @@ describe API::API do @@ -229,21 +230,21 @@ describe API::API do
229 end 230 end
230 231
231 it "should set a project as internal" do 232 it "should set a project as internal" do
232 - project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL }) 233 + project = attributes_for(:project, :internal)
233 post api("/projects/user/#{user.id}", admin), project 234 post api("/projects/user/#{user.id}", admin), project
234 json_response['public'].should be_false 235 json_response['public'].should be_false
235 json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL 236 json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
236 end 237 end
237 238
238 it "should set a project as internal overriding :public" do 239 it "should set a project as internal overriding :public" do
239 - project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL }) 240 + project = attributes_for(:project, :internal, { public: true })
240 post api("/projects/user/#{user.id}", admin), project 241 post api("/projects/user/#{user.id}", admin), project
241 json_response['public'].should be_false 242 json_response['public'].should be_false
242 json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL 243 json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
243 end 244 end
244 245
245 it "should set a project as private" do 246 it "should set a project as private" do
246 - project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) 247 + project = attributes_for(:project, :private)
247 post api("/projects/user/#{user.id}", admin), project 248 post api("/projects/user/#{user.id}", admin), project
248 json_response['public'].should be_false 249 json_response['public'].should be_false
249 json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE 250 json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
@@ -490,10 +491,10 @@ describe API::API do @@ -490,10 +491,10 @@ describe API::API do
490 491
491 describe :fork_admin do 492 describe :fork_admin do
492 let(:project_fork_target) { create(:project) } 493 let(:project_fork_target) { create(:project) }
493 - let(:project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } 494 + let(:project_fork_source) { create(:project, :public) }
494 495
495 describe "POST /projects/:id/fork/:forked_from_id" do 496 describe "POST /projects/:id/fork/:forked_from_id" do
496 - let(:new_project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } 497 + let(:new_project_fork_source) { create(:project, :public) }
497 498
498 it "shouldn't available for non admin users" do 499 it "shouldn't available for non admin users" do
499 post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user) 500 post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
@@ -562,10 +563,10 @@ describe API::API do @@ -562,10 +563,10 @@ describe API::API do
562 let!(:post) { create(:empty_project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) } 563 let!(:post) { create(:empty_project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
563 let!(:pre_post) { create(:empty_project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) } 564 let!(:pre_post) { create(:empty_project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) }
564 let!(:unfound) { create(:empty_project, name: 'unfound', creator_id: user.id, namespace: user.namespace) } 565 let!(:unfound) { create(:empty_project, name: 'unfound', creator_id: user.id, namespace: user.namespace) }
565 - let!(:internal) { create(:empty_project, name: "internal #{query}", visibility_level: Gitlab::VisibilityLevel::INTERNAL) }  
566 - let!(:unfound_internal) { create(:empty_project, name: 'unfound internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL) }  
567 - let!(:public) { create(:empty_project, name: "public #{query}", visibility_level: Gitlab::VisibilityLevel::PUBLIC) }  
568 - let!(:unfound_public) { create(:empty_project, name: 'unfound public', visibility_level: Gitlab::VisibilityLevel::PUBLIC) } 566 + let!(:internal) { create(:empty_project, :internal, name: "internal #{query}") }
  567 + let!(:unfound_internal) { create(:empty_project, :internal, name: 'unfound internal') }
  568 + let!(:public) { create(:empty_project, :public, name: "public #{query}") }
  569 + let!(:unfound_public) { create(:empty_project, :public, name: 'unfound public') }
569 570
570 context "when unauthenticated" do 571 context "when unauthenticated" do
571 it "should return authentication error" do 572 it "should return authentication error" do
@@ -632,4 +633,16 @@ describe API::API do @@ -632,4 +633,16 @@ describe API::API do
632 end 633 end
633 end 634 end
634 end 635 end
  636 +
  637 + describe "GET /projects/:id/labels" do
  638 + before { issue_with_labels }
  639 +
  640 + it "should return project labels" do
  641 + get api("/projects/#{project.id}/labels", user)
  642 + response.status.should == 200
  643 + json_response.should be_an Array
  644 + json_response.first['name'].should == issue_with_labels.labels.first.name
  645 + json_response.last['name'].should == issue_with_labels.labels.last.name
  646 + end
  647 + end
635 end 648 end
spec/seed_project.tar.gz
No preview for this file type
spec/services/event_create_service_spec.rb 0 → 100644
@@ -0,0 +1,103 @@ @@ -0,0 +1,103 @@
  1 +require 'spec_helper'
  2 +
  3 +describe EventCreateService do
  4 + let(:service) { EventCreateService.new }
  5 +
  6 + describe 'Issues' do
  7 + describe :open_issue do
  8 + let(:issue) { create(:issue) }
  9 +
  10 + it { service.open_issue(issue, issue.author).should be_true }
  11 +
  12 + it "should create new event" do
  13 + expect { service.open_issue(issue, issue.author) }.to change { Event.count }
  14 + end
  15 + end
  16 +
  17 + describe :close_issue do
  18 + let(:issue) { create(:issue) }
  19 +
  20 + it { service.close_issue(issue, issue.author).should be_true }
  21 +
  22 + it "should create new event" do
  23 + expect { service.close_issue(issue, issue.author) }.to change { Event.count }
  24 + end
  25 + end
  26 +
  27 + describe :reopen_issue do
  28 + let(:issue) { create(:issue) }
  29 +
  30 + it { service.reopen_issue(issue, issue.author).should be_true }
  31 +
  32 + it "should create new event" do
  33 + expect { service.reopen_issue(issue, issue.author) }.to change { Event.count }
  34 + end
  35 + end
  36 + end
  37 +
  38 + describe 'Merge Requests' do
  39 + describe :open_mr do
  40 + let(:merge_request) { create(:merge_request) }
  41 +
  42 + it { service.open_mr(merge_request, merge_request.author).should be_true }
  43 +
  44 + it "should create new event" do
  45 + expect { service.open_mr(merge_request, merge_request.author) }.to change { Event.count }
  46 + end
  47 + end
  48 +
  49 + describe :close_mr do
  50 + let(:merge_request) { create(:merge_request) }
  51 +
  52 + it { service.close_mr(merge_request, merge_request.author).should be_true }
  53 +
  54 + it "should create new event" do
  55 + expect { service.close_mr(merge_request, merge_request.author) }.to change { Event.count }
  56 + end
  57 + end
  58 +
  59 + describe :merge_mr do
  60 + let(:merge_request) { create(:merge_request) }
  61 +
  62 + it { service.merge_mr(merge_request, merge_request.author).should be_true }
  63 +
  64 + it "should create new event" do
  65 + expect { service.merge_mr(merge_request, merge_request.author) }.to change { Event.count }
  66 + end
  67 + end
  68 +
  69 + describe :reopen_mr do
  70 + let(:merge_request) { create(:merge_request) }
  71 +
  72 + it { service.reopen_mr(merge_request, merge_request.author).should be_true }
  73 +
  74 + it "should create new event" do
  75 + expect { service.reopen_mr(merge_request, merge_request.author) }.to change { Event.count }
  76 + end
  77 + end
  78 + end
  79 +
  80 + describe 'Milestone' do
  81 + let(:user) { create :user }
  82 +
  83 + describe :open_milestone do
  84 + let(:milestone) { create(:milestone) }
  85 +
  86 + it { service.open_milestone(milestone, user).should be_true }
  87 +
  88 + it "should create new event" do
  89 + expect { service.open_milestone(milestone, user) }.to change { Event.count }
  90 + end
  91 + end
  92 +
  93 + describe :close_mr do
  94 + let(:milestone) { create(:milestone) }
  95 +
  96 + it { service.close_milestone(milestone, user).should be_true }
  97 +
  98 + it "should create new event" do
  99 + expect { service.close_milestone(milestone, user) }.to change { Event.count }
  100 + end
  101 + end
  102 + end
  103 +end
spec/services/notification_service_spec.rb
@@ -32,6 +32,7 @@ describe NotificationService do @@ -32,6 +32,7 @@ describe NotificationService do
32 describe 'Notes' do 32 describe 'Notes' do
33 context 'issue note' do 33 context 'issue note' do
34 let(:issue) { create(:issue, assignee: create(:user)) } 34 let(:issue) { create(:issue, assignee: create(:user)) }
  35 + let(:mentioned_issue) { create(:issue, assignee: issue.assignee) }
35 let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced') } 36 let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced') }
36 37
37 before do 38 before do
@@ -50,6 +51,13 @@ describe NotificationService do @@ -50,6 +51,13 @@ describe NotificationService do
50 notification.new_note(note) 51 notification.new_note(note)
51 end 52 end
52 53
  54 + it 'filters out "mentioned in" notes' do
  55 + mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author, issue.project)
  56 +
  57 + Notify.should_not_receive(:note_issue_email)
  58 + notification.new_note(mentioned_note)
  59 + end
  60 +
53 def should_email(user_id) 61 def should_email(user_id)
54 Notify.should_receive(:note_issue_email).with(user_id, note.id) 62 Notify.should_receive(:note_issue_email).with(user_id, note.id)
55 end 63 end
@@ -233,15 +241,15 @@ describe NotificationService do @@ -233,15 +241,15 @@ describe NotificationService do
233 should_email(@u_watcher.id) 241 should_email(@u_watcher.id)
234 should_not_email(@u_participating.id) 242 should_not_email(@u_participating.id)
235 should_not_email(@u_disabled.id) 243 should_not_email(@u_disabled.id)
236 - notification.merge_mr(merge_request) 244 + notification.merge_mr(merge_request, @u_disabled)
237 end 245 end
238 246
239 def should_email(user_id) 247 def should_email(user_id)
240 - Notify.should_receive(:merged_merge_request_email).with(user_id, merge_request.id) 248 + Notify.should_receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
241 end 249 end
242 250
243 def should_not_email(user_id) 251 def should_not_email(user_id)
244 - Notify.should_not_receive(:merged_merge_request_email).with(user_id, merge_request.id) 252 + Notify.should_not_receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
245 end 253 end
246 end 254 end
247 end 255 end
spec/services/project_transfer_service_spec.rb
@@ -1,33 +0,0 @@ @@ -1,33 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe ProjectTransferService do  
4 - before(:each) { enable_observers }  
5 - after(:each) {disable_observers}  
6 -  
7 - context 'namespace -> namespace' do  
8 - let(:user) { create(:user) }  
9 - let(:group) { create(:group) }  
10 - let(:project) { create(:project, namespace: user.namespace) }  
11 -  
12 - before do  
13 - @result = service.transfer(project, group)  
14 - end  
15 -  
16 - it { @result.should be_true }  
17 - it { project.namespace.should == group }  
18 - end  
19 -  
20 - context 'namespace -> no namespace' do  
21 - let(:user) { create(:user) }  
22 - let(:project) { create(:project, namespace: user.namespace) }  
23 -  
24 - it { lambda{service.transfer(project, nil)}.should raise_error(ActiveRecord::RecordInvalid) }  
25 - end  
26 -  
27 - def service  
28 - service = ProjectTransferService.new  
29 - service.gitlab_shell.stub(mv_repository: true)  
30 - service  
31 - end  
32 -end  
33 -  
spec/services/projects/create_service_spec.rb 0 → 100644
@@ -0,0 +1,163 @@ @@ -0,0 +1,163 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Projects::CreateService do
  4 + before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
  5 + after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
  6 +
  7 + describe :create_by_user do
  8 + before do
  9 + @user = create :user
  10 + @admin = create :user, admin: true
  11 + @opts = {
  12 + name: "GitLab",
  13 + namespace: @user.namespace
  14 + }
  15 + end
  16 +
  17 + context 'user namespace' do
  18 + before do
  19 + @project = create_project(@user, @opts)
  20 + end
  21 +
  22 + it { @project.should be_valid }
  23 + it { @project.owner.should == @user }
  24 + it { @project.namespace.should == @user.namespace }
  25 + end
  26 +
  27 + context 'group namespace' do
  28 + before do
  29 + @group = create :group
  30 + @group.add_owner(@user)
  31 +
  32 + @opts.merge!(namespace_id: @group.id)
  33 + @project = create_project(@user, @opts)
  34 + end
  35 +
  36 + it { @project.should be_valid }
  37 + it { @project.owner.should == @group }
  38 + it { @project.namespace.should == @group }
  39 + end
  40 +
  41 + context 'wiki_enabled creates repository directory' do
  42 + context 'wiki_enabled true creates wiki repository directory' do
  43 + before do
  44 + @project = create_project(@user, @opts)
  45 + @path = GollumWiki.new(@project, @user).send(:path_to_repo)
  46 + end
  47 +
  48 + it { File.exists?(@path).should be_true }
  49 + end
  50 +
  51 + context 'wiki_enabled false does not create wiki repository directory' do
  52 + before do
  53 + @opts.merge!(wiki_enabled: false)
  54 + @project = create_project(@user, @opts)
  55 + @path = GollumWiki.new(@project, @user).send(:path_to_repo)
  56 + end
  57 +
  58 + it { File.exists?(@path).should be_false }
  59 + end
  60 + end
  61 +
  62 + context 'respect configured visibility setting' do
  63 + before(:each) do
  64 + @settings = double("settings")
  65 + @settings.stub(:issues) { true }
  66 + @settings.stub(:merge_requests) { true }
  67 + @settings.stub(:wiki) { true }
  68 + @settings.stub(:wall) { true }
  69 + @settings.stub(:snippets) { true }
  70 + stub_const("Settings", Class.new)
  71 + @restrictions = double("restrictions")
  72 + @restrictions.stub(:restricted_visibility_levels) { [] }
  73 + Settings.stub_chain(:gitlab).and_return(@restrictions)
  74 + Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings)
  75 + end
  76 +
  77 + context 'should be public when setting is public' do
  78 + before do
  79 + @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
  80 + @project = create_project(@user, @opts)
  81 + end
  82 +
  83 + it { @project.public?.should be_true }
  84 + end
  85 +
  86 + context 'should be private when setting is private' do
  87 + before do
  88 + @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
  89 + @project = create_project(@user, @opts)
  90 + end
  91 +
  92 + it { @project.private?.should be_true }
  93 + end
  94 +
  95 + context 'should be internal when setting is internal' do
  96 + before do
  97 + @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::INTERNAL }
  98 + @project = create_project(@user, @opts)
  99 + end
  100 +
  101 + it { @project.internal?.should be_true }
  102 + end
  103 + end
  104 +
  105 + context 'respect configured visibility restrictions setting' do
  106 + before(:each) do
  107 + @settings = double("settings")
  108 + @settings.stub(:issues) { true }
  109 + @settings.stub(:merge_requests) { true }
  110 + @settings.stub(:wiki) { true }
  111 + @settings.stub(:wall) { true }
  112 + @settings.stub(:snippets) { true }
  113 + @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
  114 + stub_const("Settings", Class.new)
  115 + @restrictions = double("restrictions")
  116 + @restrictions.stub(:restricted_visibility_levels) { [ Gitlab::VisibilityLevel::PUBLIC ] }
  117 + Settings.stub_chain(:gitlab).and_return(@restrictions)
  118 + Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings)
  119 + end
  120 +
  121 + context 'should be private when option is public' do
  122 + before do
  123 + @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
  124 + @project = create_project(@user, @opts)
  125 + end
  126 +
  127 + it { @project.private?.should be_true }
  128 + end
  129 +
  130 + context 'should be public when option is public for admin' do
  131 + before do
  132 + @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
  133 + @project = create_project(@admin, @opts)
  134 + end
  135 +
  136 + it { @project.public?.should be_true }
  137 + end
  138 +
  139 + context 'should be private when option is private' do
  140 + before do
  141 + @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
  142 + @project = create_project(@user, @opts)
  143 + end
  144 +
  145 + it { @project.private?.should be_true }
  146 + end
  147 +
  148 + context 'should be internal when option is internal' do
  149 + before do
  150 + @opts.merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
  151 + @project = create_project(@user, @opts)
  152 + end
  153 +
  154 + it { @project.internal?.should be_true }
  155 + end
  156 + end
  157 + end
  158 +
  159 + def create_project(user, opts)
  160 + Projects::CreateService.new(user, opts).execute
  161 + end
  162 +end
  163 +
spec/services/projects/transfer_service_spec.rb 0 → 100644
@@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
  1 +require 'spec_helper'
  2 +
  3 +describe ProjectTransferService do
  4 + before(:each) { enable_observers }
  5 + after(:each) {disable_observers}
  6 +
  7 + context 'namespace -> namespace' do
  8 + let(:user) { create(:user) }
  9 + let(:group) { create(:group) }
  10 + let(:project) { create(:project, namespace: user.namespace) }
  11 +
  12 + before do
  13 + @result = service.transfer(project, group)
  14 + end
  15 +
  16 + it { @result.should be_true }
  17 + it { project.namespace.should == group }
  18 + end
  19 +
  20 + context 'namespace -> no namespace' do
  21 + let(:user) { create(:user) }
  22 + let(:project) { create(:project, namespace: user.namespace) }
  23 +
  24 + it { lambda{service.transfer(project, nil)}.should raise_error(ActiveRecord::RecordInvalid) }
  25 + end
  26 +
  27 + def service
  28 + service = ProjectTransferService.new
  29 + service.gitlab_shell.stub(mv_repository: true)
  30 + service
  31 + end
  32 +end
  33 +
spec/services/projects/update_service_spec.rb 0 → 100644
@@ -0,0 +1,111 @@ @@ -0,0 +1,111 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Projects::UpdateService do
  4 + before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
  5 + after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
  6 +
  7 + describe :update_by_user do
  8 + before do
  9 + @user = create :user
  10 + @admin = create :user, admin: true
  11 + @project = create :project, creator_id: @user.id, namespace: @user.namespace
  12 + @opts = { project: {} }
  13 + end
  14 +
  15 + context 'should be private when updated to private' do
  16 + before do
  17 + @created_private = @project.private?
  18 +
  19 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
  20 + update_project(@project, @user, @opts)
  21 + end
  22 +
  23 + it { @created_private.should be_true }
  24 + it { @project.private?.should be_true }
  25 + end
  26 +
  27 + context 'should be internal when updated to internal' do
  28 + before do
  29 + @created_private = @project.private?
  30 +
  31 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
  32 + update_project(@project, @user, @opts)
  33 + end
  34 +
  35 + it { @created_private.should be_true }
  36 + it { @project.internal?.should be_true }
  37 + end
  38 +
  39 + context 'should be public when updated to public' do
  40 + before do
  41 + @created_private = @project.private?
  42 +
  43 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
  44 + update_project(@project, @user, @opts)
  45 + end
  46 +
  47 + it { @created_private.should be_true }
  48 + it { @project.public?.should be_true }
  49 + end
  50 +
  51 + context 'respect configured visibility restrictions setting' do
  52 + before(:each) do
  53 + @restrictions = double("restrictions")
  54 + @restrictions.stub(:restricted_visibility_levels) { [ Gitlab::VisibilityLevel::PUBLIC ] }
  55 + Settings.stub_chain(:gitlab).and_return(@restrictions)
  56 + end
  57 +
  58 + context 'should be private when updated to private' do
  59 + before do
  60 + @created_private = @project.private?
  61 +
  62 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
  63 + update_project(@project, @user, @opts)
  64 + end
  65 +
  66 + it { @created_private.should be_true }
  67 + it { @project.private?.should be_true }
  68 + end
  69 +
  70 + context 'should be internal when updated to internal' do
  71 + before do
  72 + @created_private = @project.private?
  73 +
  74 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
  75 + update_project(@project, @user, @opts)
  76 + end
  77 +
  78 + it { @created_private.should be_true }
  79 + it { @project.internal?.should be_true }
  80 + end
  81 +
  82 + context 'should be private when updated to public' do
  83 + before do
  84 + @created_private = @project.private?
  85 +
  86 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
  87 + update_project(@project, @user, @opts)
  88 + end
  89 +
  90 + it { @created_private.should be_true }
  91 + it { @project.private?.should be_true }
  92 + end
  93 +
  94 + context 'should be public when updated to public by admin' do
  95 + before do
  96 + @created_private = @project.private?
  97 +
  98 + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
  99 + update_project(@project, @admin, @opts)
  100 + end
  101 +
  102 + it { @created_private.should be_true }
  103 + it { @project.public?.should be_true }
  104 + end
  105 + end
  106 + end
  107 +
  108 + def update_project(project, user, opts)
  109 + Projects::UpdateService.new(project, user, opts).execute
  110 + end
  111 +end
spec/services/projects_create_service_spec.rb
@@ -1,163 +0,0 @@ @@ -1,163 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe Projects::CreateService do  
4 - before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }  
5 - after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }  
6 -  
7 - describe :create_by_user do  
8 - before do  
9 - @user = create :user  
10 - @admin = create :user, admin: true  
11 - @opts = {  
12 - name: "GitLab",  
13 - namespace: @user.namespace  
14 - }  
15 - end  
16 -  
17 - context 'user namespace' do  
18 - before do  
19 - @project = create_project(@user, @opts)  
20 - end  
21 -  
22 - it { @project.should be_valid }  
23 - it { @project.owner.should == @user }  
24 - it { @project.namespace.should == @user.namespace }  
25 - end  
26 -  
27 - context 'group namespace' do  
28 - before do  
29 - @group = create :group  
30 - @group.add_owner(@user)  
31 -  
32 - @opts.merge!(namespace_id: @group.id)  
33 - @project = create_project(@user, @opts)  
34 - end  
35 -  
36 - it { @project.should be_valid }  
37 - it { @project.owner.should == @group }  
38 - it { @project.namespace.should == @group }  
39 - end  
40 -  
41 - context 'wiki_enabled creates repository directory' do  
42 - context 'wiki_enabled true creates wiki repository directory' do  
43 - before do  
44 - @project = create_project(@user, @opts)  
45 - @path = GollumWiki.new(@project, @user).send(:path_to_repo)  
46 - end  
47 -  
48 - it { File.exists?(@path).should be_true }  
49 - end  
50 -  
51 - context 'wiki_enabled false does not create wiki repository directory' do  
52 - before do  
53 - @opts.merge!(wiki_enabled: false)  
54 - @project = create_project(@user, @opts)  
55 - @path = GollumWiki.new(@project, @user).send(:path_to_repo)  
56 - end  
57 -  
58 - it { File.exists?(@path).should be_false }  
59 - end  
60 - end  
61 -  
62 - context 'respect configured visibility setting' do  
63 - before(:each) do  
64 - @settings = double("settings")  
65 - @settings.stub(:issues) { true }  
66 - @settings.stub(:merge_requests) { true }  
67 - @settings.stub(:wiki) { true }  
68 - @settings.stub(:wall) { true }  
69 - @settings.stub(:snippets) { true }  
70 - stub_const("Settings", Class.new)  
71 - @restrictions = double("restrictions")  
72 - @restrictions.stub(:restricted_visibility_levels) { [] }  
73 - Settings.stub_chain(:gitlab).and_return(@restrictions)  
74 - Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings)  
75 - end  
76 -  
77 - context 'should be public when setting is public' do  
78 - before do  
79 - @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }  
80 - @project = create_project(@user, @opts)  
81 - end  
82 -  
83 - it { @project.public?.should be_true }  
84 - end  
85 -  
86 - context 'should be private when setting is private' do  
87 - before do  
88 - @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }  
89 - @project = create_project(@user, @opts)  
90 - end  
91 -  
92 - it { @project.private?.should be_true }  
93 - end  
94 -  
95 - context 'should be internal when setting is internal' do  
96 - before do  
97 - @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::INTERNAL }  
98 - @project = create_project(@user, @opts)  
99 - end  
100 -  
101 - it { @project.internal?.should be_true }  
102 - end  
103 - end  
104 -  
105 - context 'respect configured visibility restrictions setting' do  
106 - before(:each) do  
107 - @settings = double("settings")  
108 - @settings.stub(:issues) { true }  
109 - @settings.stub(:merge_requests) { true }  
110 - @settings.stub(:wiki) { true }  
111 - @settings.stub(:wall) { true }  
112 - @settings.stub(:snippets) { true }  
113 - @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }  
114 - stub_const("Settings", Class.new)  
115 - @restrictions = double("restrictions")  
116 - @restrictions.stub(:restricted_visibility_levels) { [ Gitlab::VisibilityLevel::PUBLIC ] }  
117 - Settings.stub_chain(:gitlab).and_return(@restrictions)  
118 - Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings)  
119 - end  
120 -  
121 - context 'should be private when option is public' do  
122 - before do  
123 - @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)  
124 - @project = create_project(@user, @opts)  
125 - end  
126 -  
127 - it { @project.private?.should be_true }  
128 - end  
129 -  
130 - context 'should be public when option is public for admin' do  
131 - before do  
132 - @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)  
133 - @project = create_project(@admin, @opts)  
134 - end  
135 -  
136 - it { @project.public?.should be_true }  
137 - end  
138 -  
139 - context 'should be private when option is private' do  
140 - before do  
141 - @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)  
142 - @project = create_project(@user, @opts)  
143 - end  
144 -  
145 - it { @project.private?.should be_true }  
146 - end  
147 -  
148 - context 'should be internal when option is internal' do  
149 - before do  
150 - @opts.merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)  
151 - @project = create_project(@user, @opts)  
152 - end  
153 -  
154 - it { @project.internal?.should be_true }  
155 - end  
156 - end  
157 - end  
158 -  
159 - def create_project(user, opts)  
160 - Projects::CreateService.new(user, opts).execute  
161 - end  
162 -end  
163 -  
spec/services/projects_update_service_spec.rb
@@ -1,111 +0,0 @@ @@ -1,111 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe Projects::UpdateService do  
4 - before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }  
5 - after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }  
6 -  
7 - describe :update_by_user do  
8 - before do  
9 - @user = create :user  
10 - @admin = create :user, admin: true  
11 - @project = create :project, creator_id: @user.id, namespace: @user.namespace  
12 - @opts = { project: {} }  
13 - end  
14 -  
15 - context 'should be private when updated to private' do  
16 - before do  
17 - @created_private = @project.private?  
18 -  
19 - @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)  
20 - update_project(@project, @user, @opts)  
21 - end  
22 -  
23 - it { @created_private.should be_true }  
24 - it { @project.private?.should be_true }  
25 - end  
26 -  
27 - context 'should be internal when updated to internal' do  
28 - before do  
29 - @created_private = @project.private?  
30 -  
31 - @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)  
32 - update_project(@project, @user, @opts)  
33 - end  
34 -  
35 - it { @created_private.should be_true }  
36 - it { @project.internal?.should be_true }  
37 - end  
38 -  
39 - context 'should be public when updated to public' do  
40 - before do  
41 - @created_private = @project.private?  
42 -  
43 - @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)  
44 - update_project(@project, @user, @opts)  
45 - end  
46 -  
47 - it { @created_private.should be_true }  
48 - it { @project.public?.should be_true }  
49 - end  
50 -  
51 - context 'respect configured visibility restrictions setting' do  
52 - before(:each) do  
53 - @restrictions = double("restrictions")  
54 - @restrictions.stub(:restricted_visibility_levels) { [ Gitlab::VisibilityLevel::PUBLIC ] }  
55 - Settings.stub_chain(:gitlab).and_return(@restrictions)  
56 - end  
57 -  
58 - context 'should be private when updated to private' do  
59 - before do  
60 - @created_private = @project.private?  
61 -  
62 - @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)  
63 - update_project(@project, @user, @opts)  
64 - end  
65 -  
66 - it { @created_private.should be_true }  
67 - it { @project.private?.should be_true }  
68 - end  
69 -  
70 - context 'should be internal when updated to internal' do  
71 - before do  
72 - @created_private = @project.private?  
73 -  
74 - @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)  
75 - update_project(@project, @user, @opts)  
76 - end  
77 -  
78 - it { @created_private.should be_true }  
79 - it { @project.internal?.should be_true }  
80 - end  
81 -  
82 - context 'should be private when updated to public' do  
83 - before do  
84 - @created_private = @project.private?  
85 -  
86 - @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)  
87 - update_project(@project, @user, @opts)  
88 - end  
89 -  
90 - it { @created_private.should be_true }  
91 - it { @project.private?.should be_true }  
92 - end  
93 -  
94 - context 'should be public when updated to public by admin' do  
95 - before do  
96 - @created_private = @project.private?  
97 -  
98 - @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)  
99 - update_project(@project, @admin, @opts)  
100 - end  
101 -  
102 - it { @created_private.should be_true }  
103 - it { @project.public?.should be_true }  
104 - end  
105 - end  
106 - end  
107 -  
108 - def update_project(project, user, opts)  
109 - Projects::UpdateService.new(project, user, opts).execute  
110 - end  
111 -end  
spec/services/search_service_spec.rb
1 require 'spec_helper' 1 require 'spec_helper'
2 2
3 describe 'Search::GlobalService' do 3 describe 'Search::GlobalService' do
4 - let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') }  
5 let(:user) { create(:user, namespace: found_namespace) } 4 let(:user) { create(:user, namespace: found_namespace) }
6 - let!(:found_project) { create(:project, name: 'searchable_project', creator_id: user.id, namespace: found_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } 5 + let(:public_user) { create(:user, namespace: public_namespace) }
  6 + let(:internal_user) { create(:user, namespace: internal_namespace) }
7 7
  8 + let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') }
8 let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') } 9 let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') }
9 - let!(:unfound_project) { create(:project, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } 10 + let(:internal_namespace) { create(:namespace, name: 'searchable internal namespace', path: 'something_internal') }
  11 + let(:public_namespace) { create(:namespace, name: 'searchable public namespace', path: 'something_public') }
10 12
11 - let(:internal_namespace) { create(:namespace, path: 'something_internal',name: 'searchable internal namespace') }  
12 - let(:internal_user) { create(:user, namespace: internal_namespace) }  
13 - let!(:internal_project) { create(:project, name: 'searchable_internal_project', creator_id: internal_user.id, namespace: internal_namespace, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }  
14 -  
15 - let(:public_namespace) { create(:namespace, path: 'something_public',name: 'searchable public namespace') }  
16 - let(:public_user) { create(:user, namespace: public_namespace) }  
17 - let!(:public_project) { create(:project, name: 'searchable_public_project', creator_id: public_user.id, namespace: public_namespace, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } 13 + let!(:found_project) { create(:project, :private, name: 'searchable_project', creator_id: user.id, namespace: found_namespace) }
  14 + let!(:unfound_project) { create(:project, :private, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace) }
  15 + let!(:internal_project) { create(:project, :internal, name: 'searchable_internal_project', creator_id: internal_user.id, namespace: internal_namespace) }
  16 + let!(:public_project) { create(:project, :public, name: 'searchable_public_project', creator_id: public_user.id, namespace: public_namespace) }
18 17
19 describe '#execute' do 18 describe '#execute' do
20 context 'unauthenticated' do 19 context 'unauthenticated' do
21 it 'should return public projects only' do 20 it 'should return public projects only' do
22 context = Search::GlobalService.new(nil, search: "searchable") 21 context = Search::GlobalService.new(nil, search: "searchable")
23 results = context.execute 22 results = context.execute
24 - results[:projects].should have(1).items  
25 - results[:projects].should include(public_project) 23 + results[:projects].should match_array [public_project]
26 end 24 end
27 end 25 end
28 26
@@ -30,24 +28,19 @@ describe &#39;Search::GlobalService&#39; do @@ -30,24 +28,19 @@ describe &#39;Search::GlobalService&#39; do
30 it 'should return public, internal and private projects' do 28 it 'should return public, internal and private projects' do
31 context = Search::GlobalService.new(user, search: "searchable") 29 context = Search::GlobalService.new(user, search: "searchable")
32 results = context.execute 30 results = context.execute
33 - results[:projects].should have(3).items  
34 - results[:projects].should include(public_project)  
35 - results[:projects].should include(found_project)  
36 - results[:projects].should include(internal_project) 31 + results[:projects].should match_array [public_project, found_project, internal_project]
37 end 32 end
38 33
39 it 'should return only public & internal projects' do 34 it 'should return only public & internal projects' do
40 context = Search::GlobalService.new(internal_user, search: "searchable") 35 context = Search::GlobalService.new(internal_user, search: "searchable")
41 results = context.execute 36 results = context.execute
42 - results[:projects].should have(2).items  
43 - results[:projects].should include(internal_project)  
44 - results[:projects].should include(public_project) 37 + results[:projects].should match_array [internal_project, public_project]
45 end 38 end
46 39
47 it 'namespace name should be searchable' do 40 it 'namespace name should be searchable' do
48 context = Search::GlobalService.new(user, search: "searchable namespace") 41 context = Search::GlobalService.new(user, search: "searchable namespace")
49 results = context.execute 42 results = context.execute
50 - results[:projects].should == [found_project] 43 + results[:projects].should match_array [found_project]
51 end 44 end
52 end 45 end
53 end 46 end
spec/spec_helper.rb
1 -require 'rubygems'  
2 -require 'spork' 1 +# This file is copied to spec/ when you run 'rails generate rspec:install'
  2 +ENV["RAILS_ENV"] ||= 'test'
  3 +require File.expand_path("../../config/environment", __FILE__)
3 4
4 -Spork.prefork do  
5 - require 'simplecov' unless ENV['CI'] 5 +require 'simplecov' unless ENV['CI']
6 6
7 - if ENV['TRAVIS']  
8 - require 'coveralls'  
9 - Coveralls.wear!  
10 - end  
11 -  
12 - # This file is copied to spec/ when you run 'rails generate rspec:install'  
13 - ENV["RAILS_ENV"] ||= 'test'  
14 - require File.expand_path("../../config/environment", __FILE__)  
15 - require 'rspec/rails'  
16 - require 'capybara/rails'  
17 - require 'capybara/rspec'  
18 - require 'webmock/rspec'  
19 - require 'email_spec'  
20 - require 'sidekiq/testing/inline'  
21 - require 'capybara/poltergeist'  
22 -  
23 - # Loading more in this block will cause your tests to run faster. However,  
24 -  
25 - # if you change any configuration or code from libraries loaded here, you'll  
26 - # need to restart spork for it take effect.  
27 - Capybara.javascript_driver = :poltergeist  
28 - Capybara.default_wait_time = 10  
29 -  
30 - # Requires supporting ruby files with custom matchers and macros, etc,  
31 - # in spec/support/ and its subdirectories.  
32 - Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}  
33 -  
34 - WebMock.disable_net_connect!(allow_localhost: true)  
35 -  
36 - RSpec.configure do |config|  
37 - config.mock_with :rspec  
38 -  
39 - config.include LoginHelpers, type: :feature  
40 - config.include LoginHelpers, type: :request  
41 - config.include FactoryGirl::Syntax::Methods  
42 - config.include Devise::TestHelpers, type: :controller  
43 -  
44 - config.include TestEnv  
45 -  
46 - # If you're not using ActiveRecord, or you'd prefer not to run each of your  
47 - # examples within a transaction, remove the following line or assign false  
48 - # instead of true.  
49 - config.use_transactional_fixtures = false  
50 -  
51 - config.before(:suite) do  
52 - TestEnv.init(observers: false, init_repos: true, repos: false)  
53 - end  
54 - config.before(:each) do  
55 - TestEnv.setup_stubs  
56 - end  
57 - end 7 +if ENV['TRAVIS']
  8 + require 'coveralls'
  9 + Coveralls.wear!
58 end 10 end
59 11
60 -Spork.each_run do  
61 - # This code will be run each time you run your specs. 12 +require 'rspec/rails'
  13 +require 'capybara/rails'
  14 +require 'capybara/rspec'
  15 +require 'webmock/rspec'
  16 +require 'email_spec'
  17 +require 'sidekiq/testing/inline'
  18 +require 'capybara/poltergeist'
  19 +
  20 +Capybara.javascript_driver = :poltergeist
  21 +Capybara.default_wait_time = 10
  22 +
  23 +# Requires supporting ruby files with custom matchers and macros, etc,
  24 +# in spec/support/ and its subdirectories.
  25 +Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
  26 +
  27 +WebMock.disable_net_connect!(allow_localhost: true)
62 28
  29 +RSpec.configure do |config|
  30 + config.mock_with :rspec
  31 +
  32 + config.include LoginHelpers, type: :feature
  33 + config.include LoginHelpers, type: :request
  34 + config.include FactoryGirl::Syntax::Methods
  35 + config.include Devise::TestHelpers, type: :controller
  36 +
  37 + config.include TestEnv
  38 +
  39 + # If you're not using ActiveRecord, or you'd prefer not to run each of your
  40 + # examples within a transaction, remove the following line or assign false
  41 + # instead of true.
  42 + config.use_transactional_fixtures = false
  43 +
  44 + config.before(:suite) do
  45 + TestEnv.init(observers: false, init_repos: true, repos: false)
  46 + end
  47 + config.before(:each) do
  48 + TestEnv.setup_stubs
  49 + end
63 end 50 end
spec/support/test_env.rb
@@ -29,7 +29,6 @@ module TestEnv @@ -29,7 +29,6 @@ module TestEnv
29 disable_mailer if opts[:mailer] == false 29 disable_mailer if opts[:mailer] == false
30 setup_stubs 30 setup_stubs
31 31
32 -  
33 clear_test_repo_dir if opts[:init_repos] == true 32 clear_test_repo_dir if opts[:init_repos] == true
34 setup_test_repos(opts) if opts[:repos] == true 33 setup_test_repos(opts) if opts[:repos] == true
35 end 34 end
@@ -91,7 +90,7 @@ module TestEnv @@ -91,7 +90,7 @@ module TestEnv
91 size: 12.45 90 size: 12.45
92 ) 91 )
93 92
94 - ActivityObserver.any_instance.stub( 93 + BaseObserver.any_instance.stub(
95 current_user: double("current_user", id: 1) 94 current_user: double("current_user", id: 1)
96 ) 95 )
97 end 96 end
@@ -165,8 +164,7 @@ module TestEnv @@ -165,8 +164,7 @@ module TestEnv
165 164
166 def clear_test_repo_dir 165 def clear_test_repo_dir
167 setup_stubs 166 setup_stubs
168 - # Use tmp dir for FS manipulations  
169 - repos_path = testing_path() 167 +
170 # Remove tmp/test-git-base-path 168 # Remove tmp/test-git-base-path
171 FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path 169 FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
172 170