Commit cfc4a2db authored by Franz-Robert van Vugt's avatar Franz-Robert van Vugt

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

parents f03820eb fc5ac145
--color --drb --color
...@@ -19,10 +19,10 @@ rvm: ...@@ -19,10 +19,10 @@ rvm:
services: services:
- redis-server - redis-server
before_script: before_script:
- "bundle exec rake db:setup"
- "bundle exec rake db:seed_fu"
- "cp config/database.yml.$DB config/database.yml" - "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml" - "cp config/gitlab.yml.example config/gitlab.yml"
- "bundle exec rake db:setup"
- "bundle exec rake db:seed_fu"
script: "bundle exec rake $TASK --trace" script: "bundle exec rake $TASK --trace"
notifications: notifications:
email: false email: false
v 6.8.0
- Ability to at mention users that are participating in issue and merge req. discussion
- 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)
- Make user search case-insensitive (Christopher Arnold)
v 6.7.2
- Fix upgrader script
v 6.7.1
- Fix GitLab CI integration
v 6.7.0 v 6.7.0
- Increased the example Nginx client_max_body_size from 5MB to 20MB, consider updating it manually on existing installations - Increased the example Nginx client_max_body_size from 5MB to 20MB, consider updating it manually on existing installations
- Add support for Gemnasium as a Project Service (Olivier Gonzalez) - Add support for Gemnasium as a Project Service (Olivier Gonzalez)
...@@ -9,6 +20,7 @@ v 6.7.0 ...@@ -9,6 +20,7 @@ v 6.7.0
- Show contribution guide link for new issue form (Jeroen van Baarsen) - Show contribution guide link for new issue form (Jeroen van Baarsen)
- Fix CI status for merge requests from fork - Fix CI status for merge requests from fork
- Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard) - Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard)
- New page load indicator that includes a spinner that scrolls with the page
- Converted all the help sections into markdown - Converted all the help sections into markdown
- LDAP user filters - LDAP user filters
- Streamline the content of notification emails (Pierre de La Morinerie) - Streamline the content of notification emails (Pierre de La Morinerie)
...@@ -24,6 +36,12 @@ v 6.7.0 ...@@ -24,6 +36,12 @@ v 6.7.0
- Faster authorized_keys rebuilding in `rake gitlab:shell:setup` (requires gitlab-shell 1.8.5) - Faster authorized_keys rebuilding in `rake gitlab:shell:setup` (requires gitlab-shell 1.8.5)
- Create and Update MR calls now support the description parameter (Greg Messner) - Create and Update MR calls now support the description parameter (Greg Messner)
- Markdown relative links in the wiki link to wiki pages, markdown relative links in repositories link to files in the repository - Markdown relative links in the wiki link to wiki pages, markdown relative links in repositories link to files in the repository
- Added Slack service integration (Federico Ravasio)
- Better API responses for access_levels (sponsored by O'Reilly Media)
- Requires at least 2 unicorn workers
- Requires gitlab-shell v1.9+
- 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)
- Fix `/:username.keys` response content type (Dmitry Medvinsky)
v 6.6.5 v 6.6.5
- Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard) - Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard)
......
...@@ -24,9 +24,12 @@ Issues and merge requests should be in English and contain appropriate language ...@@ -24,9 +24,12 @@ Issues and merge requests should be in English and contain appropriate language
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/). 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/).
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. 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).
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.
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.
Do not use the issue tracker for feature requests. We have a specific [feedback and suggestions forum](http://feedback.gitlab.com) for this purpose. Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose.
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. 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.
...@@ -48,7 +51,7 @@ Please send a merge request with a tested solution or a merge request with a fai ...@@ -48,7 +51,7 @@ Please send a merge request with a tested solution or a merge request with a fai
## Merge requests ## Merge requests
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. 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.
### Merge request guidelines ### Merge request guidelines
...@@ -64,7 +67,8 @@ If you can, please submit a merge request with the fix or improvements including ...@@ -64,7 +67,8 @@ If you can, please submit a merge request with the fix or improvements including
1. The MR title should describes the change you want to make 1. The MR title should describes the change you want to make
1. The MR description should give a motive for your change and the method you used to achieve it 1. The MR description should give a motive for your change and the method you used to achieve it
1. If the MR changes the UI it should include before and after screenshots 1. If the MR changes the UI it should include before and after screenshots
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 1. If the MR changes CSS classes please include the list of affected pages `grep css-class ./app -R`
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
1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submittion 1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submittion
1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md). 1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md).
...@@ -74,6 +78,14 @@ Please keep the change in a single MR **as small as possible**. If you want to c ...@@ -74,6 +78,14 @@ Please keep the change in a single MR **as small as possible**. If you want to c
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. 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.
**Please format your merge request description as follows:**
1. What does this MR do?
2. Are there points in the code the reviewer needs to double check?
3. Why was this MR needed?
4. What are the relevant issue numbers / [Feature requests](http://feedback.gitlab.com/)?
5. Screenshots (If appropiate)
## Contribution acceptance criteria ## Contribution acceptance criteria
1. The change is as small as possible (see the above paragraph for details) 1. The change is as small as possible (see the above paragraph for details)
......
...@@ -132,6 +132,9 @@ gem "gitlab-flowdock-git-hook", "~> 0.4.2" ...@@ -132,6 +132,9 @@ gem "gitlab-flowdock-git-hook", "~> 0.4.2"
# Gemnasium integration # Gemnasium integration
gem "gemnasium-gitlab-service", "~> 0.2" gem "gemnasium-gitlab-service", "~> 0.2"
# Slack integration
gem "slack-notifier", "~> 0.2.0"
# d3 # d3
gem "d3_rails", "~> 3.1.4" gem "d3_rails", "~> 3.1.4"
...@@ -162,8 +165,9 @@ gem "modernizr", "2.6.2" ...@@ -162,8 +165,9 @@ gem "modernizr", "2.6.2"
gem "raphael-rails", "~> 2.1.2" gem "raphael-rails", "~> 2.1.2"
gem 'bootstrap-sass', '~> 3.0' gem 'bootstrap-sass', '~> 3.0'
gem "font-awesome-rails", '~> 3.2' gem "font-awesome-rails", '~> 3.2'
gem "gemoji", "~> 1.3.0" gem "gitlab_emoji", "~> 0.0.1.1"
gem "gon", '~> 5.0.0' gem "gon", '~> 5.0.0'
gem 'nprogress-rails'
group :development do group :development do
gem "annotate", "~> 2.6.0.beta2" gem "annotate", "~> 2.6.0.beta2"
...@@ -214,7 +218,6 @@ group :development, :test do ...@@ -214,7 +218,6 @@ group :development, :test do
# PhantomJS driver for Capybara # PhantomJS driver for Capybara
gem 'poltergeist', '~> 1.4.1' gem 'poltergeist', '~> 1.4.1'
gem 'spork', '~> 1.0rc'
gem 'jasmine', '2.0.0.rc5' gem 'jasmine', '2.0.0.rc5'
gem "spring", '1.1.1' gem "spring", '1.1.1'
......
...@@ -128,6 +128,8 @@ GEM ...@@ -128,6 +128,8 @@ GEM
mail (~> 2.2) mail (~> 2.2)
email_validator (1.4.0) email_validator (1.4.0)
activemodel activemodel
emoji (1.0.1)
json
enumerize (0.7.0) enumerize (0.7.0)
activesupport (>= 3.2) activesupport (>= 3.2)
equalizer (0.0.8) equalizer (0.0.8)
...@@ -165,7 +167,6 @@ GEM ...@@ -165,7 +167,6 @@ GEM
formatador (0.2.4) formatador (0.2.4)
gemnasium-gitlab-service (0.2.1) gemnasium-gitlab-service (0.2.1)
rugged (~> 0.19) rugged (~> 0.19)
gemoji (1.3.1)
gherkin-ruby (0.3.1) gherkin-ruby (0.3.1)
racc racc
github-markdown (0.5.5) github-markdown (0.5.5)
...@@ -190,6 +191,8 @@ GEM ...@@ -190,6 +191,8 @@ GEM
charlock_holmes (~> 0.6.6) charlock_holmes (~> 0.6.6)
escape_utils (~> 0.2.4) escape_utils (~> 0.2.4)
mime-types (~> 1.19) mime-types (~> 1.19)
gitlab_emoji (0.0.1.1)
emoji (~> 1.0.1)
gitlab_git (5.7.1) gitlab_git (5.7.1)
activesupport (~> 4.0.0) activesupport (~> 4.0.0)
charlock_holmes (~> 0.6.9) charlock_holmes (~> 0.6.9)
...@@ -297,6 +300,7 @@ GEM ...@@ -297,6 +300,7 @@ GEM
net-ssh (>= 1.99.1) net-ssh (>= 1.99.1)
net-ssh (2.7.0) net-ssh (2.7.0)
nokogiri (1.5.10) nokogiri (1.5.10)
nprogress-rails (0.1.2.3)
oauth (0.4.7) oauth (0.4.7)
oauth2 (0.8.1) oauth2 (0.8.1)
faraday (~> 0.8) faraday (~> 0.8)
...@@ -468,6 +472,7 @@ GEM ...@@ -468,6 +472,7 @@ GEM
rack-protection (~> 1.4) rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4) tilt (~> 1.3, >= 1.3.4)
six (0.2.0) six (0.2.0)
slack-notifier (0.2.0)
slim (2.0.2) slim (2.0.2)
temple (~> 0.6.6) temple (~> 0.6.6)
tilt (>= 1.3.3, < 2.1) tilt (>= 1.3.3, < 2.1)
...@@ -479,7 +484,6 @@ GEM ...@@ -479,7 +484,6 @@ GEM
capybara (>= 2.0.0) capybara (>= 2.0.0)
railties (>= 3) railties (>= 3)
spinach (>= 0.4) spinach (>= 0.4)
spork (1.0.0rc4)
spring (1.1.1) spring (1.1.1)
spring-commands-rspec (1.0.1) spring-commands-rspec (1.0.1)
spring (>= 0.9.1) spring (>= 0.9.1)
...@@ -591,12 +595,12 @@ DEPENDENCIES ...@@ -591,12 +595,12 @@ DEPENDENCIES
font-awesome-rails (~> 3.2) font-awesome-rails (~> 3.2)
foreman foreman
gemnasium-gitlab-service (~> 0.2) gemnasium-gitlab-service (~> 0.2)
gemoji (~> 1.3.0)
github-markup (~> 0.7.4)! github-markup (~> 0.7.4)!
gitlab-flowdock-git-hook (~> 0.4.2) gitlab-flowdock-git-hook (~> 0.4.2)
gitlab-gollum-lib (~> 1.1.0) gitlab-gollum-lib (~> 1.1.0)
gitlab-grack (~> 2.0.0.pre) gitlab-grack (~> 2.0.0.pre)
gitlab-linguist (~> 3.0.0) gitlab-linguist (~> 3.0.0)
gitlab_emoji (~> 0.0.1.1)
gitlab_git (~> 5.7.1) gitlab_git (~> 5.7.1)
gitlab_meta (= 6.0) gitlab_meta (= 6.0)
gitlab_omniauth-ldap (= 1.0.4) gitlab_omniauth-ldap (= 1.0.4)
...@@ -620,6 +624,7 @@ DEPENDENCIES ...@@ -620,6 +624,7 @@ DEPENDENCIES
minitest (~> 4.7.0) minitest (~> 4.7.0)
modernizr (= 2.6.2) modernizr (= 2.6.2)
mysql2 mysql2
nprogress-rails
omniauth (~> 1.1.3) omniauth (~> 1.1.3)
omniauth-github omniauth-github
omniauth-google-oauth2 omniauth-google-oauth2
...@@ -652,9 +657,9 @@ DEPENDENCIES ...@@ -652,9 +657,9 @@ DEPENDENCIES
simplecov simplecov
sinatra sinatra
six six
slack-notifier (~> 0.2.0)
slim slim
spinach-rails spinach-rails
spork (~> 1.0rc)
spring (= 1.1.1) spring (= 1.1.1)
spring-commands-rspec (= 1.0.1) spring-commands-rspec (= 1.0.1)
spring-commands-spinach (= 1.0.0) spring-commands-spinach (= 1.0.0)
......
...@@ -24,8 +24,6 @@ Below we describe the contributing process to GitLab for two reasons. So that co ...@@ -24,8 +24,6 @@ Below we describe the contributing process to GitLab for two reasons. So that co
- Monitors for new merge requests (at least once a week) - Monitors for new merge requests (at least once a week)
- Manages their work queue by looking at issues and merge requests assigned to them - Manages their work queue by looking at issues and merge requests assigned to them
- Close fixed issues (via commit messages or manually) - Close fixed issues (via commit messages or manually)
- Codes [new features](http://feedback.gitlab.com/forums/176466-general/filters/top)!
- Response guidelines
- 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). - 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).
## Priorities of the issue team ## Priorities of the issue team
...@@ -73,7 +71,7 @@ Thanks for the issue report. Please reformat your issue to conform to the issue ...@@ -73,7 +71,7 @@ Thanks for the issue report. Please reformat your issue to conform to the issue
### Feature requests ### Feature requests
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. 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.
### Issue report for old version ### Issue report for old version
......
web: bundle exec unicorn_rails -p $PORT -E development web: bundle exec unicorn_rails -p $PORT -E development -c config/unicorn_development.rb
worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell
...@@ -71,7 +71,7 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl ...@@ -71,7 +71,7 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl
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. 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.
* 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). * 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).
### Run in production mode ### Run in production mode
...@@ -96,14 +96,9 @@ or start each component separately ...@@ -96,14 +96,9 @@ or start each component separately
### Run the tests ### Run the tests
* Seed the database
bundle exec rake db:setup RAILS_ENV=test
bundle exec rake db:seed_fu RAILS_ENV=test
* Run all tests * Run all tests
bundle exec rake gitlab:test RAILS_ENV=test bundle exec rake test
* [RSpec](http://rspec.info/) unit and functional tests * [RSpec](http://rspec.info/) unit and functional tests
...@@ -134,22 +129,4 @@ or start each component separately ...@@ -134,22 +129,4 @@ or start each component separately
### Getting help ### Getting help
* [Maintenance policy](MAINTENANCE.md) specifies what versions are supported. Please see [Getting help for GitLab](https://www.gitlab.com/getting-help/) on our website for the many options to get help.
* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) contains solutions to common problems.
* [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.
* [Feedback and suggestions forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab.
* [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.
* [Support subscription](http://www.gitlab.com/subscription/) connects you to the knowledge of GitLab experts that will resolve your issues and answer your questions.
* [Consultancy](http://www.gitlab.com/consultancy/) from the GitLab experts for installations, upgrades and customizations.
* [#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).
* [Book](http://www.packtpub.com/gitlab-repository-management/book) written by GitLab enthusiast Jonathan M. Hethey is unofficial but it offers a good overview.
* [Gitter chat room](https://gitter.im/gitlabhq/gitlabhq#) here you can ask questions when you need help.
6.7.0.pre 6.8.0.pre
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require jquery
//= require jquery.ui.all
//= require jquery_ujs
//= require jquery.cookie
//= require jquery.endless-scroll
//= require jquery.highlight
//= require jquery.history
//= require jquery.waitforimages
//= require jquery.atwho
//= require jquery.scrollto
//= require jquery.blockUI
//= require turbolinks
//= require jquery.turbolinks
//= require bootstrap
//= require modernizr
//= require select2
//= require raphael
//= require g.raphael-min
//= require g.bar-min
//= require branch-graph
//= require highlightjs.min
//= require ace/ace
//= require_tree .
//= require d3
//= require underscore
# This is a manifest file that'll be compiled into including all the files listed below.
# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
# be included in the compiled file accessible from http://example.com/assets/application.js
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
#
#= require jquery
#= require jquery.ui.all
#= require jquery_ujs
#= require jquery.cookie
#= require jquery.endless-scroll
#= require jquery.highlight
#= require jquery.history
#= require jquery.waitforimages
#= require jquery.atwho
#= require jquery.scrollto
#= require jquery.blockUI
#= require turbolinks
#= require jquery.turbolinks
#= require bootstrap
#= require modernizr
#= require select2
#= require raphael
#= require g.raphael-min
#= require g.bar-min
#= require branch-graph
#= require highlightjs.min
#= require ace/ace
#= require d3
#= require underscore
#= require nprogress
#= require nprogress-turbolinks
#= require_tree .
window.slugify = (text) -> window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
...@@ -41,19 +75,11 @@ window.linkify = (str) -> ...@@ -41,19 +75,11 @@ window.linkify = (str) ->
window.simpleFormat = (str) -> window.simpleFormat = (str) ->
linkify(sanitize(str).replace(/\n/g, '<br />')) linkify(sanitize(str).replace(/\n/g, '<br />'))
window.startSpinner = ->
$('.turbolink-spinner').fadeIn()
window.stopSpinner = ->
$('.turbolink-spinner').fadeOut()
window.unbindEvents = -> window.unbindEvents = ->
$(document).unbind('scroll') $(document).unbind('scroll')
$(document).off('scroll') $(document).off('scroll')
document.addEventListener("page:fetch", startSpinner)
document.addEventListener("page:fetch", unbindEvents) document.addEventListener("page:fetch", unbindEvents)
document.addEventListener("page:change", stopSpinner)
$ -> $ ->
# Click a .one_click_select field, select the contents # Click a .one_click_select field, select the contents
......
...@@ -6,7 +6,6 @@ GitLab.GfmAutoComplete = ...@@ -6,7 +6,6 @@ GitLab.GfmAutoComplete =
dataSource: '' dataSource: ''
# Emoji # Emoji
Emoji: Emoji:
assetBase: ''
template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>' template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>'
# Team Members # Team Members
...@@ -27,7 +26,7 @@ GitLab.GfmAutoComplete = ...@@ -27,7 +26,7 @@ GitLab.GfmAutoComplete =
tpl: @Emoji.template tpl: @Emoji.template
callbacks: callbacks:
before_save: (emojis) => before_save: (emojis) =>
$.map emojis, (em) => name: em, insert: em+ ':', image: "#{@Emoji.assetBase}/#{em}.png" $.map emojis, (em) => name: em.name, insert: em.name+ ':', image: em.path
# Team Members # Team Members
input.atwho input.atwho
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
*= require select2 *= require select2
*= require highlightjs.min *= require highlightjs.min
*= require_self *= require_self
*= require nprogress
*= require nprogress-bootstrap
*/ */
@import "main/variables.scss"; @import "main/variables.scss";
......
...@@ -355,3 +355,7 @@ table { ...@@ -355,3 +355,7 @@ table {
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
.container .content { margin-top: 20px; } .container .content { margin-top: 20px; }
} }
.wiki .highlight, .note-body .highlight {
margin-bottom: 9px;
}
...@@ -88,9 +88,6 @@ a:focus { ...@@ -88,9 +88,6 @@ a:focus {
.wiki { .wiki {
@include md-typography; @include md-typography;
font-size: 14px;
line-height: 1.6;
/* Link to current header. */ /* Link to current header. */
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
position: relative; position: relative;
...@@ -120,3 +117,11 @@ a:focus { ...@@ -120,3 +117,11 @@ a:focus {
.md { .md {
@include md-typography; @include md-typography;
} }
/**
* Textareas intended for GFM
*
*/
textarea.js-gfm-input {
font-family: $monospace_font;
}
...@@ -84,6 +84,9 @@ ...@@ -84,6 +84,9 @@
} }
@mixin md-typography { @mixin md-typography {
font-size: 14px;
line-height: 1.6;
img { img {
max-width: 100%; max-width: 100%;
} }
......
/* Generic print styles */
header, nav, nav.main-nav, nav.navbar-collapse, nav.navbar-collapse.collapse {display: none!important;}
.profiler-results {display: none;}
/* Styles targeted specifically at printing files */
.tree-ref-holder, .tree-holder .breadcrumb, .blob-commit-info {display: none;}
.file-title {display: none;}
.file-holder {border: none;}
.wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; }
.wiki h1 {font-size: 30px;}
.wiki h2 {font-size: 22px;}
.wiki h3 {font-size: 18px; font-weight: bold; }
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
} }
.project-row, .group-row { .project-row, .group-row {
padding: 10px 12px !important; padding: 8px 12px !important;
font-size: 14px; font-size: 14px;
line-height: 24px; line-height: 24px;
......
...@@ -62,6 +62,29 @@ ...@@ -62,6 +62,29 @@
font-size: 12px; font-size: 12px;
} }
} }
.text-file-parallel div {
display: inline-block;
padding-bottom: 16px;
}
.diff-side {
overflow-x: scroll;
width: 508px;
height: 700px;
}
.diff-side.diff-side-left{
overflow-y:hidden;
}
.diff-side table, td.diff-middle table {
height: 700px;
}
.diff-middle {
width: 114px;
vertical-align: top;
height: 700px;
overflow: hidden
}
.old_line, .new_line, .diff_line { .old_line, .new_line, .diff_line {
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
...@@ -125,8 +148,6 @@ ...@@ -125,8 +148,6 @@
} }
&.parallel { &.parallel {
display: table-cell; display: table-cell;
overflow: hidden;
width: 50%;
} }
} }
} }
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
} }
} }
padding: 14px 0px; padding: 12px 0px;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
.event-title { .event-title {
color: #333; color: #333;
...@@ -63,6 +63,10 @@ ...@@ -63,6 +63,10 @@
color: #666; color: #666;
margin-top: 5px; margin-top: 5px;
.md {
font-size: 13px;
}
pre { pre {
border: none; border: none;
background: #f9f9f9; background: #f9f9f9;
......
...@@ -273,3 +273,9 @@ header { ...@@ -273,3 +273,9 @@ header {
} }
} }
} }
@media (max-width: $screen-xs-max) {
#nprogress .spinner {
right: 35px !important;
}
}
...@@ -27,11 +27,15 @@ ul.notes { ...@@ -27,11 +27,15 @@ ul.notes {
.discussion-last-update, .discussion-last-update,
.note-last-update { .note-last-update {
font-style: italic; &:before {
content: "\00b7";
}
font-size: 13px;
} }
.author { .author {
color: $style_color; color: #555;
font-weight: bold; font-weight: bold;
font-size: 14px;
&:hover { &:hover {
color: $primary_color; color: $primary_color;
} }
......
class PasswordsController < Devise::PasswordsController
def create
email = resource_params[:email]
resource_found = resource_class.find_by_email(email)
if resource_found && resource_found.ldap_user?
flash[:alert] = "Cannot reset password for LDAP user."
respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name)) and return
end
self.resource = resource_class.send_reset_password_instructions(resource_params)
if successfully_sent?(resource)
respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name))
else
respond_with(resource)
end
end
end
...@@ -41,7 +41,7 @@ class Profiles::KeysController < ApplicationController ...@@ -41,7 +41,7 @@ class Profiles::KeysController < ApplicationController
begin begin
user = User.find_by_username(params[:username]) user = User.find_by_username(params[:username])
if user.present? if user.present?
render text: user.all_ssh_keys.join("\n") render text: user.all_ssh_keys.join("\n"), content_type: "text/plain"
else else
render_404 and return render_404 and return
end end
......
...@@ -76,7 +76,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -76,7 +76,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
def update def update
@issue.update_attributes(params[:issue].merge(author_id_of_changes: current_user.id)) @issue.update_attributes(params[:issue])
@issue.reset_events_cache @issue.reset_events_cache
respond_to do |format| respond_to do |format|
......
...@@ -109,7 +109,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -109,7 +109,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
params[:merge_request].delete(:source_project_id) params[:merge_request].delete(:source_project_id)
params[:merge_request].delete(:target_project_id) params[:merge_request].delete(:target_project_id)
if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id)) if @merge_request.update_attributes(params[:merge_request])
@merge_request.reset_events_cache @merge_request.reset_events_cache
respond_to do |format| respond_to do |format|
......
...@@ -38,7 +38,6 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -38,7 +38,6 @@ class Projects::MilestonesController < Projects::ApplicationController
def create def create
@milestone = @project.milestones.new(params[:milestone]) @milestone = @project.milestones.new(params[:milestone])
@milestone.author_id_of_changes = current_user.id
if @milestone.save if @milestone.save
redirect_to project_milestone_path(@project, @milestone) redirect_to project_milestone_path(@project, @milestone)
...@@ -48,7 +47,7 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -48,7 +47,7 @@ class Projects::MilestonesController < Projects::ApplicationController
end end
def update def update
@milestone.update_attributes(params[:milestone].merge(author_id_of_changes: current_user.id)) @milestone.update_attributes(params[:milestone])
respond_to do |format| respond_to do |format|
format.js format.js
......
...@@ -123,11 +123,20 @@ class ProjectsController < ApplicationController ...@@ -123,11 +123,20 @@ class ProjectsController < ApplicationController
end end
def autocomplete_sources def autocomplete_sources
note_type = params['type']
note_id = params['type_id']
participating = if note_type && note_id
participants_in(note_type, note_id)
else
[]
end
team_members = sorted(@project.team.members)
participants = team_members + participating
@suggestions = { @suggestions = {
emojis: Emoji.names, emojis: Emoji.names.map { |e| { name: e, path: view_context.image_url("emoji/#{e}.png") } },
issues: @project.issues.select([:iid, :title, :description]), issues: @project.issues.select([:iid, :title, :description]),
mergerequests: @project.merge_requests.select([:iid, :title, :description]), mergerequests: @project.merge_requests.select([:iid, :title, :description]),
members: @project.team.members.sort_by(&:username).map { |user| { username: user.username, name: user.name } } members: participants.uniq
} }
respond_to do |format| respond_to do |format|
...@@ -162,4 +171,25 @@ class ProjectsController < ApplicationController ...@@ -162,4 +171,25 @@ class ProjectsController < ApplicationController
def user_layout def user_layout
current_user ? "projects" : "public_projects" current_user ? "projects" : "public_projects"
end end
def participants_in(type, id)
users = case type
when "Issue"
issue = @project.issues.find_by_iid(id)
issue ? issue.participants : []
when "MergeRequest"
merge_request = @project.merge_requests.find_by_iid(id)
merge_request ? merge_request.participants : []
when "Commit"
author_ids = Note.for_commit_id(id).pluck(:author_id).uniq
User.where(id: author_ids)
else
[]
end
sorted(users)
end
def sorted(users)
users.uniq.sort_by(&:username).map { |user| { username: user.username, name: user.name } }
end
end end
...@@ -47,9 +47,9 @@ class BaseFinder ...@@ -47,9 +47,9 @@ class BaseFinder
[] []
end end
elsif current_user && params[:authorized_only].presence elsif current_user && params[:authorized_only].presence
klass.of_projects(current_user.authorized_projects) klass.of_projects(current_user.authorized_projects).references(:project)
else else
klass.of_projects(Project.accessible_to(current_user)) klass.of_projects(Project.accessible_to(current_user)).references(:project)
end end
end end
......
...@@ -105,8 +105,80 @@ module CommitsHelper ...@@ -105,8 +105,80 @@ module CommitsHelper
branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
end end
def get_old_file(project, commit, diff) def parallel_diff_lines(project, commit, diff, file)
project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id old_file = project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id
deleted_lines = {}
added_lines = {}
each_diff_line(diff, 0) do |line, type, line_code, line_new, line_old|
if type == "old"
deleted_lines[line_old] = { line_code: line_code, type: type, line: line }
elsif type == "new"
added_lines[line_new] = { line_code: line_code, type: type, line: line }
end
end
max_length = old_file ? old_file.sloc + added_lines.length : file.sloc
offset1 = 0
offset2 = 0
old_lines = []
new_lines = []
max_length.times do |line_index|
line_index1 = line_index - offset1
line_index2 = line_index - offset2
deleted_line = deleted_lines[line_index1 + 1]
added_line = added_lines[line_index2 + 1]
old_line = old_file.lines[line_index1] if old_file
new_line = file.lines[line_index2]
if deleted_line && added_line
elsif deleted_line
new_line = nil
offset2 += 1
elsif added_line
old_line = nil
offset1 += 1
end
old_lines[line_index] = DiffLine.new
new_lines[line_index] = DiffLine.new
# old
if line_index == 0 && diff.new_file
old_lines[line_index].type = :file_created
old_lines[line_index].content = 'File was created'
elsif deleted_line
old_lines[line_index].type = :deleted
old_lines[line_index].content = old_line
old_lines[line_index].num = line_index1 + 1
old_lines[line_index].code = deleted_line[:line_code]
elsif old_line
old_lines[line_index].type = :no_change
old_lines[line_index].content = old_line
old_lines[line_index].num = line_index1 + 1
else
old_lines[line_index].type = :added
end
# new
if line_index == 0 && diff.deleted_file
new_lines[line_index].type = :file_deleted
new_lines[line_index].content = "File was deleted"
elsif added_line
new_lines[line_index].type = :added
new_lines[line_index].num = line_index2 + 1
new_lines[line_index].content = new_line
new_lines[line_index].code = added_line[:line_code]
elsif new_line
new_lines[line_index].type = :no_change
new_lines[line_index].num = line_index2 + 1
new_lines[line_index].content = new_line
else
new_lines[line_index].type = :deleted
end
end
return old_lines, new_lines
end end
protected protected
......
...@@ -20,7 +20,7 @@ module MergeRequestsHelper ...@@ -20,7 +20,7 @@ module MergeRequestsHelper
target_project_id: target_project.id, target_project_id: target_project.id,
source_branch: event.branch_name, source_branch: event.branch_name,
target_branch: target_project.repository.root_ref, target_branch: target_project.repository.root_ref,
title: event.branch_name.titleize title: event.branch_name.humanize
} }
end end
......
...@@ -40,7 +40,7 @@ module TreeHelper ...@@ -40,7 +40,7 @@ module TreeHelper
# Returns boolean # Returns boolean
def markup?(filename) def markup?(filename)
filename.downcase.end_with?(*%w(.textile .rdoc .org .creole filename.downcase.end_with?(*%w(.textile .rdoc .org .creole
.mediawiki .rst .asciidoc .pod)) .mediawiki .rst .adoc .asciidoc .pod))
end end
def gitlab_markdown?(filename) def gitlab_markdown?(filename)
......
...@@ -29,11 +29,11 @@ module Emails ...@@ -29,11 +29,11 @@ module Emails
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
end end
def merged_merge_request_email(recipient_id, merge_request_id) def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id) @merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project @project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request) @target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(@merge_request.author_id_of_changes), mail(from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
end end
......
...@@ -37,8 +37,6 @@ module Issuable ...@@ -37,8 +37,6 @@ module Issuable
allow_nil: true, allow_nil: true,
prefix: true prefix: true
attr_accessor :author_id_of_changes
attr_mentionable :title, :description attr_mentionable :title, :description
end end
......
class DiffLine
attr_accessor :type, :content, :num, :code
end
...@@ -47,14 +47,6 @@ class Event < ActiveRecord::Base ...@@ -47,14 +47,6 @@ class Event < ActiveRecord::Base
scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent } scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
class << self class << self
def determine_action(record)
if [Issue, MergeRequest].include? record.class
Event::CREATED
elsif record.kind_of? Note
Event::COMMENTED
end
end
def create_ref_event(project, user, ref, action = 'add', prefix = 'refs/heads') def create_ref_event(project, user, ref, action = 'add', prefix = 'refs/heads')
commit = project.repository.commit(ref.target) commit = project.repository.commit(ref.target)
......
...@@ -30,8 +30,7 @@ class Issue < ActiveRecord::Base ...@@ -30,8 +30,7 @@ class Issue < ActiveRecord::Base
scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
attr_accessible :title, :assignee_id, :position, :description, attr_accessible :title, :assignee_id, :position, :description,
:milestone_id, :label_list, :author_id_of_changes, :milestone_id, :label_list, :state_event
:state_event
acts_as_taggable_on :labels acts_as_taggable_on :labels
......
...@@ -38,7 +38,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -38,7 +38,7 @@ class MergeRequest < ActiveRecord::Base
delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil
attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :author_id_of_changes, :state_event, :description attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :state_event, :description
attr_accessor :should_remove_source_branch attr_accessor :should_remove_source_branch
......
...@@ -16,8 +16,7 @@ ...@@ -16,8 +16,7 @@
class Milestone < ActiveRecord::Base class Milestone < ActiveRecord::Base
include InternalId include InternalId
attr_accessible :title, :description, :due_date, :state_event, :author_id_of_changes attr_accessible :title, :description, :due_date, :state_event
attr_accessor :author_id_of_changes
belongs_to :project belongs_to :project
has_many :issues has_many :issues
...@@ -89,6 +88,6 @@ class Milestone < ActiveRecord::Base ...@@ -89,6 +88,6 @@ class Milestone < ActiveRecord::Base
end end
def author_id def author_id
author_id_of_changes nil
end end
end end
...@@ -199,7 +199,8 @@ class Note < ActiveRecord::Base ...@@ -199,7 +199,8 @@ class Note < ActiveRecord::Base
def downvote? def downvote?
votable? && (note.start_with?('-1') || votable? && (note.start_with?('-1') ||
note.start_with?(':-1:') || note.start_with?(':-1:') ||
note.start_with?(':thumbsdown:') note.start_with?(':thumbsdown:') ||
note.start_with?(':thumbs_down_sign:')
) )
end end
...@@ -249,7 +250,8 @@ class Note < ActiveRecord::Base ...@@ -249,7 +250,8 @@ class Note < ActiveRecord::Base
def upvote? def upvote?
votable? && (note.start_with?('+1') || votable? && (note.start_with?('+1') ||
note.start_with?(':+1:') || note.start_with?(':+1:') ||
note.start_with?(':thumbsup:') note.start_with?(':thumbsup:') ||
note.start_with?(':thumbs_up_sign:')
) )
end end
......
...@@ -56,6 +56,7 @@ class Project < ActiveRecord::Base ...@@ -56,6 +56,7 @@ class Project < ActiveRecord::Base
has_one :flowdock_service, dependent: :destroy has_one :flowdock_service, dependent: :destroy
has_one :assembla_service, dependent: :destroy has_one :assembla_service, dependent: :destroy
has_one :gemnasium_service, dependent: :destroy has_one :gemnasium_service, dependent: :destroy
has_one :slack_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link has_one :forked_from_project, through: :forked_project_link
# Merge Requests for target project should be removed with it # Merge Requests for target project should be removed with it
...@@ -304,7 +305,7 @@ class Project < ActiveRecord::Base ...@@ -304,7 +305,7 @@ class Project < ActiveRecord::Base
end end
def available_services_names def available_services_names
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium) %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack)
end end
def gitlab_ci? def gitlab_ci?
......
require 'slack-notifier'
class SlackMessage
def initialize(params)
@after = params.fetch(:after)
@before = params.fetch(:before)
@commits = params.fetch(:commits, [])
@project_name = params.fetch(:project_name)
@project_url = params.fetch(:project_url)
@ref = params.fetch(:ref).gsub('refs/heads/', '')
@username = params.fetch(:user_name)
end
def compose
format(message)
end
private
attr_reader :after
attr_reader :before
attr_reader :commits
attr_reader :project_name
attr_reader :project_url
attr_reader :ref
attr_reader :username
def message
if new_branch?
new_branch_message
elsif removed_branch?
removed_branch_message
else
push_message << commit_messages
end
end
def format(string)
Slack::Notifier::LinkFormatter.format(string)
end
def new_branch_message
"#{username} pushed new branch #{branch_link} to #{project_link}"
end
def removed_branch_message
"#{username} removed branch #{ref} from #{project_link}"
end
def push_message
"#{username} pushed to branch #{branch_link} of #{project_link} (#{compare_link})"
end
def commit_messages
commits.each_with_object('') do |commit, str|
str << compose_commit_message(commit)
end
end
def compose_commit_message(commit)
id = commit.fetch(:id)[0..5]
message = commit.fetch(:message)
url = commit.fetch(:url)
"\n - #{message} ([#{id}](#{url}))"
end
def new_branch?
before =~ /000000/
end
def removed_branch?
after =~ /000000/
end
def branch_url
"#{project_url}/commits/#{ref}"
end
def compare_url
"#{project_url}/compare/#{before}...#{after}"
end
def branch_link
"[#{ref}](#{branch_url})"
end
def project_link
"[#{project_name}](#{project_url})"
end
def compare_link
"[Compare changes](#{compare_url})"
end
end
# == Schema Information
#
# Table name: services
#
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# token :string(255)
# project_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# active :boolean default(FALSE), not null
# project_url :string(255)
# subdomain :string(255)
# room :string(255)
# api_key :string(255)
#
class SlackService < Service
attr_accessible :room
attr_accessible :subdomain
validates :room, presence: true, if: :activated?
validates :subdomain, presence: true, if: :activated?
validates :token, presence: true, if: :activated?
def title
'Slack'
end
def description
'A team communication tool for the 21st century'
end
def to_param
'slack'
end
def fields
[
{ type: 'text', name: 'subdomain', placeholder: '' },
{ type: 'text', name: 'token', placeholder: '' },
{ type: 'text', name: 'room', placeholder: 'Ex. #general' },
]
end
def execute(push_data)
message = SlackMessage.new(push_data.merge(
project_url: project_url,
project_name: project_name
))
notifier = Slack::Notifier.new(subdomain, token)
notifier.channel = room
notifier.username = 'GitLab'
notifier.ping(message.compose)
end
private
def project_name
project.name_with_namespace.gsub(/\s/, '')
end
def project_url
project.web_url
end
end
...@@ -204,7 +204,7 @@ class User < ActiveRecord::Base ...@@ -204,7 +204,7 @@ class User < ActiveRecord::Base
end end
def search query def search query
where("name LIKE :query OR email LIKE :query OR username LIKE :query", query: "%#{query}%") where("lower(name) LIKE :query OR lower(email) LIKE :query OR lower(username) LIKE :query", query: "%#{query.downcase}%")
end end
def by_username_or_id(name_or_id) def by_username_or_id(name_or_id)
...@@ -249,7 +249,7 @@ class User < ActiveRecord::Base ...@@ -249,7 +249,7 @@ class User < ActiveRecord::Base
def namespace_uniq def namespace_uniq
namespace_name = self.username namespace_name = self.username
if Namespace.find_by(path: namespace_name) if Namespace.find_by(path: namespace_name)
self.errors.add :username, "already exist" self.errors.add :username, "already exists"
end end
end end
......
class ActivityObserver < BaseObserver
observe :issue, :note, :milestone
def after_create(record)
event_author_id = record.author_id
if record.kind_of?(Note)
# Skip system notes, like status changes and cross-references.
return true if record.system?
# Skip wall notes to prevent spamming of dashboard
return true if record.noteable_type.blank?
end
if event_author_id
create_event(record, Event.determine_action(record))
end
end
def after_close(record, transition)
create_event(record, Event::CLOSED)
end
def after_reopen(record, transition)
create_event(record, Event::REOPENED)
end
protected
def create_event(record, status)
Event.create(
project: record.project,
target_id: record.id,
target_type: record.class.name,
action: status,
author_id: current_user.id
)
end
end
...@@ -3,6 +3,10 @@ class BaseObserver < ActiveRecord::Observer ...@@ -3,6 +3,10 @@ class BaseObserver < ActiveRecord::Observer
NotificationService.new NotificationService.new
end end
def event_service
EventCreateService.new
end
def log_info message def log_info message
Gitlab::AppLogger.info message Gitlab::AppLogger.info message
end end
......
class IssueObserver < BaseObserver class IssueObserver < BaseObserver
def after_create(issue) def after_create(issue)
notification.new_issue(issue, current_user) notification.new_issue(issue, current_user)
event_service.open_issue(issue, current_user)
issue.create_cross_references!(issue.project, current_user) issue.create_cross_references!(issue.project, current_user)
execute_hooks(issue) execute_hooks(issue)
end end
def after_close(issue, transition) def after_close(issue, transition)
notification.close_issue(issue, current_user) notification.close_issue(issue, current_user)
event_service.close_issue(issue, current_user)
create_note(issue) create_note(issue)
execute_hooks(issue) execute_hooks(issue)
end end
def after_reopen(issue, transition) def after_reopen(issue, transition)
event_service.reopen_issue(issue, current_user)
create_note(issue) create_note(issue)
execute_hooks(issue) execute_hooks(issue)
end end
......
class MergeRequestObserver < ActivityObserver class MergeRequestObserver < BaseObserver
observe :merge_request
def after_create(merge_request) def after_create(merge_request)
if merge_request.author_id event_service.open_mr(merge_request, current_user)
create_event(merge_request, Event.determine_action(merge_request))
end
notification.new_merge_request(merge_request, current_user) notification.new_merge_request(merge_request, current_user)
merge_request.create_cross_references!(merge_request.project, current_user) merge_request.create_cross_references!(merge_request.project, current_user)
execute_hooks(merge_request) execute_hooks(merge_request)
end end
def after_close(merge_request, transition) def after_close(merge_request, transition)
create_event(merge_request, Event::CLOSED) event_service.close_mr(merge_request, current_user)
notification.close_mr(merge_request, current_user) notification.close_mr(merge_request, current_user)
create_note(merge_request) create_note(merge_request)
execute_hooks(merge_request) execute_hooks(merge_request)
end end
def after_reopen(merge_request, transition) def after_reopen(merge_request, transition)
create_event(merge_request, Event::REOPENED) event_service.reopen_mr(merge_request, current_user)
create_note(merge_request) create_note(merge_request)
execute_hooks(merge_request) execute_hooks(merge_request)
merge_request.reload_code merge_request.reload_code
...@@ -33,16 +28,6 @@ class MergeRequestObserver < ActivityObserver ...@@ -33,16 +28,6 @@ class MergeRequestObserver < ActivityObserver
execute_hooks(merge_request) execute_hooks(merge_request)
end end
def create_event(record, status)
Event.create(
project: record.target_project,
target_id: record.id,
target_type: record.class.name,
action: status,
author_id: current_user.id
)
end
private private
# Create merge request note with service comment like 'Status changed to closed' # Create merge request note with service comment like 'Status changed to closed'
......
class MilestoneObserver < BaseObserver
def after_create(milestone)
event_service.open_milestone(milestone, current_user)
end
def after_close(milestone, transition)
event_service.close_milestone(milestone, current_user)
end
def after_reopen(milestone, transition)
event_service.reopen_milestone(milestone, current_user)
end
end
...@@ -2,6 +2,12 @@ class NoteObserver < BaseObserver ...@@ -2,6 +2,12 @@ class NoteObserver < BaseObserver
def after_create(note) def after_create(note)
notification.new_note(note) notification.new_note(note)
# Skip system notes, like status changes and cross-references.
# Skip wall notes to prevent spamming of dashboard
if note.noteable_type.present? && !note.system
event_service.leave_note(note, current_user)
end
unless note.system? unless note.system?
# Create a cross-reference note if this Note contains GFM that names an # Create a cross-reference note if this Note contains GFM that names an
# issue, merge request, or commit. # issue, merge request, or commit.
......
# EventCreateService class
#
# Used for creating events feed on dashboard after certain user action
#
# Ex.
# EventCreateService.new.new_issue(issue, current_user)
#
class EventCreateService
def open_issue(issue, current_user)
create_event(issue, current_user, Event::CREATED)
end
def close_issue(issue, current_user)
create_event(issue, current_user, Event::CLOSED)
end
def reopen_issue(issue, current_user)
create_event(issue, current_user, Event::REOPENED)
end
def open_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::CREATED)
end
def close_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::CLOSED)
end
def reopen_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::REOPENED)
end
def merge_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::MERGED)
end
def open_milestone(milestone, current_user)
create_event(milestone, current_user, Event::CREATED)
end
def close_milestone(milestone, current_user)
create_event(milestone, current_user, Event::CLOSED)
end
def reopen_milestone(milestone, current_user)
create_event(milestone, current_user, Event::REOPENED)
end
def leave_note(note, current_user)
create_event(note, current_user, Event::COMMENTED)
end
private
def create_event(record, current_user, status)
Event.create(
project: record.project,
target_id: record.id,
target_type: record.class.name,
action: status,
author_id: current_user.id
)
end
end
...@@ -9,11 +9,10 @@ module MergeRequests ...@@ -9,11 +9,10 @@ module MergeRequests
merge_request.lock merge_request.lock
if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message) if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message)
merge_request.author_id_of_changes = current_user.id
merge_request.merge merge_request.merge
notification.merge_mr(merge_request) notification.merge_mr(merge_request, current_user)
create_merge_event(merge_request) create_merge_event(merge_request, current_user)
execute_project_hooks(merge_request) execute_project_hooks(merge_request)
true true
......
...@@ -7,14 +7,8 @@ module MergeRequests ...@@ -7,14 +7,8 @@ module MergeRequests
NotificationService.new NotificationService.new
end end
def create_merge_event(merge_request) def create_merge_event(merge_request, current_user)
Event.create( EventCreateService.new.merge_mr(merge_request, current_user)
project: merge_request.target_project,
target_id: merge_request.id,
target_type: merge_request.class.name,
action: Event::MERGED,
author_id: merge_request.author_id_of_changes
)
end end
def execute_project_hooks(merge_request) def execute_project_hooks(merge_request)
......
...@@ -7,11 +7,10 @@ module MergeRequests ...@@ -7,11 +7,10 @@ module MergeRequests
# to target branch # to target branch
class MergeService < BaseMergeService class MergeService < BaseMergeService
def execute(merge_request, current_user, commit_message) def execute(merge_request, current_user, commit_message)
merge_request.author_id_of_changes = current_user.id
merge_request.merge merge_request.merge
notification.merge_mr(merge_request) notification.merge_mr(merge_request, current_user)
create_merge_event(merge_request) create_merge_event(merge_request, current_user)
execute_project_hooks(merge_request) execute_project_hooks(merge_request)
true true
......
...@@ -86,12 +86,12 @@ class NotificationService ...@@ -86,12 +86,12 @@ class NotificationService
# * merge_request assignee if their notification level is not Disabled # * merge_request assignee if their notification level is not Disabled
# * project team members with notification level higher then Participating # * project team members with notification level higher then Participating
# #
def merge_mr(merge_request) def merge_mr(merge_request, current_user)
recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project) recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project)
recipients = recipients.concat(project_watchers(merge_request.target_project)).uniq recipients = recipients.concat(project_watchers(merge_request.target_project)).uniq
recipients.each do |recipient| recipients.each do |recipient|
mailer.merged_merge_request_email(recipient.id, merge_request.id) mailer.merged_merge_request_email(recipient.id, merge_request.id, current_user.id)
end end
end end
...@@ -111,6 +111,7 @@ class NotificationService ...@@ -111,6 +111,7 @@ class NotificationService
# ignore gitlab service messages # ignore gitlab service messages
return true if note.note =~ /\A_Status changed to closed_/ return true if note.note =~ /\A_Status changed to closed_/
return true if note.note =~ /\A_mentioned in / && note.system == true
opts = { noteable_type: note.noteable_type, project_id: note.project_id } opts = { noteable_type: note.noteable_type, project_id: note.project_id }
......
...@@ -2,10 +2,12 @@ ...@@ -2,10 +2,12 @@
- if providers.present? - if providers.present?
%hr %hr
%div{:'data-no-turbolink' => 'data-no-turbolink'} %div{:'data-no-turbolink' => 'data-no-turbolink'}
%span Sign in with: &nbsp; %span Sign in with*: &nbsp;
- providers.each do |provider| - providers.each do |provider|
%span %span
- if default_providers.include?(provider) - if default_providers.include?(provider)
= link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
- else - else
= link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn" = link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn"
%br
%small * Make sure your email address is public
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
= "#{title} | " if defined?(title) = "#{title} | " if defined?(title)
GitLab GitLab
= favicon_link_tag 'favicon.ico' = favicon_link_tag 'favicon.ico'
= stylesheet_link_tag "application" = stylesheet_link_tag "application", :media => "all"
= stylesheet_link_tag "print", :media => "print"
= javascript_include_tag "application" = javascript_include_tag "application"
= csrf_meta_tags = csrf_meta_tags
= include_gon = include_gon
......
...@@ -14,10 +14,6 @@ ...@@ -14,10 +14,6 @@
.navbar-collapse.collapse .navbar-collapse.collapse
%ul.nav.navbar-nav %ul.nav.navbar-nav
%li.hidden-sm.hidden-xs
%a
%div.hide.turbolink-spinner
%i.icon-refresh.icon-spin
%li.hidden-sm.hidden-xs %li.hidden-sm.hidden-xs
= render "layouts/search" = render "layouts/search"
%li.visible-sm.visible-xs %li.visible-sm.visible-xs
......
:javascript :javascript
GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project)}" GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project, type: @noteable.class, type_id: params[:id])}"
GitLab.GfmAutoComplete.Emoji.assetBase = "#{Gitlab.config.gitlab.relative_url_root + '/assets/emoji'}"
GitLab.GfmAutoComplete.setup(); GitLab.GfmAutoComplete.setup();
...@@ -8,11 +8,15 @@ ...@@ -8,11 +8,15 @@
%span.separator %span.separator
%h1.title= title %h1.title= title
.pull-right %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"}
%span.sr-only Toggle navigation
%i.icon-reorder
.pull-right.hidden-xs
= link_to "Sign in", new_session_path(:user), class: 'btn btn-sign-in btn-new' = link_to "Sign in", new_session_path(:user), class: 'btn btn-sign-in btn-new'
%ul.nav.navbar-nav .navbar-collapse.collapse
%li %ul.nav.navbar-nav
%a %li.visible-xs
%div.hide.turbolink-spinner = link_to "Sign in", new_session_path(:user)
%i.icon-refresh.icon-spin
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%body{class: "#{app_theme} application", :'data-page' => body_data_page} %body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/public_head_panel", title: project_title(@project) = render "layouts/public_head_panel", title: project_title(@project)
%nav.main-nav %nav.main-nav.navbar-collapse.collapse
.container= render 'layouts/nav/project' .container= render 'layouts/nav/project'
.container .container
.content= yield .content= yield
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
%li %li
#{commit.short_id} - #{commit.title} #{commit.short_id} - #{commit.title}
%h4 Diff: %h4 Changes:
- @diffs.each do |diff| - @diffs.each do |diff|
%li %li
%strong %strong
...@@ -23,6 +23,6 @@ ...@@ -23,6 +23,6 @@
%br %br
- if @compare.timeout - if @compare.timeout
%h5 Huge diff. To prevent performance issues it was hidden %h5 To prevent performance issues changes are hidden
- elsif @compare.commits_over_limit? - elsif @compare.commits_over_limit?
%h5 Diff for big amount of commits is disabled %h5 Changes are not shown due to large amount of commits
...@@ -6,7 +6,7 @@ Commits: ...@@ -6,7 +6,7 @@ Commits:
#{commit.short_id} - #{truncate(commit.title, length: 40)} #{commit.short_id} - #{truncate(commit.title, length: 40)}
\ \
\ \
Diff: Changes:
- @diffs.each do |diff| - @diffs.each do |diff|
\ \
\===================================== \=====================================
...@@ -22,4 +22,4 @@ Diff: ...@@ -22,4 +22,4 @@ Diff:
- if @compare.timeout - if @compare.timeout
Huge diff. To prevent performance issues it was hidden Huge diff. To prevent performance issues it was hidden
- elsif @compare.commits_over_limit? - elsif @compare.commits_over_limit?
Diff for big amount of commits is disabled Changes are not shown due to large amount of commits
/ Side-by-side diff view / Side-by-side diff view
- old_file = get_old_file(project, @commit, diff) - old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file)
- deleted_lines = {} - num_lines = old_lines.length
- added_lines = {}
- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|
- if type == "old"
- deleted_lines[line_old] = { line_code: line_code, type: type, line: line }
- elsif type == "new"
- added_lines[line_new] = { line_code: line_code, type: type, line: line }
- max_length = old_file.sloc + added_lines.length if old_file
- max_length ||= file.sloc
- offset1 = 0
- offset2 = 0
%div.text-file-parallel %div.text-file-parallel
%table{ style: "table-layout: fixed;" } %div.diff-side.diff-side-left
- max_length.times do |line_index| %table
- line_index1 = line_index - offset1 - old_lines.each do |line|
- line_index2 = line_index - offset2
- deleted_line = deleted_lines[line_index1 + 1] %tr.line_holder.parallel
- added_line = added_lines[line_index2 + 1] - if line.type == :file_created
- old_line = old_file.lines[line_index1] if old_file %td.line_content.parallel= "File was created"
- new_line = file.lines[line_index2] - elsif line.type == :deleted
%td.line_content{class: "parallel noteable_line old #{line.code}", "line_code" => line.code }= line.content
- else line.type == :no_change
%td.line_content.parallel= line.content
%div.diff-middle
%table
- num_lines.times do |index|
%tr
- if old_lines[index].type == :deleted
%td.old_line.old= old_lines[index].num
- else
%td.old_line= old_lines[index].num
%td.diff_line=""
- if deleted_line && added_line - if new_lines[index].type == :added
- elsif deleted_line %td.new_line.new= new_lines[index].num
- new_line = nil - else
- offset2 += 1 %td.new_line= new_lines[index].num
- elsif added_line
- old_line = nil
- offset1 += 1
%tr.line_holder.parallel %div.diff-side.diff-side-right
- if line_index == 0 && diff.new_file %table
%td.line_content.parallel= "File was created" - new_lines.each do |line|
%td.old_line= ""
- elsif deleted_line
%td.line_content{class: "parallel noteable_line old #{deleted_line[:line_code]}", "line_code" => deleted_line[:line_code] }= old_line
%td.old_line.old
= line_index1 + 1
- if @comments_allowed
=# render "projects/notes/diff_note_link", line_code: deleted_line[:line_code]
- elsif old_line
%td.line_content.parallel= old_line
%td.old_line= line_index1 + 1
- else
%td.line_content.parallel= ""
%td.old_line= ""
%td.diff_line= "" %tr.line_holder.parallel
- if line.type == :file_deleted
%td.line_content.parallel= "File was deleted"
- elsif line.type == :added
%td.line_content{class: "parallel noteable_line new #{line.code}", "line_code" => line.code }= line.content
- else line.type == :no_change
%td.line_content.parallel= line.content
- if diff.deleted_file && line_index == 0 :javascript
%td.new_line= "" $('.diff-side-right').on('scroll', function(){
%td.line_content.parallel= "File was deleted" $('.diff-side-left, .diff-middle').scrollTop($(this).scrollTop());
- elsif added_line $('.diff-side-left').scrollLeft($(this).scrollLeft());
%td.new_line.new });
= line_index2 + 1
- if @comments_allowed
=# render "projects/notes/diff_note_link", line_code: added_line[:line_code]
%td.line_content{class: "parallel noteable_line new #{added_line[:line_code]}", "line_code" => added_line[:line_code] }= new_line
- elsif new_line
%td.new_line= line_index2 + 1
%td.line_content.parallel= new_line
- else
%td.new_line= ""
%td.line_content.parallel= ""
- if @reply_allowed $('.diff-side-left').on('scroll', function(){
- comments1 = [] $('.diff-side-right, .diff-middle').scrollTop($(this).scrollTop()); // might never be relevant
- comments2 = [] $('.diff-side-right').scrollLeft($(this).scrollLeft());
- comments1 = @line_notes.select { |n| n.line_code == deleted_line[:line_code] }.sort_by(&:created_at) if deleted_line });
- comments2 = @line_notes.select { |n| n.line_code == added_line[:line_code] }.sort_by(&:created_at) if added_line
- unless comments1.empty? && comments2.empty?
= render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2, line1: deleted_line, line2: added_line
\ No newline at end of file
- too_big = diff.diff.lines.count > 1000 - too_big = diff.diff.lines.count > 1000
- if too_big - if too_big
%a.supp_diff_link Diff suppressed. Click to show %a.supp_diff_link Changes suppressed. Click to show
%table.text-file{class: "#{'hide' if too_big}"} %table.text-file{class: "#{'hide' if too_big}"}
- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line| - each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|
......
...@@ -18,17 +18,17 @@ ...@@ -18,17 +18,17 @@
- else - else
%ul.well-list= render Commit.decorate(@commits), project: @project %ul.well-list= render Commit.decorate(@commits), project: @project
%h4 Diff %h4 Changes
- if @diffs.present? - if @diffs.present?
= render "projects/commits/diffs", diffs: @diffs, project: @project = render "projects/commits/diffs", diffs: @diffs, project: @project
- elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
.bs-callout.bs-callout-danger .bs-callout.bs-callout-danger
%h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits. %h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
%p To preserve performance the line diff is not shown. %p To preserve performance the line changes are not shown.
- elsif @timeout - elsif @timeout
.bs-callout.bs-callout-danger .bs-callout.bs-callout-danger
%h4 Diff for this comparison is extremely large. %h4 Number of changed files for this comparison is extremely large.
%p Use command line to browse diff for this comparison. %p Use command line to browse through changes for this comparison.
- else - else
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Deploy key: Deploy key:
= @key.title = @key.title
%small %small
created at created on
= @key.created_at.stamp("Aug 21, 2011") = @key.created_at.stamp("Aug 21, 2011")
.back-link .back-link
= link_to project_deploy_keys_path(@project) do = link_to project_deploy_keys_path(@project) do
......
%div.issue-form-holder %div.issue-form-holder
%h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}" %h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}"
%hr %hr
- if @repository.contribution_guide && !@issue.persisted? - if !@repository.empty? && @repository.contribution_guide && !@issue.persisted?
- contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name)) - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
.alert.alert-info.col-sm-10.col-sm-offset-2 .alert.alert-info.col-sm-10.col-sm-offset-2
="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe ="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe
......
- if @repository.contribution_guide && !@merge_request.persisted?
- contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
.alert.alert-info.col-sm-10.col-sm-offset-2
="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe
= form_for [@project, @merge_request], html: { class: "merge-request-form form-horizontal" } do |f| = form_for [@project, @merge_request], html: { class: "merge-request-form form-horizontal" } do |f|
-if @merge_request.errors.any? .row
.alert.alert-danger .col-sm-2
%ul .col-sm-10
- @merge_request.errors.full_messages.each do |msg| - if @repository.contribution_guide && !@merge_request.persisted?
%li= msg - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
.alert.alert-info
Please review the
%strong #{link_to "guidelines for contribution", contribution_guide_url}
to this repository.
-if @merge_request.errors.any?
.alert.alert-danger
- @merge_request.errors.full_messages.each do |msg|
%div= msg
.merge-request-branches .merge-request-branches
.form-group .form-group
...@@ -47,24 +52,6 @@ ...@@ -47,24 +52,6 @@
= f.text_area :description, class: "form-control js-gfm-input", rows: 14 = f.text_area :description, class: "form-control js-gfm-input", rows: 14
%p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. %p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
%hr
.form-group
.merge-request-assignee
= f.label :assignee_id, class: 'control-label' do
%i.icon-user
Assign to
.col-sm-10
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
.form-group
.merge-request-milestone
= f.label :milestone_id, class: 'control-label' do
%i.icon-time
Milestone
.col-sm-10= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2'})
.form-actions .form-actions
- if @merge_request.new_record? - if @merge_request.new_record?
= f.submit 'Submit merge request', class: "btn btn-create" = f.submit 'Submit merge request', class: "btn btn-create"
...@@ -96,7 +83,3 @@ ...@@ -96,7 +83,3 @@
target_branch.on("change", function() { target_branch.on("change", function() {
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() }); $.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
}); });
$('.assign-to-me-link').on('click', function(e){
$('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");
e.preventDefault();
});
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
%li.diffs-tab{data: {action: 'diffs'}} %li.diffs-tab{data: {action: 'diffs'}}
= link_to diffs_project_merge_request_path(@project, @merge_request) do = link_to diffs_project_merge_request_path(@project, @merge_request) do
%i.icon-list-alt %i.icon-list-alt
Diff Changes
- content_for :note_actions do - content_for :note_actions do
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
......
...@@ -3,5 +3,5 @@ ...@@ -3,5 +3,5 @@
var mrTitle = $('#merge_request_title'); var mrTitle = $('#merge_request_title');
if(mrTitle.val().length == 0) { if(mrTitle.val().length == 0) {
mrTitle.val("#{params[:ref].titleize}"); mrTitle.val("#{params[:ref].humanize}");
} }
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- else - else
.bs-callout.bs-callout-warning .bs-callout.bs-callout-warning
%h4 %h4
Diff for this comparison is extremely large. Changes view for this comparison is extremely large.
%p %p
You can You can
= link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request, format: :diff), class: "vlink" = link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request, format: :diff), class: "vlink"
......
- unless @allowed_to_merge - unless @allowed_to_merge
.bs-callout - if @project.archived?
%strong You don't have permission to merge this MR .bs-callout.bs-callout-warning
%strong Archived projects cannot be committed to!
- else
.bs-callout
.automerge_widget.cannot_be_merged.hide
%strong This can't be merged automatically, even if it could be merged you don't have the permission to do so.
.automerge_widget.can_be_merged.hide
%strong This can be merged automatically but you don't have the permission to do so.
- if @show_merge_controls - if @show_merge_controls
......
...@@ -29,13 +29,13 @@ ...@@ -29,13 +29,13 @@
%span %span
%i.icon-remove %i.icon-remove
Closed by #{link_to_member(@project, @merge_request.closed_event.author)} Closed by #{link_to_member(@project, @merge_request.closed_event.author)}
#{time_ago_with_tooltip(@merge_request.closed_event.created_at)}. #{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
- if @merge_request.merged? - if @merge_request.merged?
.alert.alert-info .alert.alert-info
%span %span
%i.icon-ok %i.icon-ok
Merged by #{link_to_member(@project, @merge_request.merge_event.author)} Merged by #{link_to_member(@project, @merge_request.merge_event.author)}
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)}. #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
- if !@closes_issues.empty? && @merge_request.open? - if !@closes_issues.empty? && @merge_request.open?
.alert.alert-info.alert-info .alert.alert-info.alert-info
%span %span
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
%i.icon-thumbs-up %i.icon-thumbs-up
\+1 \+1
- if note.downvote? - if note.downvote?
%span.vote.downvote.label.label-error %span.vote.downvote.label.label-danger
%i.icon-thumbs-down %i.icon-thumbs-down
\-1 \-1
......
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
.col-md-3.project-side.hidden-sm .col-md-3.project-side.hidden-sm
.clearfix .clearfix
- if @project.archived? - if @project.archived?
.alert .alert.alert-warning
%h5 %h4
%i.icon-warning-sign %i.icon-warning-sign
Archived project! Archived project!
%p Repository is read-only %p Repository is read-only
......
...@@ -19,7 +19,7 @@ module Gitlab ...@@ -19,7 +19,7 @@ module Gitlab
# config.plugins = [ :exception_notification, :ssl_requirement, :all ] # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running. # Activate observers that should always be running.
config.active_record.observers = :activity_observer, config.active_record.observers = :milestone_observer,
:project_activity_cache_observer, :project_activity_cache_observer,
:issue_observer, :issue_observer,
:key_observer, :key_observer,
......
...@@ -33,6 +33,12 @@ Gitlab::Application.configure do ...@@ -33,6 +33,12 @@ Gitlab::Application.configure do
# See everything in the log (default is :info) # See everything in the log (default is :info)
# config.log_level = :debug # config.log_level = :debug
# Suppress 'Rendered template ...' messages in the log
# source: http://stackoverflow.com/a/16369363
%w{render_template render_partial render_collection}.each do |event|
ActiveSupport::Notifications.unsubscribe "#{event}.action_view"
end
# Prepend all log lines with the following tags # Prepend all log lines with the following tags
# config.log_tags = [ :subdomain, :uuid ] # config.log_tags = [ :subdomain, :uuid ]
......
# Workaround for https://github.com/github/gemoji/pull/18
require 'gemoji'
Gitlab::Application.config.assets.paths << Emoji.images_path
...@@ -167,7 +167,7 @@ Gitlab::Application.routes.draw do ...@@ -167,7 +167,7 @@ Gitlab::Application.routes.draw do
resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create] resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create]
devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations } devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations , passwords: :passwords}
# #
# Project Area # Project Area
......
worker_processes 2
timeout 30
...@@ -2,7 +2,7 @@ Gitlab::Seeder.quiet do ...@@ -2,7 +2,7 @@ Gitlab::Seeder.quiet do
User.first(30).each_with_index do |user, i| User.first(30).each_with_index do |user, i|
Key.seed(:id, [ Key.seed(:id, [
{ {
id: i, id: i + 1,
title: "Sample key #{i}", title: "Sample key #{i}",
key: "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt#{i + 100}6k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", key: "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt#{i + 100}6k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
user_id: user.id, user_id: user.id,
......
## The GitLab Documentation covers the following subjects
+ [API](api/README.md)
+ [Development](development/README.md)
+ [Install](install/README.md)
+ [Integration](external-issue-tracker/README.md)
+ [Legal](legal/README.md)
+ [Markdown](markdown/markdown.md)
+ [Permissions](permissions/permissions.md)
+ [Public access](public_access/public_access.md)
+ [Raketasks](raketasks/README.md)
+ [Release](release/README.md)
+ [Security](security/README.md)
+ [System hooks](system_hooks/system_hooks.md)
+ [Update](update/README.md)
+ [Web hooks](web_hooks/web_hooks.md)
+ [Workflow](workflow/workflow.md)
# GitLab API # GitLab API
## End-points
+ [Users](users.md)
+ [Session](session.md)
+ [Projects](projects.md)
+ [Project Snippets](project_snippets.md)
+ [Repositories](repositories.md)
+ [Repository Files](repository_files.md)
+ [Commits](commits.md)
+ [Merge Requests](merge_requests.md)
+ [Issues](issues.md)
+ [Milestones](milestones.md)
+ [Notes](notes.md)
+ [Deploy Keys](deploy_keys.md)
+ [System Hooks](system_hooks.md)
+ [Groups](groups.md)
## Clients
+ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP
+ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby
+ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python
+ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java
## Introduction
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. 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.
If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401: If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401:
...@@ -117,30 +143,3 @@ Issue ...@@ -117,30 +143,3 @@ Issue
So if you want to get issue with api you use `http://host/api/v3/.../issues/:id.json` So if you want to get issue with api you use `http://host/api/v3/.../issues/:id.json`
But when you want to create a link to web page - use `http:://host/project/issues/:iid.json` But when you want to create a link to web page - use `http:://host/project/issues/:iid.json`
## Contents
+ [Users](users.md)
+ [Session](session.md)
+ [Projects](projects.md)
+ [Project Snippets](project_snippets.md)
+ [Repositories](repositories.md)
+ [Repository Files](repository_files.md)
+ [Commits](commits.md)
+ [Merge Requests](merge_requests.md)
+ [Issues](issues.md)
+ [Milestones](milestones.md)
+ [Notes](notes.md)
+ [Deploy Keys](deploy_keys.md)
+ [System Hooks](system_hooks.md)
+ [Groups](groups.md)
## Clients
+ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP
+ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby
+ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python
+ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java
...@@ -150,6 +150,7 @@ Parameters: ...@@ -150,6 +150,7 @@ Parameters:
+ `target_branch` - The target branch + `target_branch` - The target branch
+ `assignee_id` - Assignee user ID + `assignee_id` - Assignee user ID
+ `title` - Title of MR + `title` - Title of MR
+ `state_event` - New state (close|reopen|merge)
```json ```json
...@@ -210,3 +211,44 @@ Parameters: ...@@ -210,3 +211,44 @@ Parameters:
"note":"text1" "note":"text1"
} }
``` ```
## Get the comments on a MR
Gets all the comments associated with a merge request.
```
GET /projects/:id/merge_request/:merge_request_id/comments
```
Parameters:
+ `id` (required) - The ID of a project
+ `merge_request_id` (required) - ID of merge request
```json
[
{
"note":"this is the 1st comment on the 2merge merge request",
"author":{
"id":11,
"username":"admin",
"email":"admin@local.host",
"name":"Administrator",
"state":"active",
"created_at":"2014-03-06T08:17:35.000Z"
}
},
{
"note":"_Status changed to closed_",
"author":{
"id":11,
"username":"admin",
"email":"admin@local.host",
"name":"Administrator",
"state":"active",
"created_at":"2014-03-06T08:17:35.000Z"
}
}
]
```
...@@ -621,3 +621,29 @@ Parameters: ...@@ -621,3 +621,29 @@ Parameters:
+ query (required) - A string contained in the project name + query (required) - A string contained in the project name
+ per_page (optional) - number of projects to return per page + per_page (optional) - number of projects to return per page
+ page (optional) - the page to retrieve + page (optional) - the page to retrieve
## Labels
### List project labels
Get a list of project labels.
```
GET /projects/:id/labels
```
Parameters:
+ `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
```json
[
{
"name":"featute"
},
{
"name": "bug"
}
]
```
All methods require admin authorization. All methods require admin authorization.
The url endpoint of the system hooks can be configured in [the admin area under hooks](/admin/hooks). The url endpoint of the system hooks can be configured in [the admin area under hooks](/admin/hooks).
## List system hooks ## List system hooks
Get list of system hooks Get list of system hooks
``` ```
GET /hooks GET /hooks
``` ```
Parameters: Parameters:
+ **none** + **none**
```json ```json
[ [
{ {
"id":3, "id":3,
"url":"http://example.com/hook", "url":"http://example.com/hook",
"created_at":"2013-10-02T10:15:31Z" "created_at":"2013-10-02T10:15:31Z"
} }
] ]
``` ```
## Add new system hook hook ## Add new system hook hook
``` ```
POST /hooks POST /hooks
``` ```
Parameters: Parameters:
+ `url` (required) - The hook URL + `url` (required) - The hook URL
## Test system hook ## Test system hook
``` ```
GET /hooks/:id GET /hooks/:id
``` ```
Parameters: Parameters:
+ `id` (required) - The ID of hook + `id` (required) - The ID of hook
```json ```json
{ {
"event_name":"project_create", "event_name":"project_create",
"name":"Ruby", "name":"Ruby",
"path":"ruby", "path":"ruby",
"project_id":1, "project_id":1,
"owner_name":"Someone", "owner_name":"Someone",
"owner_email":"example@gitlabhq.com" "owner_email":"example@gitlabhq.com"
} }
``` ```
## Delete system hook ## Delete system hook
Deletes a system hook. This is an idempotent API function and returns `200 Ok` even if the hook Deletes a system hook. This is an idempotent API function and returns `200 Ok` even if the hook
is not available. If the hook is deleted it is also returned as JSON. is not available. If the hook is deleted it is also returned as JSON.
``` ```
DELETE /hooks/:id DELETE /hooks/:id
``` ```
Parameters: Parameters:
+ `id` (required) - The ID of hook + `id` (required) - The ID of hook
+ [Architecture](architecture.md)
+ [Shell commands](shell_commands.md)
...@@ -18,7 +18,7 @@ New releases are generally around the same time as GitLab CE releases with excep ...@@ -18,7 +18,7 @@ New releases are generally around the same time as GitLab CE releases with excep
# System Layout # System Layout
When referring to ~git in the picures it means the home directory of the git user which is typically /home/git. When referring to ~git in the pictures it means the home directory of the git user which is typically /home/git.
GitLab is primarily installed within the `/home/git` user home directory as `git` user. GitLab is primarily installed within the `/home/git` user home directory as `git` user.
Within the home directory is where the gitlabhq server software resides as well as the repositories (though the repository location is configurable). 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,7 +28,7 @@ To serve repositories over SSH there's an add-on application called gitlab-shell
## Components ## Components
![GitLab Diagram Overview](resources/gitlab_diagram_overview.png "GitLab Diagram Overview") ![GitLab Diagram Overview](resources/gitlab_diagram_overview.png)
A typical install of GitLab will be on Ubuntu Linux or RHEL/CentOS. A typical install of GitLab will be on Ubuntu Linux or RHEL/CentOS.
It uses Nginx or Apache as a web front end to proxypass the Unicorn web server. It uses Nginx or Apache as a web front end to proxypass the Unicorn web server.
...@@ -180,4 +180,4 @@ bundle exec rake gitlab:check RAILS_ENV=production ...@@ -180,4 +180,4 @@ bundle exec rake gitlab:check RAILS_ENV=production
``` ```
Note: It is recommended to log into the `git` user using `sudo -i -u git` or `sudo su - git`. Note: It is recommended to log into the `git` user using `sudo -i -u git` or `sudo su - git`.
While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL. While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL.
\ No newline at end of file
# Guidelines for shell commands in the GitLab codebase # Guidelines for shell commands in the GitLab codebase
## References
- [Google Ruby Security Reviewer's Guide](https://code.google.com/p/ruby-security/wiki/Guide)
- [OWASP Command Injection](https://www.owasp.org/index.php/Command_Injection)
- [Ruby on Rails Security Guide Command Line Injection](http://guides.rubyonrails.org/security.html#command-line-injection)
## Use File and FileUtils instead of shell commands ## Use File and FileUtils instead of shell commands
Sometimes we invoke basic Unix commands via the shell when there is also a Ruby API for doing it. Sometimes we invoke basic Unix commands via the shell when there is also a Ruby API for doing it.
......
+ [Installation](installation.md)
+ [Requirements](requirements.md)
+ [Structure](structure.md)
+ [Database MySQL](database_mysql.md)
...@@ -6,6 +6,9 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se ...@@ -6,6 +6,9 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# Install the database packages # Install the database packages
sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
# Ensure you have MySQL version 5.5.14 or later
mysql --version
# Pick a database root password (can be anything), type it and press enter # Pick a database root password (can be anything), type it and press enter
# Retype the database root password and press enter # Retype the database root password and press enter
...@@ -23,6 +26,10 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se ...@@ -23,6 +26,10 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# change $password in the command below to a real password you pick # change $password in the command below to a real password you pick
mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password'; mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password';
# Ensure you can use the InnoDB engine which is necessary to support long indexes.
# If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off"
mysql> SET storage_engine=INNODB;
# Create the GitLab production database # Create the GitLab production database
mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
......
...@@ -128,7 +128,7 @@ GitLab Shell is an ssh access and repository management software developed speci ...@@ -128,7 +128,7 @@ GitLab Shell is an ssh access and repository management software developed speci
cd /home/git cd /home/git
# Clone gitlab shell # Clone gitlab shell
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-shell.git -b v1.8.0 sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-shell.git -b v1.9.1
cd gitlab-shell cd gitlab-shell
...@@ -173,7 +173,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ...@@ -173,7 +173,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
## Clone the Source ## Clone the Source
# Clone GitLab repository # Clone GitLab repository
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 6-6-stable gitlab sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 6-7-stable gitlab
# Go to gitlab dir # Go to gitlab dir
cd /home/git/gitlab cd /home/git/gitlab
......
...@@ -5,3 +5,5 @@ GitLab has a great issue tracker but you can also use an external issue tracker ...@@ -5,3 +5,5 @@ GitLab has a great issue tracker but you can also use an external issue tracker
- textual references to PROJECT-1234 in comments, commit messages get turned into HTML links to the corresponding JIRA issue. - textual references to PROJECT-1234 in comments, commit messages get turned into HTML links to the corresponding JIRA issue.
![jira screenshot](jira-intergration-points.png) ![jira screenshot](jira-intergration-points.png)
You can configure the integration in the gitlab.yml configuration file.
+ [Corporate contributor license agreement](corporate_contributor_license_agreement.md)
+ [Individual contributor license agreement](individual_contributor_license_agreement.md)
...@@ -38,7 +38,7 @@ If a user is a GitLab administrator they receive all permissions. ...@@ -38,7 +38,7 @@ If a user is a GitLab administrator they receive all permissions.
|------|-----|--------|---------|------|-----| |------|-----|--------|---------|------|-----|
|Browse group|✓|✓|✓|✓|✓| |Browse group|✓|✓|✓|✓|✓|
|Edit group|||||✓| |Edit group|||||✓|
|create project in group|||||✓| |Create project in group|||||✓|
|Manage group members|||||✓| |Manage group members|||||✓|
|Remove group|||||✓| |Remove group|||||✓|
......
+ [Backup restore](backup_restore.md)
+ [Cleanup](cleanup.md)
+ [Features](features.md)
+ [Maintenance](maintenance.md)
+ [User management](user_management.md)
+ [Web hooks](web_hooks.md)
+ [Monthly](monthly.md)
+ [Security](security.md)
...@@ -58,19 +58,17 @@ Check if changed since last release (~22nd of last month depending on when last ...@@ -58,19 +58,17 @@ Check if changed since last release (~22nd of last month depending on when last
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: 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:
* 17th: feature freeze (stop merging new features in master) * 1-7th: official merge window (see contributing guide)
* 18th: UI freeze (stop merging changes to the user interface) * 8-14th: work on bugfixes, sponsored features and GitLab EE
* 19th: code freeze (stop merging non-essential code improvements) * 15th: code freeze (stop merging into master except essential bugfixes)
* 20th: release candidate 1 (VERSION x.x.0.rc1, tag and tweet about x.x.0.rc1) * 18th: release candidate 1 (VERSION x.x.0.rc1, tag and tweet about x.x.0.rc1, release on GitLab Cloud)
* 21st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems) * 20st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems)
* 22nd: release (VERSION x.x.0, create x-x-stable branch, tag, blog and tweet) * 22nd: release (VERSION x.x.0, create x-x-stable branch, tag, blog and tweet)
* 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems) * 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems)
* 24-end of month: release Enterprise Edition and upgrade GitLab Cloud * 24-end of month: release GitLab EE and GitLab CI
* 1-7th: official merge window (see contributing guide)
* 8-16th: bugfixes and sponsored features
# Write a blog post # Write a blog post
* Mention what GitLab is on the second line: GitLab is open source software to collaborate on code. * Mention what GitLab is on the second line: GitLab is open source software to collaborate on code.
* Select and thank the the Most Valuable Person (MVP) of this release. * Select and thank the the Most Valuable Person (MVP) of this release.
* 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. * 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.
\ No newline at end of file
+ [Password length limits](password_length_limits.md)
+ [Rack attack](rack_attack.md)
Deploy keys allow read-only access one or multiple projects with a single SSH key.
This is really useful for cloning repositories to your Continuous Integration (CI) server.
By using a deploy keys you don't have to setup a dummy user account.
If you are a project master or owner you can add a deploy key in the project settings under the section Deploy Keys.
Press the 'New Deploy Key' button and upload a public ssh key.
After this the machine that uses the corresponding private key has read-only access to the project.
You can't add the same deploy key twice with the 'New Deploy Key' option.
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'.
You need to be the owner of the deploy key to see it in this list.
# From 4.2 to 5.0 # From 4.2 to 5.0
## Warning ## Warning
GitLab 5.0 is affected by critical security vulnerability CVE-2013-4490. Please update to GitLab 5.4 immediately. GitLab 5.0 is affected by critical security vulnerability CVE-2013-4490.
## Important changes ## Important changes
......
# From 5.0 to 5.1 # From 5.0 to 5.1
## Warning ## Warning
GitLab 5.1 is affected by critical security vulnerability CVE-2013-4490. Please [update to GitLab 5.4 immediately](5.1-to-5.4.md). GitLab 5.1 is affected by critical security vulnerability CVE-2013-4490.
## Release notes: ## Release notes:
......
# From 5.1 to 5.2 # From 5.1 to 5.2
## Warning ## Warning
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). GitLab 5.2 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
### 0. Backup ### 0. Backup
......
# From 5.1 to 5.4 # From 5.1 to 5.4
Also works starting from 5.2. Also works starting from 5.2.
## Notice
Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 5.4.
### 0. Backup ### 0. Backup
It's useful to make a backup just in case things go south: It's useful to make a backup just in case things go south:
......
# From 5.1 to 6.0 # From 5.1 to 6.0
## Warning ## Warning
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). GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
### Deprecations ### Deprecations
......
# From 5.2 to 5.3 # From 5.2 to 5.3
## Warning ## Warning
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). GitLab 5.3 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
### 0. Backup ### 0. Backup
......
# From 5.3 to 5.4 # From 5.3 to 5.4
## Notice
Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 5.4.
### 0. Backup ### 0. Backup
It's useful to make a backup just in case things go south: It's useful to make a backup just in case things go south:
......
# From 5.4 to 6.0 # From 5.4 to 6.0
## Warning ## Warning
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). GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
### Deprecations ### Deprecations
......
# From 6.0 to 6.1 # From 6.0 to 6.1
## Warning ## Warning
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). GitLab 6.1 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
# In 6.1 we remove a lot of deprecated code. # In 6.1 we remove a lot of deprecated code.
# You should update to 6.0 before installing 6.1 so all the necessary conversions are run. # You should update to 6.0 before installing 6.1 so all the necessary conversions are run.
......
# From 6.0 to 6.5 # From 6.0 to 6.7
# In 6.1 we remove a lot of deprecated code. # In 6.1 we remove a lot of deprecated code.
# You should update to 6.0 before installing 6.1 or higher so all the necessary conversions are run. # You should update to 6.0 before installing 6.1 or higher so all the necessary conversions are run.
...@@ -28,8 +28,20 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production ...@@ -28,8 +28,20 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```bash ```bash
cd /home/git/gitlab cd /home/git/gitlab
sudo -u git -H git fetch --all sudo -u git -H git fetch --all
sudo -u git -H git checkout 6-5-stable ```
# For GitLab Enterprise Edition: sudo -u git -H git checkout 6-5-stable-ee
For Gitlab Community Edition:
```bash
sudo -u git -H git checkout 6-7-stable
```
OR
For GitLab Enterprise Edition:
```bash
sudo -u git -H git checkout 6-7-stable-ee
``` ```
...@@ -45,7 +57,7 @@ sudo apt-get install logrotate ...@@ -45,7 +57,7 @@ sudo apt-get install logrotate
```bash ```bash
cd /home/git/gitlab-shell cd /home/git/gitlab-shell
sudo -u git -H git fetch sudo -u git -H git fetch
sudo -u git -H git checkout v1.8.0 # Addresses multiple critical security vulnerabilities sudo -u git -H git checkout v1.9.1 # Addresses multiple critical security vulnerabilities
``` ```
### 5. Install libs, migrations, etc. ### 5. Install libs, migrations, etc.
...@@ -60,11 +72,14 @@ sudo -u git -H bundle install --without development test postgres --deployment ...@@ -60,11 +72,14 @@ sudo -u git -H bundle install --without development test postgres --deployment
sudo -u git -H bundle install --without development test mysql --deployment sudo -u git -H bundle install --without development test mysql --deployment
# Run database migrations
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Enable internal issue IDs (introduced in GitLab 6.1)
sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production
sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production # Clean up assets and cache
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
``` ```
### 6. Update config files ### 6. Update config files
...@@ -72,11 +87,11 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production ...@@ -72,11 +87,11 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
TIP: to see what changed in gitlab.yml.example in this release use next command: TIP: to see what changed in gitlab.yml.example in this release use next command:
``` ```
git diff 6-0-stable:config/gitlab.yml.example 6-5-stable:config/gitlab.yml.example git diff 6-0-stable:config/gitlab.yml.example 6-7-stable:config/gitlab.yml.example
``` ```
* 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. * 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.
* 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. * 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.
* Copy rack attack middleware config * Copy rack attack middleware config
```bash ```bash
......
# From 6.1 to 6.2 # From 6.1 to 6.2
## Notice
Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 6.2.
# You should update to 6.1 before installing 6.2 so all the necessary conversions are run. # You should update to 6.1 before installing 6.2 so all the necessary conversions are run.
### 0. Backup ### 0. Backup
......
...@@ -37,7 +37,7 @@ sudo -u git -H git checkout 6-7-stable-ee ...@@ -37,7 +37,7 @@ sudo -u git -H git checkout 6-7-stable-ee
```bash ```bash
cd /home/git/gitlab-shell cd /home/git/gitlab-shell
sudo -u git -H git fetch sudo -u git -H git fetch
sudo -u git -H git checkout v1.8.0 sudo -u git -H git checkout v1.9.1
``` ```
### 4. Install libs, migrations, etc. ### 4. Install libs, migrations, etc.
......
+ [The indivual upgrade guides](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update)
+ [Uprader](upgrader.md)
+ [Ruby](ruby.md)
+ [Patch versions](patch_versions.md)
+ [MySQL to Postgress](mysql_to_postgress.md)
...@@ -5,6 +5,7 @@ Project web hooks allow you to trigger an URL if new code is pushed or a new iss ...@@ -5,6 +5,7 @@ Project web hooks allow you to trigger an URL if new code is pushed or a new iss
You can configure web hook to listen for specific events like pushes, issues, merge requests. You can configure web hook to listen for specific events like pushes, issues, merge requests.
GitLab will send POST request with data to web hook URL. GitLab will send POST request with data to web hook URL.
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. 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.
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.
--- ---
......
...@@ -37,6 +37,12 @@ Feature: Project Services ...@@ -37,6 +37,12 @@ Feature: Project Services
And I fill Assembla settings And I fill Assembla settings
Then I should see Assembla service settings saved Then I should see Assembla service settings saved
Scenario: Activate Slack service
When I visit project "Shop" services page
And I click Slack service link
And I fill Slack settings
Then I should see Slack service settings saved
Scenario: Activate email on push service Scenario: Activate email on push service
When I visit project "Shop" services page When I visit project "Shop" services page
And I click email on push service link And I click email on push service link
......
...@@ -25,7 +25,7 @@ class Dashboard < Spinach::FeatureSteps ...@@ -25,7 +25,7 @@ class Dashboard < Spinach::FeatureSteps
find("#merge_request_target_project_id").value.should == @project.id.to_s find("#merge_request_target_project_id").value.should == @project.id.to_s
find("#merge_request_source_branch").value.should == "new_design" find("#merge_request_source_branch").value.should == "new_design"
find("#merge_request_target_branch").value.should == "master" find("#merge_request_target_branch").value.should == "master"
find("#merge_request_title").value.should == "New Design" find("#merge_request_title").value.should == "New design"
end end
Given 'user with name "John Doe" joined project "Shop"' do Given 'user with name "John Doe" joined project "Shop"' do
......
...@@ -4,7 +4,7 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps ...@@ -4,7 +4,7 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps
include SharedProject include SharedProject
step 'public project "Community"' do step 'public project "Community"' do
create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC create :project, :public, name: 'Community'
end end
step 'private project "Enterprise"' do step 'private project "Enterprise"' do
......
...@@ -100,4 +100,22 @@ class ProjectServices < Spinach::FeatureSteps ...@@ -100,4 +100,22 @@ class ProjectServices < Spinach::FeatureSteps
step 'I should see email on push service settings saved' do step 'I should see email on push service settings saved' do
find_field('Recipients').value.should == 'qa@company.name' find_field('Recipients').value.should == 'qa@company.name'
end end
step 'I click Slack service link' do
click_link 'Slack'
end
step 'I fill Slack settings' do
check 'Active'
fill_in 'Subdomain', with: 'gitlab'
fill_in 'Room', with: '#gitlab'
fill_in 'Token', with: 'verySecret'
click_button 'Save'
end
step 'I should see Slack service settings saved' do
find_field('Subdomain').value.should == 'gitlab'
find_field('Room').value.should == '#gitlab'
find_field('Token').value.should == 'verySecret'
end
end end
...@@ -4,7 +4,7 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps ...@@ -4,7 +4,7 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
include SharedProject include SharedProject
step 'public empty project "Empty Public Project"' do step 'public empty project "Empty Public Project"' do
create :empty_project, name: 'Empty Public Project', visibility_level: Gitlab::VisibilityLevel::PUBLIC create :empty_project, :public, name: 'Empty Public Project'
end end
step 'I should see project "Empty Public Project"' do step 'I should see project "Empty Public Project"' do
......
...@@ -79,7 +79,7 @@ module SharedProject ...@@ -79,7 +79,7 @@ module SharedProject
end end
step 'internal project "Internal"' do step 'internal project "Internal"' do
create :project, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL create :project, :internal, name: 'Internal'
end end
step 'I should see project "Internal"' do step 'I should see project "Internal"' do
...@@ -91,7 +91,7 @@ module SharedProject ...@@ -91,7 +91,7 @@ module SharedProject
end end
step 'public project "Community"' do step 'public project "Community"' do
create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC create :project, :public, name: 'Community'
end end
step 'I should see project "Community"' do step 'I should see project "Community"' do
...@@ -112,14 +112,14 @@ module SharedProject ...@@ -112,14 +112,14 @@ module SharedProject
step '"John Doe" is authorized to internal project "Internal"' do step '"John Doe" is authorized to internal project "Internal"' do
user = user_exists("John Doe", username: "john_doe") user = user_exists("John Doe", username: "john_doe")
project = Project.find_by(name: "Internal") project = Project.find_by(name: "Internal")
project ||= create :project, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL project ||= create :project, :internal, name: 'Internal'
project.team << [user, :master] project.team << [user, :master]
end end
step '"John Doe" is authorized to public project "Community"' do step '"John Doe" is authorized to public project "Community"' do
user = user_exists("John Doe", username: "john_doe") user = user_exists("John Doe", username: "john_doe")
project = Project.find_by(name: "Community") project = Project.find_by(name: "Community")
project ||= create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC project ||= create :project, :public, name: 'Community'
project.team << [user, :master] project.team << [user, :master]
end end
end end
...@@ -187,5 +187,9 @@ module API ...@@ -187,5 +187,9 @@ module API
end end
end end
end end
class Label < Grape::Entity
expose :name
end
end end
end end
module API module API
# Internal access API # Internal access API
class Internal < Grape::API class Internal < Grape::API
DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }
PUSH_COMMANDS = %w{ git-receive-pack }
namespace 'internal' do namespace 'internal' do
# # Check if git command is allowed to project
# Check if ssh key has access to project code
# #
# Params: # Params:
# key_id - SSH Key id # key_id - ssh key id for Git over SSH
# user_id - user id for Git over HTTP
# project - project path with namespace # project - project path with namespace
# action - git action (git-upload-pack or git-receive-pack) # action - git action (git-upload-pack or git-receive-pack)
# ref - branch name # ref - branch name
...@@ -22,43 +18,25 @@ module API ...@@ -22,43 +18,25 @@ module API
# the wiki repository as well. # the wiki repository as well.
project_path = params[:project] project_path = params[:project]
project_path.gsub!(/\.wiki/,'') if project_path =~ /\.wiki/ project_path.gsub!(/\.wiki/,'') if project_path =~ /\.wiki/
key = Key.find(params[:key_id])
project = Project.find_with_namespace(project_path) project = Project.find_with_namespace(project_path)
git_cmd = params[:action]
return false unless project return false unless project
actor = if params[:key_id]
if key.is_a? DeployKey Key.find(params[:key_id])
key.projects.include?(project) && DOWNLOAD_COMMANDS.include?(git_cmd) elsif params[:user_id]
else User.find(params[:user_id])
user = key.user end
return false if user.blocked? return false unless actor
if Gitlab.config.ldap.enabled Gitlab::GitAccess.new.allowed?(
if user.ldap_user? actor,
# Check if LDAP user exists and match LDAP user_filter params[:action],
unless Gitlab::LDAP::Access.new.allowed?(user) project,
return false params[:ref],
end params[:oldrev],
end params[:newrev]
end )
action = case git_cmd
when *DOWNLOAD_COMMANDS
then :download_code
when *PUSH_COMMANDS
then
if project.protected_branch?(params[:ref])
:push_code_to_protected_branches
else
:push_code
end
end
user.can?(action, project)
end
end end
# #
......
...@@ -125,6 +125,22 @@ module API ...@@ -125,6 +125,22 @@ module API
end end
end end
# Get a merge request's comments
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# Examples:
# GET /projects/:id/merge_request/:merge_request_id/comments
#
get ":id/merge_request/:merge_request_id/comments" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present paginate(merge_request.notes), with: Entities::MRNote
end
# Post comment to merge request # Post comment to merge request
# #
# Parameters: # Parameters:
......
...@@ -215,6 +215,17 @@ module API ...@@ -215,6 +215,17 @@ module API
@users = paginate @users @users = paginate @users
present @users, with: Entities::User present @users, with: Entities::User
end end
# Get a project labels
#
# Parameters:
# id (required) - The ID of a project
# Example Request:
# GET /projects/:id/labels
get ':id/labels' do
@labels = user_project.issues_labels
present @labels, with: Entities::Label
end
end end
end end
end end
require_relative 'shell_env' require_relative 'shell_env'
require_relative 'grack_helpers'
module Grack module Grack
class Auth < Rack::Auth::Basic class Auth < Rack::Auth::Basic
include Helpers
attr_accessor :user, :project, :ref, :env attr_accessor :user, :project, :env
def call(env) def call(env)
@env = env @env = env
...@@ -24,14 +22,16 @@ module Grack ...@@ -24,14 +22,16 @@ module Grack
@env['SCRIPT_NAME'] = "" @env['SCRIPT_NAME'] = ""
auth! if project
auth!
else
render_not_found
end
end end
private private
def auth! def auth!
return render_not_found unless project
if @auth.provided? if @auth.provided?
return bad_request unless @auth.basic? return bad_request unless @auth.basic?
...@@ -40,12 +40,8 @@ module Grack ...@@ -40,12 +40,8 @@ module Grack
# Allow authentication for GitLab CI service # Allow authentication for GitLab CI service
# if valid token passed # if valid token passed
if login == "gitlab-ci-token" && project.gitlab_ci? if gitlab_ci_request?(login, password)
token = project.gitlab_ci_service.token return @app.call(env)
if token.present? && token == password && service_name == 'git-upload-pack'
return @app.call(env)
end
end end
@user = authenticate_user(login, password) @user = authenticate_user(login, password)
...@@ -53,23 +49,26 @@ module Grack ...@@ -53,23 +49,26 @@ module Grack
if @user if @user
Gitlab::ShellEnv.set_env(@user) Gitlab::ShellEnv.set_env(@user)
@env['REMOTE_USER'] = @auth.username @env['REMOTE_USER'] = @auth.username
else
return unauthorized
end end
else
return unauthorized unless project.public?
end end
if authorized_git_request? if authorized_request?
@app.call(env) @app.call(env)
else else
unauthorized unauthorized
end end
end end
def authorized_git_request? def gitlab_ci_request?(login, password)
authorize_request(service_name) if login == "gitlab-ci-token" && project.gitlab_ci?
token = project.gitlab_ci_service.token
if token.present? && token == password && git_cmd == 'git-upload-pack'
return true
end
end
false
end end
def authenticate_user(login, password) def authenticate_user(login, password)
...@@ -77,31 +76,31 @@ module Grack ...@@ -77,31 +76,31 @@ module Grack
auth.find(login, password) auth.find(login, password)
end end
def authorize_request(service) def authorized_request?
case service case git_cmd
when 'git-upload-pack' when *Gitlab::GitAccess::DOWNLOAD_COMMANDS
can?(user, :download_code, project) if user
when'git-receive-pack' Gitlab::GitAccess.new.download_allowed?(user, project)
refs.each do |ref| elsif project.public?
action = if project.protected_branch?(ref) # Allow clone/fetch for public projects
:push_code_to_protected_branches true
else else
:push_code false
end end
when *Gitlab::GitAccess::PUSH_COMMANDS
return false unless can?(user, action, project) if user
# Skip user authorization on upload request.
# It will be serverd by update hook in repository
true
else
false
end end
# Never let git-receive-pack trough unauthenticated; it's
# harmless but git < 1.8 doesn't like it
return false if user.nil?
true
else else
false false
end end
end end
def service_name def git_cmd
if @request.get? if @request.get?
@request.params['service'] @request.params['service']
elsif @request.post? elsif @request.post?
...@@ -115,28 +114,17 @@ module Grack ...@@ -115,28 +114,17 @@ module Grack
@project ||= project_by_path(@request.path_info) @project ||= project_by_path(@request.path_info)
end end
def refs def project_by_path(path)
@refs ||= parse_refs if m = /^([\w\.\/-]+)\.git/.match(path).to_a
end path_with_namespace = m.last
path_with_namespace.gsub!(/\.wiki$/, '')
def parse_refs
input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
Zlib::GzipReader.new(@request.body).read
else
@request.body.read
end
# Need to reset seek point
@request.body.rewind
# Parse refs
refs = input.force_encoding('ascii-8bit').scan(/refs\/heads\/([\/\w\.-]+)/n).flatten.compact
# Cleanup grabare from refs Project.find_with_namespace(path_with_namespace)
# if push to multiple branches
refs.map do |ref|
ref.gsub(/00.*/, "")
end end
end end
def render_not_found
[404, {"Content-Type" => "text/plain"}, ["Not Found"]]
end
end end
end end
module Grack
module Helpers
def project_by_path(path)
if m = /^([\w\.\/-]+)\.git/.match(path).to_a
path_with_namespace = m.last
path_with_namespace.gsub!(/\.wiki$/, '')
Project.find_with_namespace(path_with_namespace)
end
end
def render_not_found
[404, {"Content-Type" => "text/plain"}, ["Not Found"]]
end
def can?(object, action, subject)
abilities.allowed?(object, action, subject)
end
def abilities
@abilities ||= begin
abilities = Six.new
abilities << Ability
abilities
end
end
end
end
module Gitlab
class GitAccess
DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }
PUSH_COMMANDS = %w{ git-receive-pack }
attr_reader :params, :project, :git_cmd, :user
def allowed?(actor, cmd, project, ref = nil, oldrev = nil, newrev = nil)
case cmd
when *DOWNLOAD_COMMANDS
if actor.is_a? User
download_allowed?(actor, project)
elsif actor.is_a? DeployKey
actor.projects.include?(project)
elsif actor.is_a? Key
download_allowed?(actor.user, project)
else
raise 'Wrong actor'
end
when *PUSH_COMMANDS
if actor.is_a? User
push_allowed?(actor, project, ref, oldrev, newrev)
elsif actor.is_a? DeployKey
# Deploy key not allowed to push
return false
elsif actor.is_a? Key
push_allowed?(actor.user, project, ref, oldrev, newrev)
else
raise 'Wrong actor'
end
else
false
end
end
def download_allowed?(user, project)
if user && user_allowed?(user)
user.can?(:download_code, project)
else
false
end
end
def push_allowed?(user, project, ref, oldrev, newrev)
if user && user_allowed?(user)
action = if project.protected_branch?(ref)
:push_code_to_protected_branches
else
:push_code
end
user.can?(action, project)
else
false
end
end
private
def user_allowed?(user)
return false if user.blocked?
if Gitlab.config.ldap.enabled
if user.ldap_user?
# Check if LDAP user exists and match LDAP user_filter
unless Gitlab::LDAP::Access.new.allowed?(user)
return false
end
end
end
true
end
end
end
...@@ -152,7 +152,7 @@ module Gitlab ...@@ -152,7 +152,7 @@ module Gitlab
# #
# Returns boolean # Returns boolean
def valid_emoji?(emoji) def valid_emoji?(emoji)
Emoji.names.include? emoji Emoji.find_by_name emoji
end end
# Private: Dispatches to a dedicated processing method based on reference # Private: Dispatches to a dedicated processing method based on reference
...@@ -166,8 +166,8 @@ module Gitlab ...@@ -166,8 +166,8 @@ module Gitlab
end end
def reference_user(identifier) def reference_user(identifier)
if member = @project.team_members.find { |user| user.username == identifier } if user = User.find_by_username(identifier)
link_to("@#{identifier}", user_url(identifier), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member link_to("@#{identifier}", user_url(identifier), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}"))
end end
end end
......
module Gitlab module Gitlab
class SatelliteNotExistError < StandardError; end class SatelliteNotExistError < StandardError
def initialize(msg = "Satellite doesn't exist")
super
end
end
module Satellite module Satellite
class Satellite class Satellite
...@@ -17,14 +21,9 @@ module Gitlab ...@@ -17,14 +21,9 @@ module Gitlab
Gitlab::Satellite::Logger.error(message) Gitlab::Satellite::Logger.error(message)
end end
def raise_no_satellite
raise SatelliteNotExistError.new("Satellite doesn't exist")
end
def clear_and_update! def clear_and_update!
raise_no_satellite unless exists? raise SatelliteNotExistError unless exists?
File.exists? path
@repo = nil @repo = nil
clear_working_dir! clear_working_dir!
delete_heads! delete_heads!
...@@ -55,7 +54,7 @@ module Gitlab ...@@ -55,7 +54,7 @@ module Gitlab
# * Changes the current directory to the satellite's working dir # * Changes the current directory to the satellite's working dir
# * Yields # * Yields
def lock def lock
raise_no_satellite unless exists? raise SatelliteNotExistError unless exists?
File.open(lock_file, "w+") do |f| File.open(lock_file, "w+") do |f|
begin begin
...@@ -77,7 +76,7 @@ module Gitlab ...@@ -77,7 +76,7 @@ module Gitlab
end end
def repo def repo
raise_no_satellite unless exists? raise SatelliteNotExistError unless exists?
@repo ||= Grit::Repo.new(path) @repo ||= Grit::Repo.new(path)
end end
......
require_relative "popen"
require_relative "version_info" require_relative "version_info"
module Gitlab module Gitlab
......
...@@ -40,7 +40,7 @@ test -f /etc/default/gitlab && . /etc/default/gitlab ...@@ -40,7 +40,7 @@ test -f /etc/default/gitlab && . /etc/default/gitlab
# Switch to the app_user if it is not he/she who is running the script. # Switch to the app_user if it is not he/she who is running the script.
if [ "$USER" != "$app_user" ]; then if [ "$USER" != "$app_user" ]; then
sudo -u "$app_user" -H -i $0 "$@"; exit; eval su - "$app_user" -c $(echo \")$0 "$@"$(echo \"); exit;
fi fi
# Switch to the gitlab path, exit on failure. # Switch to the gitlab path, exit on failure.
...@@ -131,7 +131,7 @@ check_stale_pids(){ ...@@ -131,7 +131,7 @@ check_stale_pids(){
fi fi
fi fi
if [ "$spid" != "0" -a "$sidekiq_status" != "0" ]; then if [ "$spid" != "0" -a "$sidekiq_status" != "0" ]; then
echo "Removing stale Sidekiq web server pid. This is most likely caused by the Sidekiq crashing the last time it ran." echo "Removing stale Sidekiq job dispatcher pid. This is most likely caused by Sidekiq crashing the last time it ran."
if ! rm "$sidekiq_pid_path"; then if ! rm "$sidekiq_pid_path"; then
echo "Unable to remove stale pid, exiting" echo "Unable to remove stale pid, exiting"
exit 1 exit 1
...@@ -149,15 +149,15 @@ exit_if_not_running(){ ...@@ -149,15 +149,15 @@ exit_if_not_running(){
} }
## Starts Unicorn and Sidekiq if they're not running. ## Starts Unicorn and Sidekiq if they're not running.
start() { start_gitlab() {
check_stale_pids check_stale_pids
if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then
echo -n "Starting both the GitLab Unicorn and Sidekiq" echo -n "Starting both the GitLab Unicorn and Sidekiq"
elif [ "$web_status" != "0" ]; then elif [ "$web_status" != "0" ]; then
echo -n "Starting GitLab Sidekiq"
elif [ "$sidekiq_status" != "0" ]; then
echo -n "Starting GitLab Unicorn" echo -n "Starting GitLab Unicorn"
elif [ "$sidekiq_status" != "0" ]; then
echo -n "Starting GitLab Sidekiq"
fi fi
# Then check if the service is running. If it is: don't start again. # Then check if the service is running. If it is: don't start again.
...@@ -167,7 +167,7 @@ start() { ...@@ -167,7 +167,7 @@ start() {
# Remove old socket if it exists # Remove old socket if it exists
rm -f "$socket_path"/gitlab.socket 2>/dev/null rm -f "$socket_path"/gitlab.socket 2>/dev/null
# Start the web server # Start the web server
RAILS_ENV=$RAILS_ENV script/web start & RAILS_ENV=$RAILS_ENV script/web start
fi fi
# If sidekiq is already running, don't start it again. # If sidekiq is already running, don't start it again.
...@@ -184,15 +184,15 @@ start() { ...@@ -184,15 +184,15 @@ start() {
} }
## Asks the Unicorn and the Sidekiq if they would be so kind as to stop, if not kills them. ## Asks the Unicorn and the Sidekiq if they would be so kind as to stop, if not kills them.
stop() { stop_gitlab() {
exit_if_not_running exit_if_not_running
if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then
echo -n "Shutting down both Unicorn and Sidekiq" echo -n "Shutting down both Unicorn and Sidekiq"
elif [ "$web_status" = "0" ]; then elif [ "$web_status" = "0" ]; then
echo -n "Shutting down Sidekiq"
elif [ "$sidekiq_status" = "0" ]; then
echo -n "Shutting down Unicorn" echo -n "Shutting down Unicorn"
elif [ "$sidekiq_status" = "0" ]; then
echo -n "Shutting down Sidekiq"
fi fi
# If the Unicorn web server is running, tell it to stop; # If the Unicorn web server is running, tell it to stop;
...@@ -246,7 +246,7 @@ print_status() { ...@@ -246,7 +246,7 @@ print_status() {
} }
## Tells unicorn to reload it's config and Sidekiq to restart ## Tells unicorn to reload it's config and Sidekiq to restart
reload(){ reload_gitlab(){
exit_if_not_running exit_if_not_running
if [ "$wpid" = "0" ];then if [ "$wpid" = "0" ];then
echo "The GitLab Unicorn Web server is not running thus its configuration can't be reloaded." echo "The GitLab Unicorn Web server is not running thus its configuration can't be reloaded."
...@@ -263,12 +263,12 @@ reload(){ ...@@ -263,12 +263,12 @@ reload(){
} }
## Restarts Sidekiq and Unicorn. ## Restarts Sidekiq and Unicorn.
restart(){ restart_gitlab(){
check_status check_status
if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then
stop stop_gitlab
fi fi
start start_gitlab
} }
...@@ -276,16 +276,16 @@ restart(){ ...@@ -276,16 +276,16 @@ restart(){
case "$1" in case "$1" in
start) start)
start start_gitlab
;; ;;
stop) stop)
stop stop_gitlab
;; ;;
restart) restart)
restart restart_gitlab
;; ;;
reload|force-reload) reload|force-reload)
reload reload_gitlab
;; ;;
status) status)
print_status print_status
......
...@@ -54,6 +54,14 @@ server { ...@@ -54,6 +54,14 @@ server {
proxy_pass http://gitlab; proxy_pass http://gitlab;
} }
# Enable gzip compression as per rails guide: http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
location ~ ^/(assets)/ {
root /home/git/gitlab/public;
gzip_static on; # to serve pre-gzipped version
expires max;
add_header Cache-Control public;
}
error_page 502 /502.html; error_page 502 /502.html;
} }
...@@ -677,7 +677,20 @@ namespace :gitlab do ...@@ -677,7 +677,20 @@ namespace :gitlab do
end end
def filter def filter
Net::LDAP::Filter.present?(ldap_config.uid) uid_filter = Net::LDAP::Filter.present?(ldap_config.uid)
if user_filter
Net::LDAP::Filter.join(uid_filter, user_filter)
else
uid_filter
end
end
def user_filter
if ldap_config['user_filter'] && ldap_config.user_filter.present?
Net::LDAP::Filter.construct(ldap_config.user_filter)
else
nil
end
end end
def ldap def ldap
...@@ -742,7 +755,7 @@ namespace :gitlab do ...@@ -742,7 +755,7 @@ namespace :gitlab do
end end
def check_gitlab_shell def check_gitlab_shell
required_version = Gitlab::VersionInfo.new(1, 8, 5) required_version = Gitlab::VersionInfo.new(1, 9, 1)
current_version = Gitlab::VersionInfo.parse(gitlab_shell_version) current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
print "GitLab Shell version >= #{required_version} ? ... " print "GitLab Shell version >= #{required_version} ? ... "
......
...@@ -6,6 +6,11 @@ sidekiq_pidfile="$app_root/tmp/pids/sidekiq.pid" ...@@ -6,6 +6,11 @@ sidekiq_pidfile="$app_root/tmp/pids/sidekiq.pid"
sidekiq_logfile="$app_root/log/sidekiq.log" sidekiq_logfile="$app_root/log/sidekiq.log"
gitlab_user=$(ls -l config.ru | awk '{print $3}') gitlab_user=$(ls -l config.ru | awk '{print $3}')
function warn
{
echo "$@" 1>&2
}
function stop function stop
{ {
bundle exec sidekiqctl stop $sidekiq_pidfile >> $sidekiq_logfile 2>&1 bundle exec sidekiqctl stop $sidekiq_pidfile >> $sidekiq_logfile 2>&1
...@@ -35,6 +40,22 @@ function start_sidekiq ...@@ -35,6 +40,22 @@ function start_sidekiq
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 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
} }
function load_ok
{
sidekiq_pid=$(cat $sidekiq_pidfile)
if [[ -z $sidekiq_pid ]] ; then
warn "Could not find a PID in $sidekiq_pidfile"
exit 0
fi
if (ps -p $sidekiq_pid -o args | grep '\([0-9]\+\) of \1 busy' 1>&2) ; then
warn "Too many busy Sidekiq workers"
exit 1
fi
exit 0
}
case "$1" in case "$1" in
stop) stop)
stop stop
...@@ -51,6 +72,9 @@ case "$1" in ...@@ -51,6 +72,9 @@ case "$1" in
killall) killall)
killall killall
;; ;;
load_ok)
load_ok
;;
*) *)
echo "Usage: RAILS_ENV=your_env $0 {stop|start|start_no_deamonize|restart|killall}" echo "Usage: RAILS_ENV=your_env $0 {stop|start|start_no_deamonize|restart|killall|load_ok}"
esac esac
...@@ -24,6 +24,11 @@ describe Profiles::KeysController do ...@@ -24,6 +24,11 @@ describe Profiles::KeysController do
expect(response.body).to eq("") expect(response.body).to eq("")
end end
it "should respond with text/plain content type" do
get :get_keys, username: user.username
expect(response.content_type).to eq("text/plain")
end
end end
describe "user with keys" do describe "user with keys" do
...@@ -44,6 +49,11 @@ describe Profiles::KeysController do ...@@ -44,6 +49,11 @@ describe Profiles::KeysController do
expect(response.body).not_to eq("") expect(response.body).not_to eq("")
expect(response.body).to eq(user.all_ssh_keys.join("\n")) expect(response.body).to eq(user.all_ssh_keys.join("\n"))
end end
it "should respond with text/plain content type" do
get :get_keys, username: user.username
expect(response.content_type).to eq("text/plain")
end
end end
end end
end end
...@@ -32,6 +32,18 @@ FactoryGirl.define do ...@@ -32,6 +32,18 @@ FactoryGirl.define do
path { name.downcase.gsub(/\s/, '_') } path { name.downcase.gsub(/\s/, '_') }
namespace namespace
creator creator
trait :public do
visibility_level Gitlab::VisibilityLevel::PUBLIC
end
trait :internal do
visibility_level Gitlab::VisibilityLevel::INTERNAL
end
trait :private do
visibility_level Gitlab::VisibilityLevel::PRIVATE
end
end end
# Generates a test repository from the repository stored under `spec/seed_project.tar.gz`. # Generates a test repository from the repository stored under `spec/seed_project.tar.gz`.
...@@ -146,6 +158,11 @@ FactoryGirl.define do ...@@ -146,6 +158,11 @@ FactoryGirl.define do
state :reopened state :reopened
end end
trait :simple do
source_branch "simple_merge_request"
target_branch "master"
end
factory :closed_merge_request, traits: [:closed] factory :closed_merge_request, traits: [:closed]
factory :reopened_merge_request, traits: [:reopened] factory :reopened_merge_request, traits: [:reopened]
factory :merge_request_with_diffs, traits: [:with_diffs] factory :merge_request_with_diffs, traits: [:with_diffs]
...@@ -161,7 +178,6 @@ FactoryGirl.define do ...@@ -161,7 +178,6 @@ FactoryGirl.define do
factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note] factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note]
factory :note_on_merge_request, traits: [:on_merge_request] factory :note_on_merge_request, traits: [:on_merge_request]
factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff] factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff]
factory :note_on_merge_request_with_attachment, traits: [:on_merge_request, :with_attachment]
trait :on_commit do trait :on_commit do
project factory: :project project factory: :project
......
require 'spec_helper' require 'spec_helper'
describe "On a merge request", js: true do describe "On a merge request", js: true do
let!(:project) { create(:project) } let!(:merge_request) { create(:merge_request, :simple) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let!(:project) { merge_request.source_project }
let!(:note) { create(:note_on_merge_request_with_attachment, project: project) } let!(:note) { create(:note_on_merge_request, :with_attachment, project: project) }
before do before do
login_as :user login_as :admin
project.team << [@user, :master]
visit project_merge_request_path(project, merge_request) visit project_merge_request_path(project, merge_request)
end end
...@@ -134,22 +132,20 @@ describe "On a merge request", js: true do ...@@ -134,22 +132,20 @@ describe "On a merge request", js: true do
end end
end end
describe "On a merge request diff", js: true, focus: true do describe "On a merge request diff", js: true do
let!(:project) { create(:project) } let(:merge_request) { create(:merge_request, :with_diffs, :simple) }
let!(:merge_request) { create(:merge_request_with_diffs, source_project: project, target_project: project) } let(:project) { merge_request.source_project }
before do before do
login_as :user login_as :admin
project.team << [@user, :master]
visit diffs_project_merge_request_path(project, merge_request) visit diffs_project_merge_request_path(project, merge_request)
end end
subject { page } subject { page }
describe "when adding a note" do describe "when adding a note" do
before do before do
find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185"]').click find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7"]').click
end end
describe "the notes holder" do describe "the notes holder" do
...@@ -160,13 +156,13 @@ describe "On a merge request diff", js: true, focus: true do ...@@ -160,13 +156,13 @@ describe "On a merge request diff", js: true, focus: true do
describe "the note form" do describe "the note form" do
it "shouldn't add a second form for same row" do it "shouldn't add a second form for same row" do
find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185"]').click find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7"]').click
should have_css("tr[id='4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185'] + .js-temp-notes-holder form", count: 1) should have_css("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7'] + .js-temp-notes-holder form", count: 1)
end end
it "should be removed when canceled" do it "should be removed when canceled" do
within(".diff-file form[rel$='4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185']") do within(".diff-file form[rel$='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7']") do
find(".js-close-discussion-note-form").trigger("click") find(".js-close-discussion-note-form").trigger("click")
end end
...@@ -176,12 +172,9 @@ describe "On a merge request diff", js: true, focus: true do ...@@ -176,12 +172,9 @@ describe "On a merge request diff", js: true, focus: true do
end end
describe "with muliple note forms" do describe "with muliple note forms" do
let!(:project) { create(:project) }
let!(:merge_request) { create(:merge_request_with_diffs, source_project: project, target_project: project) }
before do before do
find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185"]').click find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7"]').click
find('a[data-line-code="342e16cbbd482ac2047dc679b2749d248cc1428f_18_17"]').click find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_10_10"]').click
end end
it { should have_css(".js-temp-notes-holder", count: 2) } 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,12 +182,12 @@ describe "On a merge request diff", js: true, focus: true do
describe "previewing them separately" do describe "previewing them separately" do
before do before do
# add two separate texts and trigger previews on both # add two separate texts and trigger previews on both
within("tr[id='4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185'] + .js-temp-notes-holder") do within("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7'] + .js-temp-notes-holder") do
fill_in "note[note]", with: "One comment on line 185" fill_in "note[note]", with: "One comment on line 7"
find(".js-note-preview-button").trigger("click") find(".js-note-preview-button").trigger("click")
end end
within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do within("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_10_10'] + .js-temp-notes-holder") do
fill_in "note[note]", with: "Another comment on line 17" fill_in "note[note]", with: "Another comment on line 10"
find(".js-note-preview-button").trigger("click") find(".js-note-preview-button").trigger("click")
end end
end end
...@@ -202,14 +195,14 @@ describe "On a merge request diff", js: true, focus: true do ...@@ -202,14 +195,14 @@ describe "On a merge request diff", js: true, focus: true do
describe "posting a note" do describe "posting a note" do
before do before do
within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do within("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_10_10'] + .js-temp-notes-holder") do
fill_in "note[note]", with: "Another comment on line 17" fill_in "note[note]", with: "Another comment on line 10"
click_button("Add Comment") click_button("Add Comment")
end end
end end
it 'should be added as discussion' do it 'should be added as discussion' do
should have_content("Another comment on line 17") should have_content("Another comment on line 10")
should have_css(".notes_holder") should have_css(".notes_holder")
should have_css(".notes_holder .note", count: 1) should have_css(".notes_holder .note", count: 1)
should have_link("Reply") should have_link("Reply")
......
...@@ -16,7 +16,7 @@ describe "Group with internal project access" do ...@@ -16,7 +16,7 @@ describe "Group with internal project access" do
group.add_user(reporter, Gitlab::Access::REPORTER) group.add_user(reporter, Gitlab::Access::REPORTER)
group.add_user(guest, Gitlab::Access::GUEST) group.add_user(guest, Gitlab::Access::GUEST)
create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) create(:project, :internal, group: group)
end end
describe "GET /groups/:path" do describe "GET /groups/:path" do
......
...@@ -16,8 +16,8 @@ describe "Group access" do ...@@ -16,8 +16,8 @@ describe "Group access" do
group.add_user(reporter, Gitlab::Access::REPORTER) group.add_user(reporter, Gitlab::Access::REPORTER)
group.add_user(guest, Gitlab::Access::GUEST) group.add_user(guest, Gitlab::Access::GUEST)
create(:project, path: "internal_project", group: group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) create(:project, :internal, path: "internal_project", group: group)
create(:project, path: "public_project", group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) create(:project, :public, path: "public_project", group: group)
end end
describe "GET /groups/:path" do describe "GET /groups/:path" do
......
...@@ -16,7 +16,7 @@ describe "Group with public project access" do ...@@ -16,7 +16,7 @@ describe "Group with public project access" do
group.add_user(reporter, Gitlab::Access::REPORTER) group.add_user(reporter, Gitlab::Access::REPORTER)
group.add_user(guest, Gitlab::Access::GUEST) group.add_user(guest, Gitlab::Access::GUEST)
create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) create(:project, :public, group: group)
end end
describe "GET /groups/:path" do describe "GET /groups/:path" do
......
require 'spec_helper' require 'spec_helper'
describe "Internal Project Access" do describe "Internal Project Access" do
let(:project) { create(:project) } let(:project) { create(:project, :internal) }
let(:master) { create(:user) } let(:master) { create(:user) }
let(:guest) { create(:user) } let(:guest) { create(:user) }
let(:reporter) { create(:user) } let(:reporter) { create(:user) }
before do before do
# internal project
project.visibility_level = Gitlab::VisibilityLevel::INTERNAL
project.save!
# full access # full access
project.team << [master, :master] project.team << [master, :master]
# readonly # readonly
project.team << [reporter, :reporter] project.team << [reporter, :reporter]
end end
describe "Project should be internal" do describe "Project should be internal" do
......
require 'spec_helper' require 'spec_helper'
describe MergeRequestsFinder do describe MergeRequestsFinder do
let(:user) { create :user } let(:user) { create :user }
let(:user2) { create :user } let(:user2) { create :user }
let(:project1) { create(:project) } let(:project1) { create(:project) }
let(:project2) { create(:project) } let(:project2) { create(:project) }
let(:merge_request1) { create(:merge_request, author: user, source_project: project1, target_project: project2) }
let(:merge_request2) { create(:merge_request, author: user, source_project: project2, target_project: project1) } let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project2) }
let(:merge_request3) { create(:merge_request, author: user, source_project: project2, target_project: project2) } let!(:merge_request2) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) }
let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2) }
before do before do
project1.team << [user, :master] project1.team << [user, :master]
...@@ -15,13 +17,7 @@ describe MergeRequestsFinder do ...@@ -15,13 +17,7 @@ describe MergeRequestsFinder do
project2.team << [user2, :developer] project2.team << [user2, :developer]
end end
describe :execute do describe "#execute" do
before :each do
merge_request1
merge_request2
merge_request3
end
it 'should filter by scope' do it 'should filter by scope' do
params = { scope: 'authored', state: 'opened' } params = { scope: 'authored', state: 'opened' }
merge_requests = MergeRequestsFinder.new.execute(user, params) merge_requests = MergeRequestsFinder.new.execute(user, params)
......
...@@ -4,10 +4,10 @@ describe ProjectsFinder do ...@@ -4,10 +4,10 @@ describe ProjectsFinder do
let(:user) { create :user } let(:user) { create :user }
let(:group) { create :group } let(:group) { create :group }
let(:project1) { create(:empty_project, group: group, visibility_level: Project::PUBLIC) } let(:project1) { create(:empty_project, :public, group: group) }
let(:project2) { create(:empty_project, group: group, visibility_level: Project::INTERNAL) } let(:project2) { create(:empty_project, :internal, group: group) }
let(:project3) { create(:empty_project, group: group, visibility_level: Project::PRIVATE) } let(:project3) { create(:empty_project, :private, group: group) }
let(:project4) { create(:empty_project, group: group, visibility_level: Project::PRIVATE) } let(:project4) { create(:empty_project, :private, group: group) }
context 'non authenticated' do context 'non authenticated' do
subject { ProjectsFinder.new.execute(nil, group: group) } subject { ProjectsFinder.new.execute(nil, group: group) }
......
...@@ -229,6 +229,7 @@ describe Notify do ...@@ -229,6 +229,7 @@ describe Notify do
end end
context 'for merge requests' do context 'for merge requests' do
let(:merge_author) { create(:user) }
let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) } let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) }
let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: Faker::Lorem.sentence) } let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: Faker::Lorem.sentence) }
...@@ -288,7 +289,30 @@ describe Notify do ...@@ -288,7 +289,30 @@ describe Notify do
it 'contains a link to the merge request' do it 'contains a link to the merge request' do
should have_body_text /#{project_merge_request_path project, merge_request}/ should have_body_text /#{project_merge_request_path project, merge_request}/
end end
end
describe 'that are merged' do
subject { Notify.merged_merge_request_email(recipient.id, merge_request.id, merge_author.id) }
it_behaves_like 'a multiple recipients email'
it 'is sent as the merge author' do
sender = subject.header[:from].addrs[0]
sender.display_name.should eq(merge_author.name)
sender.address.should eq(gitlab_sender)
end
it 'has the correct subject' do
should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/
end
it 'contains the new status' do
should have_body_text /merged/i
end
it 'contains a link to the merge request' do
should have_body_text /#{project_merge_request_path project, merge_request}/
end
end end
end end
end end
......
...@@ -25,11 +25,6 @@ describe Issue, "Issuable" do ...@@ -25,11 +25,6 @@ describe Issue, "Issuable" do
it { described_class.should respond_to(:assigned) } it { described_class.should respond_to(:assigned) }
end end
it "has an :author_id_of_changes accessor" do
issue.should respond_to(:author_id_of_changes)
issue.should respond_to(:author_id_of_changes=)
end
describe ".search" do describe ".search" do
let!(:searchable_issue) { create(:issue, title: "Searchable issue") } let!(:searchable_issue) { create(:issue, title: "Searchable issue") }
......
...@@ -47,6 +47,7 @@ describe Project do ...@@ -47,6 +47,7 @@ describe Project do
it { should have_many(:hooks).dependent(:destroy) } it { should have_many(:hooks).dependent(:destroy) }
it { should have_many(:protected_branches).dependent(:destroy) } it { should have_many(:protected_branches).dependent(:destroy) }
it { should have_one(:forked_project_link).dependent(:destroy) } it { should have_one(:forked_project_link).dependent(:destroy) }
it { should have_one(:slack_service).dependent(:destroy) }
end end
describe "Mass assignment" do describe "Mass assignment" do
......
require_relative '../../app/models/project_services/slack_message'
describe SlackMessage do
subject { SlackMessage.new(args) }
let(:args) {
{
after: 'after',
before: 'before',
project_name: 'project_name',
ref: 'refs/heads/master',
user_name: 'user_name',
project_url: 'url'
}
}
context 'push' do
before do
args[:commits] = [
{ message: 'message1', url: 'url1', id: 'abcdefghi' },
{ message: 'message2', url: 'url2', id: '123456789' },
]
end
it 'returns a message regarding pushes' do
subject.compose.should ==
'user_name pushed to branch <url/commits/master|master> of ' <<
'<url|project_name> (<url/compare/before...after|Compare changes>)' <<
"\n - message1 (<url1|abcdef>)" <<
"\n - message2 (<url2|123456>)"
end
end
context 'new branch' do
before do
args[:before] = '000000'
end
it 'returns a message regarding a new branch' do
subject.compose.should ==
'user_name pushed new branch <url/commits/master|master> to ' <<
'<url|project_name>'
end
end
context 'removed branch' do
before do
args[:after] = '000000'
end
it 'returns a message regarding a removed branch' do
subject.compose.should ==
'user_name removed branch master from <url|project_name>'
end
end
end
# == Schema Information
#
# Table name: services
#
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# token :string(255)
# project_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# active :boolean default(FALSE), not null
# project_url :string(255)
# subdomain :string(255)
# room :string(255)
# api_key :string(255)
#
require 'spec_helper'
describe SlackService do
describe "Associations" do
it { should belong_to :project }
it { should have_one :service_hook }
end
describe "Validations" do
context "active" do
before do
subject.active = true
end
it { should validate_presence_of :room }
it { should validate_presence_of :subdomain }
it { should validate_presence_of :token }
end
end
describe "Execute" do
let(:slack) { SlackService.new }
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:sample_data) { GitPushService.new.sample_data(project, user) }
let(:subdomain) { 'gitlab' }
let(:token) { 'verySecret' }
let(:api_url) {
"https://#{subdomain}.slack.com/services/hooks/incoming-webhook?token=#{token}"
}
before do
slack.stub(
project: project,
project_id: project.id,
room: '#gitlab',
service_hook: true,
subdomain: subdomain,
token: token
)
WebMock.stub_request(:post, api_url)
end
it "should call Slack API" do
slack.execute(sample_data)
WebMock.should have_requested(:post, api_url).once
end
end
end
...@@ -292,6 +292,20 @@ describe User do ...@@ -292,6 +292,20 @@ describe User do
end end
end end
describe 'search' do
let(:user1) { create(:user, username: 'James', email: 'james@testing.com') }
let(:user2) { create(:user, username: 'jameson', email: 'jameson@example.com') }
it "should be case insensitive" do
User.search(user1.username.upcase).to_a.should == [user1]
User.search(user1.username.downcase).to_a.should == [user1]
User.search(user2.username.upcase).to_a.should == [user2]
User.search(user2.username.downcase).to_a.should == [user2]
User.search(user1.username.downcase).to_a.count.should == 2
User.search(user2.username.downcase).to_a.count.should == 1
end
end
describe 'by_username_or_id' do describe 'by_username_or_id' do
let(:user1) { create(:user, username: 'foo') } let(:user1) { create(:user, username: 'foo') }
......
require 'spec_helper'
describe ActivityObserver do
let(:project) { create(:project) }
before { Thread.current[:current_user] = create(:user) }
def self.it_should_be_valid_event
it { @event.should_not be_nil }
it { @event.project.should == project }
end
describe "Issue created" do
before do
Issue.observers.enable :activity_observer do
@issue = create(:issue, project: project)
@event = Event.last
end
end
it_should_be_valid_event
it { @event.action.should == Event::CREATED }
it { @event.target.should == @issue }
end
describe "Issue commented" do
before do
Note.observers.enable :activity_observer do
@issue = create(:issue, project: project)
@note = create(:note, noteable: @issue, project: project, author: @issue.author)
@event = Event.last
end
end
it_should_be_valid_event
it { @event.action.should == Event::COMMENTED }
it { @event.target.should == @note }
end
describe "Ignore system notes" do
let(:author) { create(:user) }
let!(:issue) { create(:issue, project: project) }
let!(:other) { create(:issue) }
it "should not create events for status change notes" do
expect do
Note.observers.enable :activity_observer do
Note.create_status_change_note(issue, project, author, 'reopened', nil)
end
end.to_not change { Event.count }
end
it "should not create events for cross-reference notes" do
expect do
Note.observers.enable :activity_observer do
Note.create_cross_reference_note(issue, other, author, issue.project)
end
end.to_not change { Event.count }
end
end
end
...@@ -120,7 +120,7 @@ describe MergeRequestObserver do ...@@ -120,7 +120,7 @@ describe MergeRequestObserver do
end end
before do before do
@merge_request = create(:merge_request, source_project: project, source_project: project) @merge_request = create(:merge_request, source_project: project, target_project: project)
@event = Event.last @event = Event.last
end end
......
...@@ -100,16 +100,4 @@ describe API::API do ...@@ -100,16 +100,4 @@ describe API::API do
response.status.should == 405 response.status.should == 405
end end
end end
describe "PUT /projects/:id/issues/:issue_id to test observer on close" do
before { enable_observers }
after { disable_observers }
it "should create an activity event when an issue is closed" do
Event.should_receive(:create)
put api("/projects/#{project.id}/issues/#{issue.id}", user),
state_event: "close"
end
end
end end
...@@ -7,6 +7,7 @@ describe API::API do ...@@ -7,6 +7,7 @@ describe API::API do
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) } let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:merge_request) { create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, title: "Test") } let!(:merge_request) { create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, title: "Test") }
let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
before { before {
project.team << [user, :reporters] project.team << [user, :reporters]
} }
...@@ -205,4 +206,20 @@ describe API::API do ...@@ -205,4 +206,20 @@ describe API::API do
response.status.should == 404 response.status.should == 404
end end
end end
describe "GET :id/merge_request/:merge_request_id/comments" do
it "should return merge_request comments" do
get api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 1
json_response.first['note'].should == "a comment on a MR"
json_response.first['author']['id'].should == user.id
end
it "should return a 404 error if merge_request_id not found" do
get api("/projects/#{project.id}/merge_request/999/comments", user)
response.status.should == 404
end
end
end end
...@@ -13,6 +13,7 @@ describe API::API do ...@@ -13,6 +13,7 @@ describe API::API do
let(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') } let(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') }
let(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } let(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
let(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } let(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) }
let(:issue_with_labels) { create(:issue, author: user, assignee: user, project: project, :label_list => "label1, label2") }
describe "GET /projects" do describe "GET /projects" do
before { project } before { project }
...@@ -133,7 +134,7 @@ describe API::API do ...@@ -133,7 +134,7 @@ describe API::API do
end end
it "should set a project as public" do it "should set a project as public" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC }) project = attributes_for(:project, :public)
post api("/projects", user), project post api("/projects", user), project
json_response['public'].should be_true json_response['public'].should be_true
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
...@@ -147,21 +148,21 @@ describe API::API do ...@@ -147,21 +148,21 @@ describe API::API do
end end
it "should set a project as internal" do it "should set a project as internal" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL }) project = attributes_for(:project, :internal)
post api("/projects", user), project post api("/projects", user), project
json_response['public'].should be_false json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end end
it "should set a project as internal overriding :public" do it "should set a project as internal overriding :public" do
project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL }) project = attributes_for(:project, :internal, { public: true })
post api("/projects", user), project post api("/projects", user), project
json_response['public'].should be_false json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end end
it "should set a project as private" do it "should set a project as private" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) project = attributes_for(:project, :private)
post api("/projects", user), project post api("/projects", user), project
json_response['public'].should be_false json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
...@@ -215,7 +216,7 @@ describe API::API do ...@@ -215,7 +216,7 @@ describe API::API do
end end
it "should set a project as public" do it "should set a project as public" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC }) project = attributes_for(:project, :public)
post api("/projects/user/#{user.id}", admin), project post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_true json_response['public'].should be_true
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
...@@ -229,21 +230,21 @@ describe API::API do ...@@ -229,21 +230,21 @@ describe API::API do
end end
it "should set a project as internal" do it "should set a project as internal" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL }) project = attributes_for(:project, :internal)
post api("/projects/user/#{user.id}", admin), project post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end end
it "should set a project as internal overriding :public" do it "should set a project as internal overriding :public" do
project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL }) project = attributes_for(:project, :internal, { public: true })
post api("/projects/user/#{user.id}", admin), project post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end end
it "should set a project as private" do it "should set a project as private" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) project = attributes_for(:project, :private)
post api("/projects/user/#{user.id}", admin), project post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
...@@ -490,10 +491,10 @@ describe API::API do ...@@ -490,10 +491,10 @@ describe API::API do
describe :fork_admin do describe :fork_admin do
let(:project_fork_target) { create(:project) } let(:project_fork_target) { create(:project) }
let(:project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } let(:project_fork_source) { create(:project, :public) }
describe "POST /projects/:id/fork/:forked_from_id" do describe "POST /projects/:id/fork/:forked_from_id" do
let(:new_project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } let(:new_project_fork_source) { create(:project, :public) }
it "shouldn't available for non admin users" do it "shouldn't available for non admin users" do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user) post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
...@@ -562,10 +563,10 @@ describe API::API do ...@@ -562,10 +563,10 @@ describe API::API do
let!(:post) { create(:empty_project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) } let!(:post) { create(:empty_project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
let!(:pre_post) { create(:empty_project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) } let!(:pre_post) { create(:empty_project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) }
let!(:unfound) { create(:empty_project, name: 'unfound', creator_id: user.id, namespace: user.namespace) } let!(:unfound) { create(:empty_project, name: 'unfound', creator_id: user.id, namespace: user.namespace) }
let!(:internal) { create(:empty_project, name: "internal #{query}", visibility_level: Gitlab::VisibilityLevel::INTERNAL) } let!(:internal) { create(:empty_project, :internal, name: "internal #{query}") }
let!(:unfound_internal) { create(:empty_project, name: 'unfound internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL) } let!(:unfound_internal) { create(:empty_project, :internal, name: 'unfound internal') }
let!(:public) { create(:empty_project, name: "public #{query}", visibility_level: Gitlab::VisibilityLevel::PUBLIC) } let!(:public) { create(:empty_project, :public, name: "public #{query}") }
let!(:unfound_public) { create(:empty_project, name: 'unfound public', visibility_level: Gitlab::VisibilityLevel::PUBLIC) } let!(:unfound_public) { create(:empty_project, :public, name: 'unfound public') }
context "when unauthenticated" do context "when unauthenticated" do
it "should return authentication error" do it "should return authentication error" do
...@@ -632,4 +633,16 @@ describe API::API do ...@@ -632,4 +633,16 @@ describe API::API do
end end
end end
end end
describe "GET /projects/:id/labels" do
before { issue_with_labels }
it "should return project labels" do
get api("/projects/#{project.id}/labels", user)
response.status.should == 200
json_response.should be_an Array
json_response.first['name'].should == issue_with_labels.labels.first.name
json_response.last['name'].should == issue_with_labels.labels.last.name
end
end
end end
No preview for this file type
require 'spec_helper'
describe EventCreateService do
let(:service) { EventCreateService.new }
describe 'Issues' do
describe :open_issue do
let(:issue) { create(:issue) }
it { service.open_issue(issue, issue.author).should be_true }
it "should create new event" do
expect { service.open_issue(issue, issue.author) }.to change { Event.count }
end
end
describe :close_issue do
let(:issue) { create(:issue) }
it { service.close_issue(issue, issue.author).should be_true }
it "should create new event" do
expect { service.close_issue(issue, issue.author) }.to change { Event.count }
end
end
describe :reopen_issue do
let(:issue) { create(:issue) }
it { service.reopen_issue(issue, issue.author).should be_true }
it "should create new event" do
expect { service.reopen_issue(issue, issue.author) }.to change { Event.count }
end
end
end
describe 'Merge Requests' do
describe :open_mr do
let(:merge_request) { create(:merge_request) }
it { service.open_mr(merge_request, merge_request.author).should be_true }
it "should create new event" do
expect { service.open_mr(merge_request, merge_request.author) }.to change { Event.count }
end
end
describe :close_mr do
let(:merge_request) { create(:merge_request) }
it { service.close_mr(merge_request, merge_request.author).should be_true }
it "should create new event" do
expect { service.close_mr(merge_request, merge_request.author) }.to change { Event.count }
end
end
describe :merge_mr do
let(:merge_request) { create(:merge_request) }
it { service.merge_mr(merge_request, merge_request.author).should be_true }
it "should create new event" do
expect { service.merge_mr(merge_request, merge_request.author) }.to change { Event.count }
end
end
describe :reopen_mr do
let(:merge_request) { create(:merge_request) }
it { service.reopen_mr(merge_request, merge_request.author).should be_true }
it "should create new event" do
expect { service.reopen_mr(merge_request, merge_request.author) }.to change { Event.count }
end
end
end
describe 'Milestone' do
let(:user) { create :user }
describe :open_milestone do
let(:milestone) { create(:milestone) }
it { service.open_milestone(milestone, user).should be_true }
it "should create new event" do
expect { service.open_milestone(milestone, user) }.to change { Event.count }
end
end
describe :close_mr do
let(:milestone) { create(:milestone) }
it { service.close_milestone(milestone, user).should be_true }
it "should create new event" do
expect { service.close_milestone(milestone, user) }.to change { Event.count }
end
end
end
end
...@@ -32,6 +32,7 @@ describe NotificationService do ...@@ -32,6 +32,7 @@ describe NotificationService do
describe 'Notes' do describe 'Notes' do
context 'issue note' do context 'issue note' do
let(:issue) { create(:issue, assignee: create(:user)) } let(:issue) { create(:issue, assignee: create(:user)) }
let(:mentioned_issue) { create(:issue, assignee: issue.assignee) }
let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced') } let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced') }
before do before do
...@@ -50,6 +51,13 @@ describe NotificationService do ...@@ -50,6 +51,13 @@ describe NotificationService do
notification.new_note(note) notification.new_note(note)
end end
it 'filters out "mentioned in" notes' do
mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author, issue.project)
Notify.should_not_receive(:note_issue_email)
notification.new_note(mentioned_note)
end
def should_email(user_id) def should_email(user_id)
Notify.should_receive(:note_issue_email).with(user_id, note.id) Notify.should_receive(:note_issue_email).with(user_id, note.id)
end end
...@@ -233,15 +241,15 @@ describe NotificationService do ...@@ -233,15 +241,15 @@ describe NotificationService do
should_email(@u_watcher.id) should_email(@u_watcher.id)
should_not_email(@u_participating.id) should_not_email(@u_participating.id)
should_not_email(@u_disabled.id) should_not_email(@u_disabled.id)
notification.merge_mr(merge_request) notification.merge_mr(merge_request, @u_disabled)
end end
def should_email(user_id) def should_email(user_id)
Notify.should_receive(:merged_merge_request_email).with(user_id, merge_request.id) Notify.should_receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
end end
def should_not_email(user_id) def should_not_email(user_id)
Notify.should_not_receive(:merged_merge_request_email).with(user_id, merge_request.id) Notify.should_not_receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
end end
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe 'Search::GlobalService' do describe 'Search::GlobalService' do
let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') }
let(:user) { create(:user, namespace: found_namespace) } let(:user) { create(:user, namespace: found_namespace) }
let!(:found_project) { create(:project, name: 'searchable_project', creator_id: user.id, namespace: found_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } let(:public_user) { create(:user, namespace: public_namespace) }
let(:internal_user) { create(:user, namespace: internal_namespace) }
let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') }
let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') } let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') }
let!(:unfound_project) { create(:project, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } let(:internal_namespace) { create(:namespace, name: 'searchable internal namespace', path: 'something_internal') }
let(:public_namespace) { create(:namespace, name: 'searchable public namespace', path: 'something_public') }
let(:internal_namespace) { create(:namespace, path: 'something_internal',name: 'searchable internal namespace') } let!(:found_project) { create(:project, :private, name: 'searchable_project', creator_id: user.id, namespace: found_namespace) }
let(:internal_user) { create(:user, namespace: internal_namespace) } let!(:unfound_project) { create(:project, :private, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace) }
let!(:internal_project) { create(:project, name: 'searchable_internal_project', creator_id: internal_user.id, namespace: internal_namespace, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } let!(:internal_project) { create(:project, :internal, name: 'searchable_internal_project', creator_id: internal_user.id, namespace: internal_namespace) }
let!(:public_project) { create(:project, :public, name: 'searchable_public_project', creator_id: public_user.id, namespace: public_namespace) }
let(:public_namespace) { create(:namespace, path: 'something_public',name: 'searchable public namespace') }
let(:public_user) { create(:user, namespace: public_namespace) }
let!(:public_project) { create(:project, name: 'searchable_public_project', creator_id: public_user.id, namespace: public_namespace, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
describe '#execute' do describe '#execute' do
context 'unauthenticated' do context 'unauthenticated' do
it 'should return public projects only' do it 'should return public projects only' do
context = Search::GlobalService.new(nil, search: "searchable") context = Search::GlobalService.new(nil, search: "searchable")
results = context.execute results = context.execute
results[:projects].should have(1).items results[:projects].should match_array [public_project]
results[:projects].should include(public_project)
end end
end end
...@@ -30,24 +28,19 @@ describe 'Search::GlobalService' do ...@@ -30,24 +28,19 @@ describe 'Search::GlobalService' do
it 'should return public, internal and private projects' do it 'should return public, internal and private projects' do
context = Search::GlobalService.new(user, search: "searchable") context = Search::GlobalService.new(user, search: "searchable")
results = context.execute results = context.execute
results[:projects].should have(3).items results[:projects].should match_array [public_project, found_project, internal_project]
results[:projects].should include(public_project)
results[:projects].should include(found_project)
results[:projects].should include(internal_project)
end end
it 'should return only public & internal projects' do it 'should return only public & internal projects' do
context = Search::GlobalService.new(internal_user, search: "searchable") context = Search::GlobalService.new(internal_user, search: "searchable")
results = context.execute results = context.execute
results[:projects].should have(2).items results[:projects].should match_array [internal_project, public_project]
results[:projects].should include(internal_project)
results[:projects].should include(public_project)
end end
it 'namespace name should be searchable' do it 'namespace name should be searchable' do
context = Search::GlobalService.new(user, search: "searchable namespace") context = Search::GlobalService.new(user, search: "searchable namespace")
results = context.execute results = context.execute
results[:projects].should == [found_project] results[:projects].should match_array [found_project]
end end
end end
end end
......
require 'rubygems' # This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spork' ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
Spork.prefork do require 'simplecov' unless ENV['CI']
require 'simplecov' unless ENV['CI']
if ENV['TRAVIS'] if ENV['TRAVIS']
require 'coveralls' require 'coveralls'
Coveralls.wear! Coveralls.wear!
end
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'capybara/rails'
require 'capybara/rspec'
require 'webmock/rspec'
require 'email_spec'
require 'sidekiq/testing/inline'
require 'capybara/poltergeist'
# Loading more in this block will cause your tests to run faster. However,
# if you change any configuration or code from libraries loaded here, you'll
# need to restart spork for it take effect.
Capybara.javascript_driver = :poltergeist
Capybara.default_wait_time = 10
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
WebMock.disable_net_connect!(allow_localhost: true)
RSpec.configure do |config|
config.mock_with :rspec
config.include LoginHelpers, type: :feature
config.include LoginHelpers, type: :request
config.include FactoryGirl::Syntax::Methods
config.include Devise::TestHelpers, type: :controller
config.include TestEnv
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
config.before(:suite) do
TestEnv.init(observers: false, init_repos: true, repos: false)
end
config.before(:each) do
TestEnv.setup_stubs
end
end
end end
Spork.each_run do require 'rspec/rails'
# This code will be run each time you run your specs. require 'capybara/rails'
require 'capybara/rspec'
require 'webmock/rspec'
require 'email_spec'
require 'sidekiq/testing/inline'
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
Capybara.default_wait_time = 10
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
WebMock.disable_net_connect!(allow_localhost: true)
RSpec.configure do |config|
config.mock_with :rspec
config.include LoginHelpers, type: :feature
config.include LoginHelpers, type: :request
config.include FactoryGirl::Syntax::Methods
config.include Devise::TestHelpers, type: :controller
config.include TestEnv
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
config.before(:suite) do
TestEnv.init(observers: false, init_repos: true, repos: false)
end
config.before(:each) do
TestEnv.setup_stubs
end
end end
...@@ -29,7 +29,6 @@ module TestEnv ...@@ -29,7 +29,6 @@ module TestEnv
disable_mailer if opts[:mailer] == false disable_mailer if opts[:mailer] == false
setup_stubs setup_stubs
clear_test_repo_dir if opts[:init_repos] == true clear_test_repo_dir if opts[:init_repos] == true
setup_test_repos(opts) if opts[:repos] == true setup_test_repos(opts) if opts[:repos] == true
end end
...@@ -91,7 +90,7 @@ module TestEnv ...@@ -91,7 +90,7 @@ module TestEnv
size: 12.45 size: 12.45
) )
ActivityObserver.any_instance.stub( BaseObserver.any_instance.stub(
current_user: double("current_user", id: 1) current_user: double("current_user", id: 1)
) )
end end
...@@ -165,8 +164,7 @@ module TestEnv ...@@ -165,8 +164,7 @@ module TestEnv
def clear_test_repo_dir def clear_test_repo_dir
setup_stubs setup_stubs
# Use tmp dir for FS manipulations
repos_path = testing_path()
# Remove tmp/test-git-base-path # Remove tmp/test-git-base-path
FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment