Commit cfc4a2dbe3652180c1d08d5d9e154e28cbb340fb
Exists in
spb-stable
and in
3 other branches
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
.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
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
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 |
... | ... | @@ -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
app/assets/stylesheets/generic/common.scss
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
... | ... | @@ -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
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
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 | } | ... | ... |
... | ... | @@ -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 < 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 < 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 < 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 < 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 < 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 < 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 < 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
app/models/event.rb
... | ... | @@ -47,14 +47,6 @@ class Event < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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? | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 < 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 < 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
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 < 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' | ... | ... |
... | ... | @@ -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 < 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. | ... | ... |
... | ... | @@ -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: | |
5 | + %span Sign in with*: | |
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
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 | - | |
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
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
app/views/projects/show.html.haml
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
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 | ... | ... |
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, | ... | ... |
... | ... | @@ -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/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's an add-on application called gitlab-shell |
28 | 28 | |
29 | 29 | ## Components |
30 | 30 | |
31 | - | |
31 | + | |
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/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 |  |
8 | + | |
9 | +You can configure the integration in the gitlab.yml configuration file. | ... | ... |
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/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. | ... | ... |
... | ... | @@ -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
doc/update/5.0-to-5.1.md
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
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
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) |
... | ... | @@ -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
doc/update/6.6-to-6.7.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 < 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 < 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 < 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 < 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
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 |
... | ... | @@ -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
lib/support/init.d/gitlab
... | ... | @@ -40,7 +40,7 @@ test -f /etc/default/gitlab && . /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="$app_root/tmp/pids/sidekiq.pid" |
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 "$1" 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 "On a merge request", 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 "On a merge request diff", 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 "On a merge request diff", 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 "On a merge request diff", 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 "On a merge request diff", 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 "Group with internal project access" 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 "Group access" 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 "Group with public project access" 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, "Issuable" 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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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
... | ... | @@ -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 | - |
... | ... | @@ -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 | + | ... | ... |
... | ... | @@ -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 | + | ... | ... |
... | ... | @@ -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 'Search::GlobalService' 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 | ... | ... |