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
.rspec
1   ---color --drb
  1 +--color
... ...
.travis.yml
... ... @@ -19,10 +19,10 @@ rvm:
19 19 services:
20 20 - redis-server
21 21 before_script:
22   - - "bundle exec rake db:setup"
23   - - "bundle exec rake db:seed_fu"
24 22 - "cp config/database.yml.$DB config/database.yml"
25 23 - "cp config/gitlab.yml.example config/gitlab.yml"
  24 + - "bundle exec rake db:setup"
  25 + - "bundle exec rake db:seed_fu"
26 26 script: "bundle exec rake $TASK --trace"
27 27 notifications:
28 28 email: false
... ...
CHANGELOG
  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 12 v 6.7.0
2 13 - Increased the example Nginx client_max_body_size from 5MB to 20MB, consider updating it manually on existing installations
3 14 - Add support for Gemnasium as a Project Service (Olivier Gonzalez)
... ... @@ -9,6 +20,7 @@ v 6.7.0
9 20 - Show contribution guide link for new issue form (Jeroen van Baarsen)
10 21 - Fix CI status for merge requests from fork
11 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 24 - Converted all the help sections into markdown
13 25 - LDAP user filters
14 26 - Streamline the content of notification emails (Pierre de La Morinerie)
... ... @@ -24,6 +36,12 @@ v 6.7.0
24 36 - Faster authorized_keys rebuilding in `rake gitlab:shell:setup` (requires gitlab-shell 1.8.5)
25 37 - Create and Update MR calls now support the description parameter (Greg Messner)
26 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 46 v 6.6.5
29 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 24  
25 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 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 51  
49 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 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 67 1. The MR title should describes the change you want to make
65 68 1. The MR description should give a motive for your change and the method you used to achieve it
66 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 72 1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submittion
69 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 78  
75 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 89 ## Contribution acceptance criteria
78 90  
79 91 1. The change is as small as possible (see the above paragraph for details)
... ...
Gemfile
... ... @@ -132,6 +132,9 @@ gem "gitlab-flowdock-git-hook", "~> 0.4.2"
132 132 # Gemnasium integration
133 133 gem "gemnasium-gitlab-service", "~> 0.2"
134 134  
  135 +# Slack integration
  136 +gem "slack-notifier", "~> 0.2.0"
  137 +
135 138 # d3
136 139 gem "d3_rails", "~> 3.1.4"
137 140  
... ... @@ -162,8 +165,9 @@ gem "modernizr", "2.6.2"
162 165 gem "raphael-rails", "~> 2.1.2"
163 166 gem 'bootstrap-sass', '~> 3.0'
164 167 gem "font-awesome-rails", '~> 3.2'
165   -gem "gemoji", "~> 1.3.0"
  168 +gem "gitlab_emoji", "~> 0.0.1.1"
166 169 gem "gon", '~> 5.0.0'
  170 +gem 'nprogress-rails'
167 171  
168 172 group :development do
169 173 gem "annotate", "~> 2.6.0.beta2"
... ... @@ -214,7 +218,6 @@ group :development, :test do
214 218 # PhantomJS driver for Capybara
215 219 gem 'poltergeist', '~> 1.4.1'
216 220  
217   - gem 'spork', '~> 1.0rc'
218 221 gem 'jasmine', '2.0.0.rc5'
219 222  
220 223 gem "spring", '1.1.1'
... ...
Gemfile.lock
... ... @@ -128,6 +128,8 @@ GEM
128 128 mail (~> 2.2)
129 129 email_validator (1.4.0)
130 130 activemodel
  131 + emoji (1.0.1)
  132 + json
131 133 enumerize (0.7.0)
132 134 activesupport (>= 3.2)
133 135 equalizer (0.0.8)
... ... @@ -165,7 +167,6 @@ GEM
165 167 formatador (0.2.4)
166 168 gemnasium-gitlab-service (0.2.1)
167 169 rugged (~> 0.19)
168   - gemoji (1.3.1)
169 170 gherkin-ruby (0.3.1)
170 171 racc
171 172 github-markdown (0.5.5)
... ... @@ -190,6 +191,8 @@ GEM
190 191 charlock_holmes (~> 0.6.6)
191 192 escape_utils (~> 0.2.4)
192 193 mime-types (~> 1.19)
  194 + gitlab_emoji (0.0.1.1)
  195 + emoji (~> 1.0.1)
193 196 gitlab_git (5.7.1)
194 197 activesupport (~> 4.0.0)
195 198 charlock_holmes (~> 0.6.9)
... ... @@ -297,6 +300,7 @@ GEM
297 300 net-ssh (>= 1.99.1)
298 301 net-ssh (2.7.0)
299 302 nokogiri (1.5.10)
  303 + nprogress-rails (0.1.2.3)
300 304 oauth (0.4.7)
301 305 oauth2 (0.8.1)
302 306 faraday (~> 0.8)
... ... @@ -468,6 +472,7 @@ GEM
468 472 rack-protection (~> 1.4)
469 473 tilt (~> 1.3, >= 1.3.4)
470 474 six (0.2.0)
  475 + slack-notifier (0.2.0)
471 476 slim (2.0.2)
472 477 temple (~> 0.6.6)
473 478 tilt (>= 1.3.3, < 2.1)
... ... @@ -479,7 +484,6 @@ GEM
479 484 capybara (>= 2.0.0)
480 485 railties (>= 3)
481 486 spinach (>= 0.4)
482   - spork (1.0.0rc4)
483 487 spring (1.1.1)
484 488 spring-commands-rspec (1.0.1)
485 489 spring (>= 0.9.1)
... ... @@ -591,12 +595,12 @@ DEPENDENCIES
591 595 font-awesome-rails (~> 3.2)
592 596 foreman
593 597 gemnasium-gitlab-service (~> 0.2)
594   - gemoji (~> 1.3.0)
595 598 github-markup (~> 0.7.4)!
596 599 gitlab-flowdock-git-hook (~> 0.4.2)
597 600 gitlab-gollum-lib (~> 1.1.0)
598 601 gitlab-grack (~> 2.0.0.pre)
599 602 gitlab-linguist (~> 3.0.0)
  603 + gitlab_emoji (~> 0.0.1.1)
600 604 gitlab_git (~> 5.7.1)
601 605 gitlab_meta (= 6.0)
602 606 gitlab_omniauth-ldap (= 1.0.4)
... ... @@ -620,6 +624,7 @@ DEPENDENCIES
620 624 minitest (~> 4.7.0)
621 625 modernizr (= 2.6.2)
622 626 mysql2
  627 + nprogress-rails
623 628 omniauth (~> 1.1.3)
624 629 omniauth-github
625 630 omniauth-google-oauth2
... ... @@ -652,9 +657,9 @@ DEPENDENCIES
652 657 simplecov
653 658 sinatra
654 659 six
  660 + slack-notifier (~> 0.2.0)
655 661 slim
656 662 spinach-rails
657   - spork (~> 1.0rc)
658 663 spring (= 1.1.1)
659 664 spring-commands-rspec (= 1.0.1)
660 665 spring-commands-spinach (= 1.0.0)
... ...
PROCESS.md
... ... @@ -24,8 +24,6 @@ Below we describe the contributing process to GitLab for two reasons. So that co
24 24 - Monitors for new merge requests (at least once a week)
25 25 - Manages their work queue by looking at issues and merge requests assigned to them
26 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 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 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 71  
74 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 76 ### Issue report for old version
79 77  
... ...
Procfile
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 2 worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell
... ...
README.md
... ... @@ -71,7 +71,7 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl
71 71  
72 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 76 ### Run in production mode
77 77  
... ... @@ -96,14 +96,9 @@ or start each component separately
96 96  
97 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 99 * Run all tests
105 100  
106   - bundle exec rake gitlab:test RAILS_ENV=test
  101 + bundle exec rake test
107 102  
108 103 * [RSpec](http://rspec.info/) unit and functional tests
109 104  
... ... @@ -134,22 +129,4 @@ or start each component separately
134 129  
135 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.
... ...
VERSION
1   -6.7.0.pre
  1 +6.8.0.pre
... ...
app/assets/javascripts/application.js
... ... @@ -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 @@
  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 6 dataSource: ''
7 7 # Emoji
8 8 Emoji:
9   - assetBase: ''
10 9 template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>'
11 10  
12 11 # Team Members
... ... @@ -27,7 +26,7 @@ GitLab.GfmAutoComplete =
27 26 tpl: @Emoji.template
28 27 callbacks:
29 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 31 # Team Members
33 32 input.atwho
... ...
app/assets/javascripts/main.js.coffee
... ... @@ -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 7 *= require select2
8 8 *= require highlightjs.min
9 9 *= require_self
  10 + *= require nprogress
  11 + *= require nprogress-bootstrap
10 12 */
11 13  
12 14 @import "main/variables.scss";
... ...
app/assets/stylesheets/generic/common.scss
... ... @@ -355,3 +355,7 @@ table {
355 355 @media (max-width: $screen-xs-max) {
356 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 88 .wiki {
89 89 @include md-typography;
90 90  
91   - font-size: 14px;
92   - line-height: 1.6;
93   -
94 91 /* Link to current header. */
95 92 h1, h2, h3, h4, h5, h6 {
96 93 position: relative;
... ... @@ -120,3 +117,11 @@ a:focus {
120 117 .md {
121 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 84 }
85 85  
86 86 @mixin md-typography {
  87 + font-size: 14px;
  88 + line-height: 1.6;
  89 +
87 90 img {
88 91 max-width: 100%;
89 92 }
... ...
app/assets/stylesheets/print.scss 0 → 100644
... ... @@ -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 61 }
62 62  
63 63 .project-row, .group-row {
64   - padding: 10px 12px !important;
  64 + padding: 8px 12px !important;
65 65 font-size: 14px;
66 66 line-height: 24px;
67 67  
... ...
app/assets/stylesheets/sections/diff.scss
... ... @@ -62,6 +62,29 @@
62 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 88 .old_line, .new_line, .diff_line {
66 89 margin: 0px;
67 90 padding: 0px;
... ... @@ -125,8 +148,6 @@
125 148 }
126 149 &.parallel {
127 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 42 }
43 43 }
44 44  
45   - padding: 14px 0px;
  45 + padding: 12px 0px;
46 46 border-bottom: 1px solid #eee;
47 47 .event-title {
48 48 color: #333;
... ... @@ -63,6 +63,10 @@
63 63 color: #666;
64 64 margin-top: 5px;
65 65  
  66 + .md {
  67 + font-size: 13px;
  68 + }
  69 +
66 70 pre {
67 71 border: none;
68 72 background: #f9f9f9;
... ...
app/assets/stylesheets/sections/header.scss
... ... @@ -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 27  
28 28 .discussion-last-update,
29 29 .note-last-update {
30   - font-style: italic;
  30 + &:before {
  31 + content: "\00b7";
  32 + }
  33 + font-size: 13px;
31 34 }
32 35 .author {
33   - color: $style_color;
  36 + color: #555;
34 37 font-weight: bold;
  38 + font-size: 14px;
35 39 &:hover {
36 40 color: $primary_color;
37 41 }
... ...
app/controllers/passwords_controller.rb 0 → 100644
... ... @@ -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 41 begin
42 42 user = User.find_by_username(params[:username])
43 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 45 else
46 46 render_404 and return
47 47 end
... ...
app/controllers/projects/issues_controller.rb
... ... @@ -76,7 +76,7 @@ class Projects::IssuesController &lt; Projects::ApplicationController
76 76 end
77 77  
78 78 def update
79   - @issue.update_attributes(params[:issue].merge(author_id_of_changes: current_user.id))
  79 + @issue.update_attributes(params[:issue])
80 80 @issue.reset_events_cache
81 81  
82 82 respond_to do |format|
... ...
app/controllers/projects/merge_requests_controller.rb
... ... @@ -109,7 +109,7 @@ class Projects::MergeRequestsController &lt; Projects::ApplicationController
109 109 params[:merge_request].delete(:source_project_id)
110 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 113 @merge_request.reset_events_cache
114 114  
115 115 respond_to do |format|
... ...
app/controllers/projects/milestones_controller.rb
... ... @@ -38,7 +38,6 @@ class Projects::MilestonesController &lt; Projects::ApplicationController
38 38  
39 39 def create
40 40 @milestone = @project.milestones.new(params[:milestone])
41   - @milestone.author_id_of_changes = current_user.id
42 41  
43 42 if @milestone.save
44 43 redirect_to project_milestone_path(@project, @milestone)
... ... @@ -48,7 +47,7 @@ class Projects::MilestonesController &lt; Projects::ApplicationController
48 47 end
49 48  
50 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 52 respond_to do |format|
54 53 format.js
... ...
app/controllers/projects_controller.rb
... ... @@ -123,11 +123,20 @@ class ProjectsController &lt; ApplicationController
123 123 end
124 124  
125 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 135 @suggestions = {
127   - emojis: Emoji.names,
  136 + emojis: Emoji.names.map { |e| { name: e, path: view_context.image_url("emoji/#{e}.png") } },
128 137 issues: @project.issues.select([:iid, :title, :description]),
129 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 142 respond_to do |format|
... ... @@ -162,4 +171,25 @@ class ProjectsController &lt; ApplicationController
162 171 def user_layout
163 172 current_user ? "projects" : "public_projects"
164 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 195 end
... ...
app/finders/base_finder.rb
... ... @@ -47,9 +47,9 @@ class BaseFinder
47 47 []
48 48 end
49 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 51 else
52   - klass.of_projects(Project.accessible_to(current_user))
  52 + klass.of_projects(Project.accessible_to(current_user)).references(:project)
53 53 end
54 54 end
55 55  
... ...
app/helpers/commits_helper.rb
... ... @@ -105,8 +105,80 @@ module CommitsHelper
105 105 branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
106 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 182 end
111 183  
112 184 protected
... ...
app/helpers/merge_requests_helper.rb
... ... @@ -20,7 +20,7 @@ module MergeRequestsHelper
20 20 target_project_id: target_project.id,
21 21 source_branch: event.branch_name,
22 22 target_branch: target_project.repository.root_ref,
23   - title: event.branch_name.titleize
  23 + title: event.branch_name.humanize
24 24 }
25 25 end
26 26  
... ...
app/helpers/tree_helper.rb
... ... @@ -40,7 +40,7 @@ module TreeHelper
40 40 # Returns boolean
41 41 def markup?(filename)
42 42 filename.downcase.end_with?(*%w(.textile .rdoc .org .creole
43   - .mediawiki .rst .asciidoc .pod))
  43 + .mediawiki .rst .adoc .asciidoc .pod))
44 44 end
45 45  
46 46 def gitlab_markdown?(filename)
... ...
app/mailers/emails/merge_requests.rb
... ... @@ -29,11 +29,11 @@ module Emails
29 29 subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
30 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 33 @merge_request = MergeRequest.find(merge_request_id)
34 34 @project = @merge_request.project
35 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 37 to: recipient(recipient_id),
38 38 subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
39 39 end
... ...
app/models/concerns/issuable.rb
... ... @@ -37,8 +37,6 @@ module Issuable
37 37 allow_nil: true,
38 38 prefix: true
39 39  
40   - attr_accessor :author_id_of_changes
41   -
42 40 attr_mentionable :title, :description
43 41 end
44 42  
... ...
app/models/diff_line.rb 0 → 100644
... ... @@ -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 47 scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
48 48  
49 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 50 def create_ref_event(project, user, ref, action = 'add', prefix = 'refs/heads')
59 51 commit = project.repository.commit(ref.target)
60 52  
... ...
app/models/issue.rb
... ... @@ -30,8 +30,7 @@ class Issue &lt; ActiveRecord::Base
30 30 scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
31 31  
32 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 35 acts_as_taggable_on :labels
37 36  
... ...
app/models/merge_request.rb
... ... @@ -38,7 +38,7 @@ class MergeRequest &lt; ActiveRecord::Base
38 38  
39 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 43 attr_accessor :should_remove_source_branch
44 44  
... ...
app/models/milestone.rb
... ... @@ -16,8 +16,7 @@
16 16 class Milestone < ActiveRecord::Base
17 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 21 belongs_to :project
23 22 has_many :issues
... ... @@ -89,6 +88,6 @@ class Milestone &lt; ActiveRecord::Base
89 88 end
90 89  
91 90 def author_id
92   - author_id_of_changes
  91 + nil
93 92 end
94 93 end
... ...
app/models/note.rb
... ... @@ -199,7 +199,8 @@ class Note &lt; ActiveRecord::Base
199 199 def downvote?
200 200 votable? && (note.start_with?('-1') ||
201 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 205 end
205 206  
... ... @@ -249,7 +250,8 @@ class Note &lt; ActiveRecord::Base
249 250 def upvote?
250 251 votable? && (note.start_with?('+1') ||
251 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 256 end
255 257  
... ...
app/models/project.rb
... ... @@ -56,6 +56,7 @@ class Project &lt; ActiveRecord::Base
56 56 has_one :flowdock_service, dependent: :destroy
57 57 has_one :assembla_service, dependent: :destroy
58 58 has_one :gemnasium_service, dependent: :destroy
  59 + has_one :slack_service, dependent: :destroy
59 60 has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
60 61 has_one :forked_from_project, through: :forked_project_link
61 62 # Merge Requests for target project should be removed with it
... ... @@ -304,7 +305,7 @@ class Project &lt; ActiveRecord::Base
304 305 end
305 306  
306 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 309 end
309 310  
310 311 def gitlab_ci?
... ...
app/models/project_services/slack_message.rb 0 → 100644
... ... @@ -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 @@
  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 204 end
205 205  
206 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 208 end
209 209  
210 210 def by_username_or_id(name_or_id)
... ... @@ -249,7 +249,7 @@ class User &lt; ActiveRecord::Base
249 249 def namespace_uniq
250 250 namespace_name = self.username
251 251 if Namespace.find_by(path: namespace_name)
252   - self.errors.add :username, "already exist"
  252 + self.errors.add :username, "already exists"
253 253 end
254 254 end
255 255  
... ...
app/observers/activity_observer.rb
... ... @@ -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 3 NotificationService.new
4 4 end
5 5  
  6 + def event_service
  7 + EventCreateService.new
  8 + end
  9 +
6 10 def log_info message
7 11 Gitlab::AppLogger.info message
8 12 end
... ...
app/observers/issue_observer.rb
1 1 class IssueObserver < BaseObserver
2 2 def after_create(issue)
3 3 notification.new_issue(issue, current_user)
  4 + event_service.open_issue(issue, current_user)
4 5 issue.create_cross_references!(issue.project, current_user)
5 6 execute_hooks(issue)
6 7 end
7 8  
8 9 def after_close(issue, transition)
9 10 notification.close_issue(issue, current_user)
  11 + event_service.close_issue(issue, current_user)
10 12 create_note(issue)
11 13 execute_hooks(issue)
12 14 end
13 15  
14 16 def after_reopen(issue, transition)
  17 + event_service.reopen_issue(issue, current_user)
15 18 create_note(issue)
16 19 execute_hooks(issue)
17 20 end
... ...
app/observers/merge_request_observer.rb
1   -class MergeRequestObserver < ActivityObserver
2   - observe :merge_request
3   -
  1 +class MergeRequestObserver < BaseObserver
4 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 4 notification.new_merge_request(merge_request, current_user)
10 5 merge_request.create_cross_references!(merge_request.project, current_user)
11 6 execute_hooks(merge_request)
12 7 end
13 8  
14 9 def after_close(merge_request, transition)
15   - create_event(merge_request, Event::CLOSED)
  10 + event_service.close_mr(merge_request, current_user)
16 11 notification.close_mr(merge_request, current_user)
17 12 create_note(merge_request)
18 13 execute_hooks(merge_request)
19 14 end
20 15  
21 16 def after_reopen(merge_request, transition)
22   - create_event(merge_request, Event::REOPENED)
  17 + event_service.reopen_mr(merge_request, current_user)
23 18 create_note(merge_request)
24 19 execute_hooks(merge_request)
25 20 merge_request.reload_code
... ... @@ -33,16 +28,6 @@ class MergeRequestObserver &lt; ActivityObserver
33 28 execute_hooks(merge_request)
34 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 31 private
47 32  
48 33 # Create merge request note with service comment like 'Status changed to closed'
... ...
app/observers/milestone_observer.rb 0 → 100644
... ... @@ -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 2 def after_create(note)
3 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 11 unless note.system?
6 12 # Create a cross-reference note if this Note contains GFM that names an
7 13 # issue, merge request, or commit.
... ...
app/services/event_create_service.rb 0 → 100644
... ... @@ -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 9 merge_request.lock
10 10  
11 11 if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message)
12   - merge_request.author_id_of_changes = current_user.id
13 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 16 execute_project_hooks(merge_request)
18 17  
19 18 true
... ...
app/services/merge_requests/base_merge_service.rb
... ... @@ -7,14 +7,8 @@ module MergeRequests
7 7 NotificationService.new
8 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 12 end
19 13  
20 14 def execute_project_hooks(merge_request)
... ...
app/services/merge_requests/merge_service.rb
... ... @@ -7,11 +7,10 @@ module MergeRequests
7 7 # to target branch
8 8 class MergeService < BaseMergeService
9 9 def execute(merge_request, current_user, commit_message)
10   - merge_request.author_id_of_changes = current_user.id
11 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 14 execute_project_hooks(merge_request)
16 15  
17 16 true
... ...
app/services/notification_service.rb
... ... @@ -86,12 +86,12 @@ class NotificationService
86 86 # * merge_request assignee if their notification level is not Disabled
87 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 90 recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project)
91 91 recipients = recipients.concat(project_watchers(merge_request.target_project)).uniq
92 92  
93 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 95 end
96 96 end
97 97  
... ... @@ -111,6 +111,7 @@ class NotificationService
111 111  
112 112 # ignore gitlab service messages
113 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 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 2 - if providers.present?
3 3 %hr
4 4 %div{:'data-no-turbolink' => 'data-no-turbolink'}
5   - %span Sign in with: &nbsp;
  5 + %span Sign in with*: &nbsp;
6 6 - providers.each do |provider|
7 7 %span
8 8 - if default_providers.include?(provider)
9 9 = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
10 10 - else
11 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 4 = "#{title} | " if defined?(title)
5 5 GitLab
6 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 9 = javascript_include_tag "application"
9 10 = csrf_meta_tags
10 11 = include_gon
... ...
app/views/layouts/_head_panel.html.haml
... ... @@ -15,10 +15,6 @@
15 15 .navbar-collapse.collapse
16 16 %ul.nav.navbar-nav
17 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 18 = render "layouts/search"
23 19 %li.visible-sm.visible-xs
24 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 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 3 GitLab.GfmAutoComplete.setup();
... ...
app/views/layouts/_public_head_panel.html.haml
... ... @@ -8,11 +8,15 @@
8 8 %span.separator
9 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 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 4 %body{class: "#{app_theme} application", :'data-page' => body_data_page}
5 5 = render "layouts/broadcast"
6 6 = render "layouts/public_head_panel", title: project_title(@project)
7   - %nav.main-nav
  7 + %nav.main-nav.navbar-collapse.collapse
8 8 .container= render 'layouts/nav/project'
9 9 .container
10 10 .content= yield
... ...
app/views/notify/repository_push_email.html.haml
... ... @@ -7,7 +7,7 @@
7 7 %li
8 8 #{commit.short_id} - #{commit.title}
9 9  
10   -%h4 Diff:
  10 +%h4 Changes:
11 11 - @diffs.each do |diff|
12 12 %li
13 13 %strong
... ... @@ -23,6 +23,6 @@
23 23 %br
24 24  
25 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 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 6 #{commit.short_id} - #{truncate(commit.title, length: 40)}
7 7 \
8 8 \
9   -Diff:
  9 +Changes:
10 10 - @diffs.each do |diff|
11 11 \
12 12 \=====================================
... ... @@ -22,4 +22,4 @@ Diff:
22 22 - if @compare.timeout
23 23 Huge diff. To prevent performance issues it was hidden
24 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 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 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 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 1 - too_big = diff.diff.lines.count > 1000
2 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 5 %table.text-file{class: "#{'hide' if too_big}"}
6 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 18 - else
19 19 %ul.well-list= render Commit.decorate(@commits), project: @project
20 20  
21   - %h4 Diff
  21 + %h4 Changes
22 22 - if @diffs.present?
23 23 = render "projects/commits/diffs", diffs: @diffs, project: @project
24 24 - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
25 25 .bs-callout.bs-callout-danger
26 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 28 - elsif @timeout
29 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 34 - else
... ...
app/views/projects/deploy_keys/show.html.haml
... ... @@ -2,7 +2,7 @@
2 2 Deploy key:
3 3 = @key.title
4 4 %small
5   - created at
  5 + created on
6 6 = @key.created_at.stamp("Aug 21, 2011")
7 7 .back-link
8 8 = link_to project_deploy_keys_path(@project) do
... ...
app/views/projects/issues/_form.html.haml
1 1 %div.issue-form-holder
2 2 %h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}"
3 3 %hr
4   - - if @repository.contribution_guide && !@issue.persisted?
  4 + - if !@repository.empty? && @repository.contribution_guide && !@issue.persisted?
5 5 - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
6 6 .alert.alert-info.col-sm-10.col-sm-offset-2
7 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 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 17 .merge-request-branches
13 18 .form-group
... ... @@ -47,24 +52,6 @@
47 52 = f.text_area :description, class: "form-control js-gfm-input", rows: 14
48 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 55 .form-actions
69 56 - if @merge_request.new_record?
70 57 = f.submit 'Submit merge request', class: "btn btn-create"
... ... @@ -96,7 +83,3 @@
96 83 target_branch.on("change", function() {
97 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 20 %li.diffs-tab{data: {action: 'diffs'}}
21 21 = link_to diffs_project_merge_request_path(@project, @merge_request) do
22 22 %i.icon-list-alt
23   - Diff
  23 + Changes
24 24  
25 25 - content_for :note_actions do
26 26 - if can?(current_user, :modify_merge_request, @merge_request)
... ...
app/views/projects/merge_requests/branch_from.js.haml
... ... @@ -3,5 +3,5 @@
3 3 var mrTitle = $('#merge_request_title');
4 4  
5 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 5 - else
6 6 .bs-callout.bs-callout-warning
7 7 %h4
8   - Diff for this comparison is extremely large.
  8 + Changes view for this comparison is extremely large.
9 9 %p
10 10 You can
11 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 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 13 - if @show_merge_controls
... ...
app/views/projects/merge_requests/show/_mr_box.html.haml
... ... @@ -29,13 +29,13 @@
29 29 %span
30 30 %i.icon-remove
31 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 33 - if @merge_request.merged?
34 34 .alert.alert-info
35 35 %span
36 36 %i.icon-ok
37 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 39 - if !@closes_issues.empty? && @merge_request.open?
40 40 .alert.alert-info.alert-info
41 41 %span
... ...
app/views/projects/notes/_note.html.haml
... ... @@ -23,7 +23,7 @@
23 23 %i.icon-thumbs-up
24 24 \+1
25 25 - if note.downvote?
26   - %span.vote.downvote.label.label-error
  26 + %span.vote.downvote.label.label-danger
27 27 %i.icon-thumbs-down
28 28 \-1
29 29  
... ...
app/views/projects/show.html.haml
... ... @@ -9,8 +9,8 @@
9 9 .col-md-3.project-side.hidden-sm
10 10 .clearfix
11 11 - if @project.archived?
12   - .alert
13   - %h5
  12 + .alert.alert-warning
  13 + %h4
14 14 %i.icon-warning-sign
15 15 Archived project!
16 16 %p Repository is read-only
... ...
config/application.rb
... ... @@ -19,7 +19,7 @@ module Gitlab
19 19 # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
20 20  
21 21 # Activate observers that should always be running.
22   - config.active_record.observers = :activity_observer,
  22 + config.active_record.observers = :milestone_observer,
23 23 :project_activity_cache_observer,
24 24 :issue_observer,
25 25 :key_observer,
... ...
config/environments/production.rb
... ... @@ -33,6 +33,12 @@ Gitlab::Application.configure do
33 33 # See everything in the log (default is :info)
34 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 42 # Prepend all log lines with the following tags
37 43 # config.log_tags = [ :subdomain, :uuid ]
38 44  
... ...
config/initializers/gemoji.rb
... ... @@ -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 167  
168 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 173 # Project Area
... ...
config/unicorn_development.rb 0 → 100644
... ... @@ -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 2 User.first(30).each_with_index do |user, i|
3 3 Key.seed(:id, [
4 4 {
5   - id: i,
  5 + id: i + 1,
6 6 title: "Sample key #{i}",
7 7 key: "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt#{i + 100}6k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
8 8 user_id: user.id,
... ...
doc/README.md 0 → 100644
... ... @@ -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 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 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 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 143  
118 144 So if you want to get issue with api you use `http://host/api/v3/.../issues/:id.json`
119 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 150 + `target_branch` - The target branch
151 151 + `assignee_id` - Assignee user ID
152 152 + `title` - Title of MR
  153 ++ `state_event` - New state (close|reopen|merge)
153 154  
154 155 ```json
155 156  
... ... @@ -210,3 +211,44 @@ Parameters:
210 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 621 + query (required) - A string contained in the project name
622 622 + per_page (optional) - number of projects to return per page
623 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 @@
  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 18  
19 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 23 GitLab is primarily installed within the `/home/git` user home directory as `git` user.
24 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 28  
29 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 33 A typical install of GitLab will be on Ubuntu Linux or RHEL/CentOS.
34 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 180 ```
181 181  
182 182 Note: It is recommended to log into the `git` user using `sudo -i -u git` or `sudo su - git`.
183 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 185 \ No newline at end of file
... ...
doc/development/shell_commands.md
1 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 9 ## Use File and FileUtils instead of shell commands
4 10  
5 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 @@
  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  
7 7 # Install the database packages
8 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 13 # Pick a database root password (can be anything), type it and press enter
11 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 26 # change $password in the command below to a real password you pick
24 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 33 # Create the GitLab production database
27 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 128 cd /home/git
129 129  
130 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 133 cd gitlab-shell
134 134  
... ... @@ -173,7 +173,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
173 173 ## Clone the Source
174 174  
175 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 178 # Go to gitlab dir
179 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 5 - textual references to PROJECT-1234 in comments, commit messages get turned into HTML links to the corresponding JIRA issue.
6 6  
7 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 @@
  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 38 |------|-----|--------|---------|------|-----|
39 39 |Browse group|✓|✓|✓|✓|✓|
40 40 |Edit group|||||✓|
41   -|create project in group|||||✓|
  41 +|Create project in group|||||✓|
42 42 |Manage group members|||||✓|
43 43 |Remove group|||||✓|
44 44  
... ...
doc/raketasks/README.md 0 → 100644
... ... @@ -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 @@
  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 58  
59 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 66 * 22nd: release (VERSION x.x.0, create x-x-stable branch, tag, blog and tweet)
67 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 70 # Write a blog post
73 71  
74 72 * Mention what GitLab is on the second line: GitLab is open source software to collaborate on code.
75 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 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 @@
  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 @@
  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 1 # From 4.2 to 5.0
2 2  
3 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 6 ## Important changes
7 7  
... ...
doc/update/5.0-to-5.1.md
1 1 # From 5.0 to 5.1
2 2  
3 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 6 ## Release notes:
7 7  
... ...
doc/update/5.1-to-5.2.md
1 1 # From 5.1 to 5.2
2 2  
3 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 6 ### 0. Backup
7 7  
... ...
doc/update/5.1-to-5.4.md
1 1 # From 5.1 to 5.4
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 4 ### 0. Backup
8 5  
9 6 It's useful to make a backup just in case things go south:
... ...
doc/update/5.1-to-6.0.md
1 1 # From 5.1 to 6.0
2 2  
3 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 6 ### Deprecations
7 7  
... ...
doc/update/5.2-to-5.3.md
1 1 # From 5.2 to 5.3
2 2  
3 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 6 ### 0. Backup
7 7  
... ...
doc/update/5.3-to-5.4.md
1 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 3 ### 0. Backup
7 4  
8 5 It's useful to make a backup just in case things go south:
... ...
doc/update/5.4-to-6.0.md
1 1 # From 5.4 to 6.0
2 2  
3 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 6 ### Deprecations
7 7  
... ...
doc/update/6.0-to-6.1.md
1 1 # From 6.0 to 6.1
2 2  
3 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 6 # In 6.1 we remove a lot of deprecated code.
7 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   -# 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 @@
  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 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 3 # You should update to 6.1 before installing 6.2 so all the necessary conversions are run.
7 4  
8 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 37 ```bash
38 38 cd /home/git/gitlab-shell
39 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 43 ### 4. Install libs, migrations, etc.
... ...
doc/update/README.md 0 → 100644
... ... @@ -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 5 You can configure web hook to listen for specific events like pushes, issues, merge requests.
6 6 GitLab will send POST request with data to web hook URL.
7 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 37 And I fill Assembla settings
38 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 46 Scenario: Activate email on push service
41 47 When I visit project "Shop" services page
42 48 And I click email on push service link
... ...
features/steps/dashboard/dashboard.rb
... ... @@ -25,7 +25,7 @@ class Dashboard &lt; Spinach::FeatureSteps
25 25 find("#merge_request_target_project_id").value.should == @project.id.to_s
26 26 find("#merge_request_source_branch").value.should == "new_design"
27 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 29 end
30 30  
31 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 4 include SharedProject
5 5  
6 6 step 'public project "Community"' do
7   - create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC
  7 + create :project, :public, name: 'Community'
8 8 end
9 9  
10 10 step 'private project "Enterprise"' do
... ...
features/steps/project/services.rb
... ... @@ -100,4 +100,22 @@ class ProjectServices &lt; Spinach::FeatureSteps
100 100 step 'I should see email on push service settings saved' do
101 101 find_field('Recipients').value.should == 'qa@company.name'
102 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 121 end
... ...
features/steps/public/projects.rb
... ... @@ -4,7 +4,7 @@ class Spinach::Features::PublicProjectsFeature &lt; Spinach::FeatureSteps
4 4 include SharedProject
5 5  
6 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 8 end
9 9  
10 10 step 'I should see project "Empty Public Project"' do
... ...
features/steps/shared/project.rb
... ... @@ -79,7 +79,7 @@ module SharedProject
79 79 end
80 80  
81 81 step 'internal project "Internal"' do
82   - create :project, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL
  82 + create :project, :internal, name: 'Internal'
83 83 end
84 84  
85 85 step 'I should see project "Internal"' do
... ... @@ -91,7 +91,7 @@ module SharedProject
91 91 end
92 92  
93 93 step 'public project "Community"' do
94   - create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC
  94 + create :project, :public, name: 'Community'
95 95 end
96 96  
97 97 step 'I should see project "Community"' do
... ... @@ -112,14 +112,14 @@ module SharedProject
112 112 step '"John Doe" is authorized to internal project "Internal"' do
113 113 user = user_exists("John Doe", username: "john_doe")
114 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 116 project.team << [user, :master]
117 117 end
118 118  
119 119 step '"John Doe" is authorized to public project "Community"' do
120 120 user = user_exists("John Doe", username: "john_doe")
121 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 123 project.team << [user, :master]
124 124 end
125 125 end
... ...
lib/api/entities.rb
... ... @@ -187,5 +187,9 @@ module API
187 187 end
188 188 end
189 189 end
  190 +
  191 + class Label < Grape::Entity
  192 + expose :name
  193 + end
190 194 end
191 195 end
... ...
lib/api/internal.rb
1 1 module API
2 2 # Internal access API
3 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 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 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 10 # project - project path with namespace
15 11 # action - git action (git-upload-pack or git-receive-pack)
16 12 # ref - branch name
... ... @@ -22,43 +18,25 @@ module API
22 18 # the wiki repository as well.
23 19 project_path = params[:project]
24 20 project_path.gsub!(/\.wiki/,'') if project_path =~ /\.wiki/
25   -
26   - key = Key.find(params[:key_id])
27 21 project = Project.find_with_namespace(project_path)
28   - git_cmd = params[:action]
29 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 40 end
63 41  
64 42 #
... ...
lib/api/merge_requests.rb
... ... @@ -125,6 +125,22 @@ module API
125 125 end
126 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 144 # Post comment to merge request
129 145 #
130 146 # Parameters:
... ...
lib/api/projects.rb
... ... @@ -215,6 +215,17 @@ module API
215 215 @users = paginate @users
216 216 present @users, with: Entities::User
217 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 229 end
219 230 end
220 231 end
... ...
lib/gitlab/backend/grack_auth.rb
1 1 require_relative 'shell_env'
2   -require_relative 'grack_helpers'
3 2  
4 3 module Grack
5 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 8 def call(env)
11 9 @env = env
... ... @@ -24,14 +22,16 @@ module Grack
24 22  
25 23 @env['SCRIPT_NAME'] = ""
26 24  
27   - auth!
  25 + if project
  26 + auth!
  27 + else
  28 + render_not_found
  29 + end
28 30 end
29 31  
30 32 private
31 33  
32 34 def auth!
33   - return render_not_found unless project
34   -
35 35 if @auth.provided?
36 36 return bad_request unless @auth.basic?
37 37  
... ... @@ -40,12 +40,8 @@ module Grack
40 40  
41 41 # Allow authentication for GitLab CI service
42 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 45 end
50 46  
51 47 @user = authenticate_user(login, password)
... ... @@ -53,23 +49,26 @@ module Grack
53 49 if @user
54 50 Gitlab::ShellEnv.set_env(@user)
55 51 @env['REMOTE_USER'] = @auth.username
56   - else
57   - return unauthorized
58 52 end
59   -
60   - else
61   - return unauthorized unless project.public?
62 53 end
63 54  
64   - if authorized_git_request?
  55 + if authorized_request?
65 56 @app.call(env)
66 57 else
67 58 unauthorized
68 59 end
69 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 72 end
74 73  
75 74 def authenticate_user(login, password)
... ... @@ -77,31 +76,31 @@ module Grack
77 76 auth.find(login, password)
78 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 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 98 else
100 99 false
101 100 end
102 101 end
103 102  
104   - def service_name
  103 + def git_cmd
105 104 if @request.get?
106 105 @request.params['service']
107 106 elsif @request.post?
... ... @@ -115,28 +114,17 @@ module Grack
115 114 @project ||= project_by_path(@request.path_info)
116 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 123 end
140 124 end
  125 +
  126 + def render_not_found
  127 + [404, {"Content-Type" => "text/plain"}, ["Not Found"]]
  128 + end
141 129 end
142 130 end
... ...
lib/gitlab/backend/grack_helpers.rb
... ... @@ -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 @@
  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 152 #
153 153 # Returns boolean
154 154 def valid_emoji?(emoji)
155   - Emoji.names.include? emoji
  155 + Emoji.find_by_name emoji
156 156 end
157 157  
158 158 # Private: Dispatches to a dedicated processing method based on reference
... ... @@ -166,8 +166,8 @@ module Gitlab
166 166 end
167 167  
168 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 171 end
172 172 end
173 173  
... ...
lib/gitlab/satellite/satellite.rb
1 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 8 module Satellite
5 9 class Satellite
... ... @@ -17,14 +21,9 @@ module Gitlab
17 21 Gitlab::Satellite::Logger.error(message)
18 22 end
19 23  
20   - def raise_no_satellite
21   - raise SatelliteNotExistError.new("Satellite doesn't exist")
22   - end
23   -
24 24 def clear_and_update!
25   - raise_no_satellite unless exists?
  25 + raise SatelliteNotExistError unless exists?
26 26  
27   - File.exists? path
28 27 @repo = nil
29 28 clear_working_dir!
30 29 delete_heads!
... ... @@ -55,7 +54,7 @@ module Gitlab
55 54 # * Changes the current directory to the satellite's working dir
56 55 # * Yields
57 56 def lock
58   - raise_no_satellite unless exists?
  57 + raise SatelliteNotExistError unless exists?
59 58  
60 59 File.open(lock_file, "w+") do |f|
61 60 begin
... ... @@ -77,7 +76,7 @@ module Gitlab
77 76 end
78 77  
79 78 def repo
80   - raise_no_satellite unless exists?
  79 + raise SatelliteNotExistError unless exists?
81 80  
82 81 @repo ||= Grit::Repo.new(path)
83 82 end
... ...
lib/gitlab/upgrader.rb
  1 +require_relative "popen"
1 2 require_relative "version_info"
2 3  
3 4 module Gitlab
... ...
lib/support/init.d/gitlab
... ... @@ -40,7 +40,7 @@ test -f /etc/default/gitlab &amp;&amp; . /etc/default/gitlab
40 40  
41 41 # Switch to the app_user if it is not he/she who is running the script.
42 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 44 fi
45 45  
46 46 # Switch to the gitlab path, exit on failure.
... ... @@ -131,7 +131,7 @@ check_stale_pids(){
131 131 fi
132 132 fi
133 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 135 if ! rm "$sidekiq_pid_path"; then
136 136 echo "Unable to remove stale pid, exiting"
137 137 exit 1
... ... @@ -149,15 +149,15 @@ exit_if_not_running(){
149 149 }
150 150  
151 151 ## Starts Unicorn and Sidekiq if they're not running.
152   -start() {
  152 +start_gitlab() {
153 153 check_stale_pids
154 154  
155 155 if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then
156 156 echo -n "Starting both the GitLab Unicorn and Sidekiq"
157 157 elif [ "$web_status" != "0" ]; then
158   - echo -n "Starting GitLab Sidekiq"
159   - elif [ "$sidekiq_status" != "0" ]; then
160 158 echo -n "Starting GitLab Unicorn"
  159 + elif [ "$sidekiq_status" != "0" ]; then
  160 + echo -n "Starting GitLab Sidekiq"
161 161 fi
162 162  
163 163 # Then check if the service is running. If it is: don't start again.
... ... @@ -167,7 +167,7 @@ start() {
167 167 # Remove old socket if it exists
168 168 rm -f "$socket_path"/gitlab.socket 2>/dev/null
169 169 # Start the web server
170   - RAILS_ENV=$RAILS_ENV script/web start &
  170 + RAILS_ENV=$RAILS_ENV script/web start
171 171 fi
172 172  
173 173 # If sidekiq is already running, don't start it again.
... ... @@ -184,15 +184,15 @@ start() {
184 184 }
185 185  
186 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 188 exit_if_not_running
189 189  
190 190 if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then
191 191 echo -n "Shutting down both Unicorn and Sidekiq"
192 192 elif [ "$web_status" = "0" ]; then
193   - echo -n "Shutting down Sidekiq"
194   - elif [ "$sidekiq_status" = "0" ]; then
195 193 echo -n "Shutting down Unicorn"
  194 + elif [ "$sidekiq_status" = "0" ]; then
  195 + echo -n "Shutting down Sidekiq"
196 196 fi
197 197  
198 198 # If the Unicorn web server is running, tell it to stop;
... ... @@ -246,7 +246,7 @@ print_status() {
246 246 }
247 247  
248 248 ## Tells unicorn to reload it's config and Sidekiq to restart
249   -reload(){
  249 +reload_gitlab(){
250 250 exit_if_not_running
251 251 if [ "$wpid" = "0" ];then
252 252 echo "The GitLab Unicorn Web server is not running thus its configuration can't be reloaded."
... ... @@ -263,12 +263,12 @@ reload(){
263 263 }
264 264  
265 265 ## Restarts Sidekiq and Unicorn.
266   -restart(){
  266 +restart_gitlab(){
267 267 check_status
268 268 if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then
269   - stop
  269 + stop_gitlab
270 270 fi
271   - start
  271 + start_gitlab
272 272 }
273 273  
274 274  
... ... @@ -276,16 +276,16 @@ restart(){
276 276  
277 277 case "$1" in
278 278 start)
279   - start
  279 + start_gitlab
280 280 ;;
281 281 stop)
282   - stop
  282 + stop_gitlab
283 283 ;;
284 284 restart)
285   - restart
  285 + restart_gitlab
286 286 ;;
287 287 reload|force-reload)
288   - reload
  288 + reload_gitlab
289 289 ;;
290 290 status)
291 291 print_status
... ...
lib/support/nginx/gitlab
... ... @@ -54,6 +54,14 @@ server {
54 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 65 error_page 502 /502.html;
58 66 }
59 67  
... ...
lib/tasks/gitlab/check.rake
... ... @@ -677,7 +677,20 @@ namespace :gitlab do
677 677 end
678 678  
679 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 694 end
682 695  
683 696 def ldap
... ... @@ -742,7 +755,7 @@ namespace :gitlab do
742 755 end
743 756  
744 757 def check_gitlab_shell
745   - required_version = Gitlab::VersionInfo.new(1, 8, 5)
  758 + required_version = Gitlab::VersionInfo.new(1, 9, 1)
746 759 current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
747 760  
748 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 sidekiq_logfile="$app_root/log/sidekiq.log"
7 7 gitlab_user=$(ls -l config.ru | awk '{print $3}')
8 8  
  9 +function warn
  10 +{
  11 + echo "$@" 1>&2
  12 +}
  13 +
9 14 function stop
10 15 {
11 16 bundle exec sidekiqctl stop $sidekiq_pidfile >> $sidekiq_logfile 2>&1
... ... @@ -35,6 +40,22 @@ function start_sidekiq
35 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 59 case "$1" in
39 60 stop)
40 61 stop
... ... @@ -51,6 +72,9 @@ case &quot;$1&quot; in
51 72 killall)
52 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 80 esac
... ...
spec/controllers/profile_keys_controller_spec.rb
... ... @@ -24,6 +24,11 @@ describe Profiles::KeysController do
24 24  
25 25 expect(response.body).to eq("")
26 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 32 end
28 33  
29 34 describe "user with keys" do
... ... @@ -44,6 +49,11 @@ describe Profiles::KeysController do
44 49 expect(response.body).not_to eq("")
45 50 expect(response.body).to eq(user.all_ssh_keys.join("\n"))
46 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 57 end
48 58 end
49 59 end
... ...
spec/factories.rb
... ... @@ -32,6 +32,18 @@ FactoryGirl.define do
32 32 path { name.downcase.gsub(/\s/, '_') }
33 33 namespace
34 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 47 end
36 48  
37 49 # Generates a test repository from the repository stored under `spec/seed_project.tar.gz`.
... ... @@ -146,6 +158,11 @@ FactoryGirl.define do
146 158 state :reopened
147 159 end
148 160  
  161 + trait :simple do
  162 + source_branch "simple_merge_request"
  163 + target_branch "master"
  164 + end
  165 +
149 166 factory :closed_merge_request, traits: [:closed]
150 167 factory :reopened_merge_request, traits: [:reopened]
151 168 factory :merge_request_with_diffs, traits: [:with_diffs]
... ... @@ -161,7 +178,6 @@ FactoryGirl.define do
161 178 factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note]
162 179 factory :note_on_merge_request, traits: [:on_merge_request]
163 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 182 trait :on_commit do
167 183 project factory: :project
... ...
spec/features/notes_on_merge_requests_spec.rb
1 1 require 'spec_helper'
2 2  
3 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 8 before do
9   - login_as :user
10   - project.team << [@user, :master]
11   -
  9 + login_as :admin
12 10 visit project_merge_request_path(project, merge_request)
13 11 end
14 12  
... ... @@ -134,22 +132,20 @@ describe &quot;On a merge request&quot;, js: true do
134 132 end
135 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 139 before do
142   - login_as :user
143   - project.team << [@user, :master]
  140 + login_as :admin
144 141 visit diffs_project_merge_request_path(project, merge_request)
145 142 end
146 143  
147   -
148 144 subject { page }
149 145  
150 146 describe "when adding a note" do
151 147 before do
152   - find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185"]').click
  148 + find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7"]').click
153 149 end
154 150  
155 151 describe "the notes holder" do
... ... @@ -160,13 +156,13 @@ describe &quot;On a merge request diff&quot;, js: true, focus: true do
160 156  
161 157 describe "the note form" do
162 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 162 end
167 163  
168 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 166 find(".js-close-discussion-note-form").trigger("click")
171 167 end
172 168  
... ... @@ -176,12 +172,9 @@ describe &quot;On a merge request diff&quot;, js: true, focus: true do
176 172 end
177 173  
178 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 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 178 end
186 179  
187 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 182 describe "previewing them separately" do
190 183 before do
191 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 187 find(".js-note-preview-button").trigger("click")
195 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 191 find(".js-note-preview-button").trigger("click")
199 192 end
200 193 end
... ... @@ -202,14 +195,14 @@ describe &quot;On a merge request diff&quot;, js: true, focus: true do
202 195  
203 196 describe "posting a note" do
204 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 200 click_button("Add Comment")
208 201 end
209 202 end
210 203  
211 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 206 should have_css(".notes_holder")
214 207 should have_css(".notes_holder .note", count: 1)
215 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 16 group.add_user(reporter, Gitlab::Access::REPORTER)
17 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 20 end
21 21  
22 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 16 group.add_user(reporter, Gitlab::Access::REPORTER)
17 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 21 end
22 22  
23 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 16 group.add_user(reporter, Gitlab::Access::REPORTER)
17 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 20 end
21 21  
22 22 describe "GET /groups/:path" do
... ...
spec/features/security/project/internal_access_spec.rb
1 1 require 'spec_helper'
2 2  
3 3 describe "Internal Project Access" do
4   - let(:project) { create(:project) }
  4 + let(:project) { create(:project, :internal) }
5 5  
6 6 let(:master) { create(:user) }
7 7 let(:guest) { create(:user) }
8 8 let(:reporter) { create(:user) }
9 9  
10 10 before do
11   - # internal project
12   - project.visibility_level = Gitlab::VisibilityLevel::INTERNAL
13   - project.save!
14   -
15 11 # full access
16 12 project.team << [master, :master]
17 13  
18 14 # readonly
19 15 project.team << [reporter, :reporter]
20   -
21 16 end
22 17  
23 18 describe "Project should be internal" do
... ...
spec/finders/merge_requests_finder_spec.rb
1 1 require 'spec_helper'
2 2  
3 3 describe MergeRequestsFinder do
4   - let(:user) { create :user }
  4 + let(:user) { create :user }
5 5 let(:user2) { create :user }
  6 +
6 7 let(:project1) { create(:project) }
7 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 14 before do
13 15 project1.team << [user, :master]
... ... @@ -15,13 +17,7 @@ describe MergeRequestsFinder do
15 17 project2.team << [user2, :developer]
16 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 21 it 'should filter by scope' do
26 22 params = { scope: 'authored', state: 'opened' }
27 23 merge_requests = MergeRequestsFinder.new.execute(user, params)
... ...
spec/finders/projects_finder_spec.rb
... ... @@ -4,10 +4,10 @@ describe ProjectsFinder do
4 4 let(:user) { create :user }
5 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 12 context 'non authenticated' do
13 13 subject { ProjectsFinder.new.execute(nil, group: group) }
... ...
spec/mailers/notify_spec.rb
... ... @@ -229,6 +229,7 @@ describe Notify do
229 229 end
230 230  
231 231 context 'for merge requests' do
  232 + let(:merge_author) { create(:user) }
232 233 let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) }
233 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 289 it 'contains a link to the merge request' do
289 290 should have_body_text /#{project_merge_request_path project, merge_request}/
290 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 316 end
293 317 end
294 318 end
... ...
spec/models/concerns/issuable_spec.rb
... ... @@ -25,11 +25,6 @@ describe Issue, &quot;Issuable&quot; do
25 25 it { described_class.should respond_to(:assigned) }
26 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 28 describe ".search" do
34 29 let!(:searchable_issue) { create(:issue, title: "Searchable issue") }
35 30  
... ...
spec/models/project_spec.rb
... ... @@ -47,6 +47,7 @@ describe Project do
47 47 it { should have_many(:hooks).dependent(:destroy) }
48 48 it { should have_many(:protected_branches).dependent(:destroy) }
49 49 it { should have_one(:forked_project_link).dependent(:destroy) }
  50 + it { should have_one(:slack_service).dependent(:destroy) }
50 51 end
51 52  
52 53 describe "Mass assignment" do
... ...
spec/models/slack_message_spec.rb 0 → 100644
... ... @@ -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 @@
  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 292 end
293 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 309 describe 'by_username_or_id' do
296 310 let(:user1) { create(:user, username: 'foo') }
297 311  
... ...
spec/observers/activity_observer_spec.rb
... ... @@ -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 120 end
121 121  
122 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 124 @event = Event.last
125 125 end
126 126  
... ...
spec/requests/api/issues_spec.rb
... ... @@ -100,16 +100,4 @@ describe API::API do
100 100 response.status.should == 405
101 101 end
102 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 103 end
... ...
spec/requests/api/merge_requests_spec.rb
... ... @@ -7,6 +7,7 @@ describe API::API do
7 7 let(:user) { create(:user) }
8 8 let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
9 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 11 before {
11 12 project.team << [user, :reporters]
12 13 }
... ... @@ -205,4 +206,20 @@ describe API::API do
205 206 response.status.should == 404
206 207 end
207 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 225 end
... ...
spec/requests/api/projects_spec.rb
... ... @@ -13,6 +13,7 @@ describe API::API do
13 13 let(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') }
14 14 let(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
15 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 18 describe "GET /projects" do
18 19 before { project }
... ... @@ -133,7 +134,7 @@ describe API::API do
133 134 end
134 135  
135 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 138 post api("/projects", user), project
138 139 json_response['public'].should be_true
139 140 json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
... ... @@ -147,21 +148,21 @@ describe API::API do
147 148 end
148 149  
149 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 152 post api("/projects", user), project
152 153 json_response['public'].should be_false
153 154 json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
154 155 end
155 156  
156 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 159 post api("/projects", user), project
159 160 json_response['public'].should be_false
160 161 json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
161 162 end
162 163  
163 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 166 post api("/projects", user), project
166 167 json_response['public'].should be_false
167 168 json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
... ... @@ -215,7 +216,7 @@ describe API::API do
215 216 end
216 217  
217 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 220 post api("/projects/user/#{user.id}", admin), project
220 221 json_response['public'].should be_true
221 222 json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
... ... @@ -229,21 +230,21 @@ describe API::API do
229 230 end
230 231  
231 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 234 post api("/projects/user/#{user.id}", admin), project
234 235 json_response['public'].should be_false
235 236 json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
236 237 end
237 238  
238 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 241 post api("/projects/user/#{user.id}", admin), project
241 242 json_response['public'].should be_false
242 243 json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
243 244 end
244 245  
245 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 248 post api("/projects/user/#{user.id}", admin), project
248 249 json_response['public'].should be_false
249 250 json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
... ... @@ -490,10 +491,10 @@ describe API::API do
490 491  
491 492 describe :fork_admin do
492 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 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 499 it "shouldn't available for non admin users" do
499 500 post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
... ... @@ -562,10 +563,10 @@ describe API::API do
562 563 let!(:post) { create(:empty_project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
563 564 let!(:pre_post) { create(:empty_project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) }
564 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 571 context "when unauthenticated" do
571 572 it "should return authentication error" do
... ... @@ -632,4 +633,16 @@ describe API::API do
632 633 end
633 634 end
634 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 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 @@
  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 32 describe 'Notes' do
33 33 context 'issue note' do
34 34 let(:issue) { create(:issue, assignee: create(:user)) }
  35 + let(:mentioned_issue) { create(:issue, assignee: issue.assignee) }
35 36 let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced') }
36 37  
37 38 before do
... ... @@ -50,6 +51,13 @@ describe NotificationService do
50 51 notification.new_note(note)
51 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 61 def should_email(user_id)
54 62 Notify.should_receive(:note_issue_email).with(user_id, note.id)
55 63 end
... ... @@ -233,15 +241,15 @@ describe NotificationService do
233 241 should_email(@u_watcher.id)
234 242 should_not_email(@u_participating.id)
235 243 should_not_email(@u_disabled.id)
236   - notification.merge_mr(merge_request)
  244 + notification.merge_mr(merge_request, @u_disabled)
237 245 end
238 246  
239 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 249 end
242 250  
243 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 253 end
246 254 end
247 255 end
... ...
spec/services/project_transfer_service_spec.rb
... ... @@ -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 @@
  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 @@
  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 @@
  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   -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   -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 1 require 'spec_helper'
2 2  
3 3 describe 'Search::GlobalService' do
4   - let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') }
5 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 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 18 describe '#execute' do
20 19 context 'unauthenticated' do
21 20 it 'should return public projects only' do
22 21 context = Search::GlobalService.new(nil, search: "searchable")
23 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 24 end
27 25 end
28 26  
... ... @@ -30,24 +28,19 @@ describe &#39;Search::GlobalService&#39; do
30 28 it 'should return public, internal and private projects' do
31 29 context = Search::GlobalService.new(user, search: "searchable")
32 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 32 end
38 33  
39 34 it 'should return only public & internal projects' do
40 35 context = Search::GlobalService.new(internal_user, search: "searchable")
41 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 38 end
46 39  
47 40 it 'namespace name should be searchable' do
48 41 context = Search::GlobalService.new(user, search: "searchable namespace")
49 42 results = context.execute
50   - results[:projects].should == [found_project]
  43 + results[:projects].should match_array [found_project]
51 44 end
52 45 end
53 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 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 50 end
... ...
spec/support/test_env.rb
... ... @@ -29,7 +29,6 @@ module TestEnv
29 29 disable_mailer if opts[:mailer] == false
30 30 setup_stubs
31 31  
32   -
33 32 clear_test_repo_dir if opts[:init_repos] == true
34 33 setup_test_repos(opts) if opts[:repos] == true
35 34 end
... ... @@ -91,7 +90,7 @@ module TestEnv
91 90 size: 12.45
92 91 )
93 92  
94   - ActivityObserver.any_instance.stub(
  93 + BaseObserver.any_instance.stub(
95 94 current_user: double("current_user", id: 1)
96 95 )
97 96 end
... ... @@ -165,8 +164,7 @@ module TestEnv
165 164  
166 165 def clear_test_repo_dir
167 166 setup_stubs
168   - # Use tmp dir for FS manipulations
169   - repos_path = testing_path()
  167 +
170 168 # Remove tmp/test-git-base-path
171 169 FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
172 170  
... ...