diff --git a/.rspec b/.rspec index 7488cbe7792e3f584f6bcc8fc283dc8036f25b50..4e1e0d2f722485c7d284fb5cd7da855826e39b5a 100644 --- a/.rspec +++ b/.rspec @@ -1 +1 @@ ---color --drb +--color diff --git a/.travis.yml b/.travis.yml index d7c65286cfe55d38acb3c213439b48dea0115427..6bff3752b2ac5d526438e10198450e0a0ef6ad2f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,10 +19,10 @@ rvm: services: - redis-server before_script: - - "bundle exec rake db:setup" - - "bundle exec rake db:seed_fu" - "cp config/database.yml.$DB config/database.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" notifications: email: false diff --git a/CHANGELOG b/CHANGELOG index 031536816a5e17beea7afa2ee57c01c04a328fc4..06d19df7a326a08eace0e096954aec54f008048e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,14 @@ +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 - 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) @@ -9,6 +20,7 @@ v 6.7.0 - Show contribution guide link for new issue form (Jeroen van Baarsen) - Fix CI status for merge requests from fork - 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 - LDAP user filters - Streamline the content of notification emails (Pierre de La Morinerie) @@ -24,6 +36,12 @@ v 6.7.0 - 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) - 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 - Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 93dd88360a862339ee2577190eb360d9fc6deb47..b5fc2d59c9dc758b3b058701a7ac5f53306c1583 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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/). -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. @@ -48,7 +51,7 @@ Please send a merge request with a tested solution or a merge request with a fai ## 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 @@ -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 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. 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. 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 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 1. The change is as small as possible (see the above paragraph for details) diff --git a/Gemfile b/Gemfile index e3acf4a153d72c9d993c6a9dd9e867e4e7d0f3e4..397165f668f29c8c1f197128376e87729872efae 100644 --- a/Gemfile +++ b/Gemfile @@ -132,6 +132,9 @@ gem "gitlab-flowdock-git-hook", "~> 0.4.2" # Gemnasium integration gem "gemnasium-gitlab-service", "~> 0.2" +# Slack integration +gem "slack-notifier", "~> 0.2.0" + # d3 gem "d3_rails", "~> 3.1.4" @@ -162,8 +165,9 @@ gem "modernizr", "2.6.2" gem "raphael-rails", "~> 2.1.2" gem 'bootstrap-sass', '~> 3.0' gem "font-awesome-rails", '~> 3.2' -gem "gemoji", "~> 1.3.0" +gem "gitlab_emoji", "~> 0.0.1.1" gem "gon", '~> 5.0.0' +gem 'nprogress-rails' group :development do gem "annotate", "~> 2.6.0.beta2" @@ -214,7 +218,6 @@ group :development, :test do # PhantomJS driver for Capybara gem 'poltergeist', '~> 1.4.1' - gem 'spork', '~> 1.0rc' gem 'jasmine', '2.0.0.rc5' gem "spring", '1.1.1' diff --git a/Gemfile.lock b/Gemfile.lock index 52d6ac314633454bca33d6f7b146ac43c6fdc40b..1a0bce98ac52fcbf36790dc5520dd585a9457693 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -128,6 +128,8 @@ GEM mail (~> 2.2) email_validator (1.4.0) activemodel + emoji (1.0.1) + json enumerize (0.7.0) activesupport (>= 3.2) equalizer (0.0.8) @@ -165,7 +167,6 @@ GEM formatador (0.2.4) gemnasium-gitlab-service (0.2.1) rugged (~> 0.19) - gemoji (1.3.1) gherkin-ruby (0.3.1) racc github-markdown (0.5.5) @@ -190,6 +191,8 @@ GEM charlock_holmes (~> 0.6.6) escape_utils (~> 0.2.4) mime-types (~> 1.19) + gitlab_emoji (0.0.1.1) + emoji (~> 1.0.1) gitlab_git (5.7.1) activesupport (~> 4.0.0) charlock_holmes (~> 0.6.9) @@ -297,6 +300,7 @@ GEM net-ssh (>= 1.99.1) net-ssh (2.7.0) nokogiri (1.5.10) + nprogress-rails (0.1.2.3) oauth (0.4.7) oauth2 (0.8.1) faraday (~> 0.8) @@ -468,6 +472,7 @@ GEM rack-protection (~> 1.4) tilt (~> 1.3, >= 1.3.4) six (0.2.0) + slack-notifier (0.2.0) slim (2.0.2) temple (~> 0.6.6) tilt (>= 1.3.3, < 2.1) @@ -479,7 +484,6 @@ GEM capybara (>= 2.0.0) railties (>= 3) spinach (>= 0.4) - spork (1.0.0rc4) spring (1.1.1) spring-commands-rspec (1.0.1) spring (>= 0.9.1) @@ -591,12 +595,12 @@ DEPENDENCIES font-awesome-rails (~> 3.2) foreman gemnasium-gitlab-service (~> 0.2) - gemoji (~> 1.3.0) github-markup (~> 0.7.4)! gitlab-flowdock-git-hook (~> 0.4.2) gitlab-gollum-lib (~> 1.1.0) gitlab-grack (~> 2.0.0.pre) gitlab-linguist (~> 3.0.0) + gitlab_emoji (~> 0.0.1.1) gitlab_git (~> 5.7.1) gitlab_meta (= 6.0) gitlab_omniauth-ldap (= 1.0.4) @@ -620,6 +624,7 @@ DEPENDENCIES minitest (~> 4.7.0) modernizr (= 2.6.2) mysql2 + nprogress-rails omniauth (~> 1.1.3) omniauth-github omniauth-google-oauth2 @@ -652,9 +657,9 @@ DEPENDENCIES simplecov sinatra six + slack-notifier (~> 0.2.0) slim spinach-rails - spork (~> 1.0rc) spring (= 1.1.1) spring-commands-rspec (= 1.0.1) spring-commands-spinach (= 1.0.0) diff --git a/PROCESS.md b/PROCESS.md index c582dc6fd0d3552871d2ef8a8fe05d41555253be..2266d50b2384f12c61db44e136f99e7eb55e56a7 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -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) - Manages their work queue by looking at issues and merge requests assigned to them - 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). ## Priorities of the issue team @@ -73,7 +71,7 @@ Thanks for the issue report. Please reformat your issue to conform to the issue ### 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 diff --git a/Procfile b/Procfile index 9003369c938bdbf4b65feff0c0c8f5afb6655560..18df7e78f9b561c17e73313edc7c47f1ae07a565 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,2 @@ -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 diff --git a/README.md b/README.md index b23c0bb14f978bbc44e1d501b1254167e5a792c4..2668ed8bac0d4c5a8d9b2a027295f7195c47475c 100644 --- a/README.md +++ b/README.md @@ -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. -* 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 @@ -96,14 +96,9 @@ or start each component separately ### 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 - bundle exec rake gitlab:test RAILS_ENV=test + bundle exec rake test * [RSpec](http://rspec.info/) unit and functional tests @@ -134,22 +129,4 @@ or start each component separately ### Getting help -* [Maintenance policy](MAINTENANCE.md) specifies what versions are supported. - -* [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. +Please see [Getting help for GitLab](https://www.gitlab.com/getting-help/) on our website for the many options to get help. diff --git a/VERSION b/VERSION index 998707b421c89bd9a3063333f9f728ef3e43d101..38f2fa8992556cf51644a65679eeb585b0912591 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.7.0.pre +6.8.0.pre diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js deleted file mode 100644 index 4a393dbfe81113d35d9aedeb5c76f763190c1b2f..0000000000000000000000000000000000000000 --- a/app/assets/javascripts/application.js +++ /dev/null @@ -1,31 +0,0 @@ -// 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 diff --git a/app/assets/javascripts/main.js.coffee b/app/assets/javascripts/application.js.coffee similarity index 75% rename from app/assets/javascripts/main.js.coffee rename to app/assets/javascripts/application.js.coffee index 70e8972d24d132dae78c8508df753292b514ef69..5042221abe4d6e75a7e75800b574f130367da9ef 100644 --- a/app/assets/javascripts/main.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -1,3 +1,37 @@ +# 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) -> text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() @@ -41,19 +75,11 @@ window.linkify = (str) -> window.simpleFormat = (str) -> linkify(sanitize(str).replace(/\n/g, '<br />')) -window.startSpinner = -> - $('.turbolink-spinner').fadeIn() - -window.stopSpinner = -> - $('.turbolink-spinner').fadeOut() - window.unbindEvents = -> $(document).unbind('scroll') $(document).off('scroll') -document.addEventListener("page:fetch", startSpinner) document.addEventListener("page:fetch", unbindEvents) -document.addEventListener("page:change", stopSpinner) $ -> # Click a .one_click_select field, select the contents diff --git a/app/assets/javascripts/gfm_auto_complete.js.coffee b/app/assets/javascripts/gfm_auto_complete.js.coffee index dd12000a1cfcec32fc149a93aaf20b33fa5fd117..00d56ae5b4b0de2d24c87d34e4b072c9a3ab56c8 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.coffee +++ b/app/assets/javascripts/gfm_auto_complete.js.coffee @@ -6,7 +6,6 @@ GitLab.GfmAutoComplete = dataSource: '' # Emoji Emoji: - assetBase: '' template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>' # Team Members @@ -27,7 +26,7 @@ GitLab.GfmAutoComplete = tpl: @Emoji.template callbacks: 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 input.atwho diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 334667146819c7e1b0da2b8021b72f824b2a018f..4b7103010bbebfeb2f3db0ebf1e002333b431638 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -7,6 +7,8 @@ *= require select2 *= require highlightjs.min *= require_self + *= require nprogress + *= require nprogress-bootstrap */ @import "main/variables.scss"; diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index b5ccaed0401087e7afe52ea8b73528bbb7dc3012..4e660d0b1e07224ec4068442191a2c773e9837a4 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -355,3 +355,7 @@ table { @media (max-width: $screen-xs-max) { .container .content { margin-top: 20px; } } + +.wiki .highlight, .note-body .highlight { + margin-bottom: 9px; +} diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss index 3a31e28e0ac35f80705280eb4dc16674825423de..a4419551738755908a050b54ca7a6cdcf7cb986c 100644 --- a/app/assets/stylesheets/generic/typography.scss +++ b/app/assets/stylesheets/generic/typography.scss @@ -88,9 +88,6 @@ a:focus { .wiki { @include md-typography; - font-size: 14px; - line-height: 1.6; - /* Link to current header. */ h1, h2, h3, h4, h5, h6 { position: relative; @@ -120,3 +117,11 @@ a:focus { .md { @include md-typography; } + +/** + * Textareas intended for GFM + * + */ +textarea.js-gfm-input { + font-family: $monospace_font; +} diff --git a/app/assets/stylesheets/main/mixins.scss b/app/assets/stylesheets/main/mixins.scss index 4afe61d756c05b5d79e96dd4ba09b89e2b57e3c5..fcc7374d0d91ad6f5f021ae2d8dcc68fe2efb64c 100644 --- a/app/assets/stylesheets/main/mixins.scss +++ b/app/assets/stylesheets/main/mixins.scss @@ -84,6 +84,9 @@ } @mixin md-typography { + font-size: 14px; + line-height: 1.6; + img { max-width: 100%; } diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss new file mode 100644 index 0000000000000000000000000000000000000000..42dbf4d6ef3578f07a8761b3aaef06d1d2cbce78 --- /dev/null +++ b/app/assets/stylesheets/print.scss @@ -0,0 +1,13 @@ +/* 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; } diff --git a/app/assets/stylesheets/sections/dashboard.scss b/app/assets/stylesheets/sections/dashboard.scss index 6fc394e2e2bad6235aa7d00cca883dfa3b9da5fc..e088ef82203fa3a854b69d8b381524df1e0caffd 100644 --- a/app/assets/stylesheets/sections/dashboard.scss +++ b/app/assets/stylesheets/sections/dashboard.scss @@ -61,7 +61,7 @@ } .project-row, .group-row { - padding: 10px 12px !important; + padding: 8px 12px !important; font-size: 14px; line-height: 24px; diff --git a/app/assets/stylesheets/sections/diff.scss b/app/assets/stylesheets/sections/diff.scss index 813566c4def8799f26f295f3f30a7f7bb9f089b6..eb272f20f40c23d5b1e473b85bd642c70f5ad27f 100644 --- a/app/assets/stylesheets/sections/diff.scss +++ b/app/assets/stylesheets/sections/diff.scss @@ -62,6 +62,29 @@ 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 { margin: 0px; padding: 0px; @@ -125,8 +148,6 @@ } &.parallel { display: table-cell; - overflow: hidden; - width: 50%; } } } diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss index 6ef12b20a5a11c5e30c976f91b0f86753fd72aba..b68f882220ec54c1be294be1dcfb233cd3bf94e7 100644 --- a/app/assets/stylesheets/sections/events.scss +++ b/app/assets/stylesheets/sections/events.scss @@ -42,7 +42,7 @@ } } - padding: 14px 0px; + padding: 12px 0px; border-bottom: 1px solid #eee; .event-title { color: #333; @@ -63,6 +63,10 @@ color: #666; margin-top: 5px; + .md { + font-size: 13px; + } + pre { border: none; background: #f9f9f9; diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss index f8da4f0f87bf0ca3ffec0665f614fb0166f7cacc..06709bd7ef684bfdf3cc5a1d89d07308cba09df9 100644 --- a/app/assets/stylesheets/sections/header.scss +++ b/app/assets/stylesheets/sections/header.scss @@ -273,3 +273,9 @@ header { } } } + +@media (max-width: $screen-xs-max) { + #nprogress .spinner { + right: 35px !important; + } +} diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index a65771974c6f3f4bd634f6c191d7a35a9dff47a0..4d7a873a5199c30300fafdfbee7c3e5bc9a0117c 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -27,11 +27,15 @@ ul.notes { .discussion-last-update, .note-last-update { - font-style: italic; + &:before { + content: "\00b7"; + } + font-size: 13px; } .author { - color: $style_color; + color: #555; font-weight: bold; + font-size: 14px; &:hover { color: $primary_color; } diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..988ede3007bcde76b39f4754d9e94d412ef6d32d --- /dev/null +++ b/app/controllers/passwords_controller.rb @@ -0,0 +1,18 @@ +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 diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb index b4f14e649e2ee2d293dca298e4373d1c99319369..6713cd7c8c742dc4f82d7e895548ffd2d61b00d3 100644 --- a/app/controllers/profiles/keys_controller.rb +++ b/app/controllers/profiles/keys_controller.rb @@ -41,7 +41,7 @@ class Profiles::KeysController < ApplicationController begin user = User.find_by_username(params[:username]) if user.present? - render text: user.all_ssh_keys.join("\n") + render text: user.all_ssh_keys.join("\n"), content_type: "text/plain" else render_404 and return end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 9d97c820f38437b69f6a140fabec860230fce1b1..eef849d8209a5a61398383d9b456ed5344bf2855 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -76,7 +76,7 @@ class Projects::IssuesController < Projects::ApplicationController end def update - @issue.update_attributes(params[:issue].merge(author_id_of_changes: current_user.id)) + @issue.update_attributes(params[:issue]) @issue.reset_events_cache respond_to do |format| diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 38e567f3b137624dc19cd3cb682ce729f4067024..e6edceb1fbc1fb7cb4a52aca16aed9807072da0b 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -109,7 +109,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController params[:merge_request].delete(:source_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 respond_to do |format| diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index aea92a19f3456d29920eaeebc8d55d084ad299b3..05237bbd2d25dc321e7ff08ae3be812cf878e92a 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -38,7 +38,6 @@ class Projects::MilestonesController < Projects::ApplicationController def create @milestone = @project.milestones.new(params[:milestone]) - @milestone.author_id_of_changes = current_user.id if @milestone.save redirect_to project_milestone_path(@project, @milestone) @@ -48,7 +47,7 @@ class Projects::MilestonesController < Projects::ApplicationController end def update - @milestone.update_attributes(params[:milestone].merge(author_id_of_changes: current_user.id)) + @milestone.update_attributes(params[:milestone]) respond_to do |format| format.js diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 8d24052f724dfab265f1c721cce56150c249a93e..ebb8a90c630039ace5730de753303406c5ba70e2 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -123,11 +123,20 @@ class ProjectsController < ApplicationController end 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 = { - 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]), 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| @@ -162,4 +171,25 @@ class ProjectsController < ApplicationController def user_layout current_user ? "projects" : "public_projects" 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 diff --git a/app/finders/base_finder.rb b/app/finders/base_finder.rb index d20716fb170bc8082018a9fb860a16581dbd045e..7fc5840561cb7748e71bfbef1dee83ba202fed83 100644 --- a/app/finders/base_finder.rb +++ b/app/finders/base_finder.rb @@ -47,9 +47,9 @@ class BaseFinder [] end elsif current_user && params[:authorized_only].presence - klass.of_projects(current_user.authorized_projects) + klass.of_projects(current_user.authorized_projects).references(:project) else - klass.of_projects(Project.accessible_to(current_user)) + klass.of_projects(Project.accessible_to(current_user)).references(:project) end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 5e5f3f77a21dec545646dd23a46150f3c9eccd06..c6e4f574b67805c48353ea4544fc132f210d11cd 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -105,8 +105,80 @@ module CommitsHelper branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe end - def get_old_file(project, commit, diff) - project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id + def parallel_diff_lines(project, commit, diff, file) + 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 protected diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 1381b0220d676bd5258f0619ae8b64e76c466159..ba25a87f392dbbeb38715ac777e91b7df9288b33 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -20,7 +20,7 @@ module MergeRequestsHelper target_project_id: target_project.id, source_branch: event.branch_name, target_branch: target_project.repository.root_ref, - title: event.branch_name.titleize + title: event.branch_name.humanize } end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 2dbc1cffb16c7ecfe7120d8333408ff9cde2b706..50501dffefb54055d5b2ddbe28079f21fce9f051 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -40,7 +40,7 @@ module TreeHelper # Returns boolean def markup?(filename) filename.downcase.end_with?(*%w(.textile .rdoc .org .creole - .mediawiki .rst .asciidoc .pod)) + .mediawiki .rst .adoc .asciidoc .pod)) end def gitlab_markdown?(filename) diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb index 5e1b8faf13e5ddd7d6cd8175136bdb3f8f98c4d1..a97d55f1b505040b84b1fc47fab8ff4e3ee52710 100644 --- a/app/mailers/emails/merge_requests.rb +++ b/app/mailers/emails/merge_requests.rb @@ -29,11 +29,11 @@ module Emails subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) 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) @project = @merge_request.project @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), subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 75989888bfac047c4011150d6240da4f5e607e57..de167f8803a9b5afcc4620d2355ac6abbbdaf697 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -37,8 +37,6 @@ module Issuable allow_nil: true, prefix: true - attr_accessor :author_id_of_changes - attr_mentionable :title, :description end diff --git a/app/models/diff_line.rb b/app/models/diff_line.rb new file mode 100644 index 0000000000000000000000000000000000000000..ad37945874a69fd21511b0646f1344273e9a6b17 --- /dev/null +++ b/app/models/diff_line.rb @@ -0,0 +1,3 @@ +class DiffLine + attr_accessor :type, :content, :num, :code +end diff --git a/app/models/event.rb b/app/models/event.rb index d43d6eb682fe7e33b2f61434c282bb7fbd228367..5c156856d7947a9a0928533fbede4b34fdcac132 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -47,14 +47,6 @@ class Event < ActiveRecord::Base scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent } 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') commit = project.repository.commit(ref.target) diff --git a/app/models/issue.rb b/app/models/issue.rb index a8dc6e5fd85a7acd1ea7dc9f1fac894152941484..2104032774112da80fbe6207834380c46d1520ac 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -30,8 +30,7 @@ class Issue < ActiveRecord::Base scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } attr_accessible :title, :assignee_id, :position, :description, - :milestone_id, :label_list, :author_id_of_changes, - :state_event + :milestone_id, :label_list, :state_event acts_as_taggable_on :labels diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 7c2648d8c7af8511bbf30dba400039e80efd34e8..5c2b0656d40327557fd63c5a956566dca839d11e 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -38,7 +38,7 @@ class MergeRequest < ActiveRecord::Base 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 diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 1a73fa71e4868ad64cd71a0465d52d3a3db2eafb..e16529a634c3fed7a75abc6b42d2f6265a7bfc7c 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -16,8 +16,7 @@ class Milestone < ActiveRecord::Base include InternalId - attr_accessible :title, :description, :due_date, :state_event, :author_id_of_changes - attr_accessor :author_id_of_changes + attr_accessible :title, :description, :due_date, :state_event belongs_to :project has_many :issues @@ -89,6 +88,6 @@ class Milestone < ActiveRecord::Base end def author_id - author_id_of_changes + nil end end diff --git a/app/models/note.rb b/app/models/note.rb index 48c03c9d587d48291774ccb96d48a8710b853cff..906de4855ab145d9a3df7fbc411428208ee75dfb 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -199,7 +199,8 @@ class Note < ActiveRecord::Base def downvote? votable? && (note.start_with?('-1') || note.start_with?(':-1:') || - note.start_with?(':thumbsdown:') + note.start_with?(':thumbsdown:') || + note.start_with?(':thumbs_down_sign:') ) end @@ -249,7 +250,8 @@ class Note < ActiveRecord::Base def upvote? votable? && (note.start_with?('+1') || note.start_with?(':+1:') || - note.start_with?(':thumbsup:') + note.start_with?(':thumbsup:') || + note.start_with?(':thumbs_up_sign:') ) end diff --git a/app/models/project.rb b/app/models/project.rb index 7d7edc457391a94c34ea61f8f77f8b9d88e0eb18..769ab217625cb104545ce97bd17aee59dd0262bc 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -56,6 +56,7 @@ class Project < ActiveRecord::Base has_one :flowdock_service, dependent: :destroy has_one :assembla_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_from_project, through: :forked_project_link # Merge Requests for target project should be removed with it @@ -304,7 +305,7 @@ class Project < ActiveRecord::Base end 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 def gitlab_ci? diff --git a/app/models/project_services/slack_message.rb b/app/models/project_services/slack_message.rb new file mode 100644 index 0000000000000000000000000000000000000000..b2b8d6fed7a161151ec250e769cb40be6a82aacc --- /dev/null +++ b/app/models/project_services/slack_message.rb @@ -0,0 +1,95 @@ +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 diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..410dda22d47c0e05d7b08ec5fb49baf50f7134dc --- /dev/null +++ b/app/models/project_services/slack_service.rb @@ -0,0 +1,68 @@ +# == 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 diff --git a/app/models/user.rb b/app/models/user.rb index a9ee471ad61fb82d9da0b7b8407d7c53b895dc25..25c10a6faa0e5738d453588004d12bdca26e228a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -204,7 +204,7 @@ class User < ActiveRecord::Base end 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 def by_username_or_id(name_or_id) @@ -249,7 +249,7 @@ class User < ActiveRecord::Base def namespace_uniq namespace_name = self.username if Namespace.find_by(path: namespace_name) - self.errors.add :username, "already exist" + self.errors.add :username, "already exists" end end diff --git a/app/observers/activity_observer.rb b/app/observers/activity_observer.rb deleted file mode 100644 index d754ac8185a50c011bd2c4a99b97392994850086..0000000000000000000000000000000000000000 --- a/app/observers/activity_observer.rb +++ /dev/null @@ -1,39 +0,0 @@ -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 diff --git a/app/observers/base_observer.rb b/app/observers/base_observer.rb index f9a0242ce770f2479fed5c8c8de4943720010e9f..d685bd5d819cdbbd504fce72ff07563d6c7ffa1e 100644 --- a/app/observers/base_observer.rb +++ b/app/observers/base_observer.rb @@ -3,6 +3,10 @@ class BaseObserver < ActiveRecord::Observer NotificationService.new end + def event_service + EventCreateService.new + end + def log_info message Gitlab::AppLogger.info message end diff --git a/app/observers/issue_observer.rb b/app/observers/issue_observer.rb index 6ef13eb5d5e757674b0ff6ce5c3cb9b8c2aef756..30da1f83da796c7a79dfe16ee0447775ab52ec80 100644 --- a/app/observers/issue_observer.rb +++ b/app/observers/issue_observer.rb @@ -1,17 +1,20 @@ class IssueObserver < BaseObserver def after_create(issue) notification.new_issue(issue, current_user) + event_service.open_issue(issue, current_user) issue.create_cross_references!(issue.project, current_user) execute_hooks(issue) end def after_close(issue, transition) notification.close_issue(issue, current_user) + event_service.close_issue(issue, current_user) create_note(issue) execute_hooks(issue) end def after_reopen(issue, transition) + event_service.reopen_issue(issue, current_user) create_note(issue) execute_hooks(issue) end diff --git a/app/observers/merge_request_observer.rb b/app/observers/merge_request_observer.rb index f2e2d16c9436a81e3818ad1dda7c7baf72f9ca67..04ee30b4976cd0c71e3f8ebfb68fc6c43cea5b48 100644 --- a/app/observers/merge_request_observer.rb +++ b/app/observers/merge_request_observer.rb @@ -1,25 +1,20 @@ -class MergeRequestObserver < ActivityObserver - observe :merge_request - +class MergeRequestObserver < BaseObserver def after_create(merge_request) - if merge_request.author_id - create_event(merge_request, Event.determine_action(merge_request)) - end - + event_service.open_mr(merge_request, current_user) notification.new_merge_request(merge_request, current_user) merge_request.create_cross_references!(merge_request.project, current_user) execute_hooks(merge_request) end 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) create_note(merge_request) execute_hooks(merge_request) end def after_reopen(merge_request, transition) - create_event(merge_request, Event::REOPENED) + event_service.reopen_mr(merge_request, current_user) create_note(merge_request) execute_hooks(merge_request) merge_request.reload_code @@ -33,16 +28,6 @@ class MergeRequestObserver < ActivityObserver execute_hooks(merge_request) 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 # Create merge request note with service comment like 'Status changed to closed' diff --git a/app/observers/milestone_observer.rb b/app/observers/milestone_observer.rb new file mode 100644 index 0000000000000000000000000000000000000000..a1a62a99a8f56579f84731179395835516734f14 --- /dev/null +++ b/app/observers/milestone_observer.rb @@ -0,0 +1,13 @@ +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 diff --git a/app/observers/note_observer.rb b/app/observers/note_observer.rb index d31b6e8d7ce6c4a363434004ae80c28f2229032c..337bb1dc5ae6222aab4ebb94b341e25e279b959c 100644 --- a/app/observers/note_observer.rb +++ b/app/observers/note_observer.rb @@ -2,6 +2,12 @@ class NoteObserver < BaseObserver def after_create(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? # Create a cross-reference note if this Note contains GFM that names an # issue, merge request, or commit. diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..8d8a5873e625fdf91ef58509045d56e9f38c45eb --- /dev/null +++ b/app/services/event_create_service.rb @@ -0,0 +1,64 @@ +# 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 diff --git a/app/services/merge_requests/auto_merge_service.rb b/app/services/merge_requests/auto_merge_service.rb index d60d61ed54a0f651b72cbd624270f907d5f004ef..e35c03275f24cf8802b01cd1852a8d4df7eecbbc 100644 --- a/app/services/merge_requests/auto_merge_service.rb +++ b/app/services/merge_requests/auto_merge_service.rb @@ -9,11 +9,10 @@ module MergeRequests merge_request.lock if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message) - merge_request.author_id_of_changes = current_user.id merge_request.merge - notification.merge_mr(merge_request) - create_merge_event(merge_request) + notification.merge_mr(merge_request, current_user) + create_merge_event(merge_request, current_user) execute_project_hooks(merge_request) true diff --git a/app/services/merge_requests/base_merge_service.rb b/app/services/merge_requests/base_merge_service.rb index dbdb00630740b9c74982a3bc78160f99a40e5e97..9bc50d3d16ca29452cfb71ee5b915306f59027b4 100644 --- a/app/services/merge_requests/base_merge_service.rb +++ b/app/services/merge_requests/base_merge_service.rb @@ -7,14 +7,8 @@ module MergeRequests NotificationService.new end - def create_merge_event(merge_request) - Event.create( - 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 - ) + def create_merge_event(merge_request, current_user) + EventCreateService.new.merge_mr(merge_request, current_user) end def execute_project_hooks(merge_request) diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index 1d5af04cdbbb7e5dae299388df8751ad660c65fd..680766140bdf2ede94077110dfce5b0e7154871d 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -7,11 +7,10 @@ module MergeRequests # to target branch class MergeService < BaseMergeService def execute(merge_request, current_user, commit_message) - merge_request.author_id_of_changes = current_user.id merge_request.merge - notification.merge_mr(merge_request) - create_merge_event(merge_request) + notification.merge_mr(merge_request, current_user) + create_merge_event(merge_request, current_user) execute_project_hooks(merge_request) true diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 5daf573630dda4d3d450475b2334ce05c9313b4f..6fda9868aa5609be6270fb54f698c3f484d05f46 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -86,12 +86,12 @@ class NotificationService # * merge_request assignee if their notification level is not Disabled # * 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 = recipients.concat(project_watchers(merge_request.target_project)).uniq 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 @@ -111,6 +111,7 @@ class NotificationService # ignore gitlab service messages 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 } diff --git a/app/views/devise/sessions/_oauth_providers.html.haml b/app/views/devise/sessions/_oauth_providers.html.haml index 935bc6af505432b1b19c0233e9a4312dc085d56e..2b1cb9c694f26bc72dfcf512cf5a4b54fbd9f214 100644 --- a/app/views/devise/sessions/_oauth_providers.html.haml +++ b/app/views/devise/sessions/_oauth_providers.html.haml @@ -2,10 +2,12 @@ - if providers.present? %hr %div{:'data-no-turbolink' => 'data-no-turbolink'} - %span Sign in with: + %span Sign in with*: - providers.each do |provider| %span - if default_providers.include?(provider) = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) - else = link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn" + %br + %small * Make sure your email address is public diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 9ba20f1347d0970f2810f57cdf849ec0a9dbe8ab..d9a7a2d31cfa058aa51d95413ada90b8a95ba60b 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -4,7 +4,8 @@ = "#{title} | " if defined?(title) GitLab = 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" = csrf_meta_tags = include_gon diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml index 5080a1b7ef6cf429da771715b0356baf930f7a06..d8001fd76d72f64b0151b95f619b293b882c3005 100644 --- a/app/views/layouts/_head_panel.html.haml +++ b/app/views/layouts/_head_panel.html.haml @@ -14,10 +14,6 @@ .navbar-collapse.collapse %ul.nav.navbar-nav - %li.hidden-sm.hidden-xs - %a - %div.hide.turbolink-spinner - %i.icon-refresh.icon-spin %li.hidden-sm.hidden-xs = render "layouts/search" %li.visible-sm.visible-xs diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml index 6a20dedf62f76ad99ee54ba02e9145518bd2567a..353f7ce34f1273604f4d6a9aa08d646c8e3c79a5 100644 --- a/app/views/layouts/_init_auto_complete.html.haml +++ b/app/views/layouts/_init_auto_complete.html.haml @@ -1,4 +1,3 @@ :javascript - GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project)}" - GitLab.GfmAutoComplete.Emoji.assetBase = "#{Gitlab.config.gitlab.relative_url_root + '/assets/emoji'}" + GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project, type: @noteable.class, type_id: params[:id])}" GitLab.GfmAutoComplete.setup(); diff --git a/app/views/layouts/_public_head_panel.html.haml b/app/views/layouts/_public_head_panel.html.haml index 99e11cd2b5c62e8ab8ace91e88128cac66b6c14e..63992a22f320498976896b48bfbdc357490b530c 100644 --- a/app/views/layouts/_public_head_panel.html.haml +++ b/app/views/layouts/_public_head_panel.html.haml @@ -8,11 +8,15 @@ %span.separator %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' - %ul.nav.navbar-nav - %li - %a - %div.hide.turbolink-spinner - %i.icon-refresh.icon-spin + .navbar-collapse.collapse + %ul.nav.navbar-nav + %li.visible-xs + = link_to "Sign in", new_session_path(:user) + diff --git a/app/views/layouts/public_projects.html.haml b/app/views/layouts/public_projects.html.haml index cf4ca9c7a84598dc3da90b695ba94580437b1f3b..2a9230244f89a38391ccfa4a205f1ff07135f23c 100644 --- a/app/views/layouts/public_projects.html.haml +++ b/app/views/layouts/public_projects.html.haml @@ -4,7 +4,7 @@ %body{class: "#{app_theme} application", :'data-page' => body_data_page} = render "layouts/broadcast" = render "layouts/public_head_panel", title: project_title(@project) - %nav.main-nav + %nav.main-nav.navbar-collapse.collapse .container= render 'layouts/nav/project' .container .content= yield diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml index ab0d6c653b909c997ddbba8929317583f75d1811..85a01a567f34e0bd667df21640ac6b226bb22fb4 100644 --- a/app/views/notify/repository_push_email.html.haml +++ b/app/views/notify/repository_push_email.html.haml @@ -7,7 +7,7 @@ %li #{commit.short_id} - #{commit.title} -%h4 Diff: +%h4 Changes: - @diffs.each do |diff| %li %strong @@ -23,6 +23,6 @@ %br - 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? - %h5 Diff for big amount of commits is disabled + %h5 Changes are not shown due to large amount of commits diff --git a/app/views/notify/repository_push_email.text.haml b/app/views/notify/repository_push_email.text.haml index 93b344d2c8285adcec9fa12f7b7f25af0ceb0dba..b8d7fbeb046d660d21a409f2b69ae75cc06fe494 100644 --- a/app/views/notify/repository_push_email.text.haml +++ b/app/views/notify/repository_push_email.text.haml @@ -6,7 +6,7 @@ Commits: #{commit.short_id} - #{truncate(commit.title, length: 40)} \ \ -Diff: +Changes: - @diffs.each do |diff| \ \===================================== @@ -22,4 +22,4 @@ Diff: - if @compare.timeout Huge diff. To prevent performance issues it was hidden - elsif @compare.commits_over_limit? - Diff for big amount of commits is disabled + Changes are not shown due to large amount of commits diff --git a/app/views/projects/commits/_parallel_view.html.haml b/app/views/projects/commits/_parallel_view.html.haml index 3234e9da0ac88fa923ee22fb156ea49cdc5e56db..5b60ab80ba45dd8111f49b50225ebde0ce0c28f0 100644 --- a/app/views/projects/commits/_parallel_view.html.haml +++ b/app/views/projects/commits/_parallel_view.html.haml @@ -1,75 +1,55 @@ / Side-by-side diff view -- old_file = get_old_file(project, @commit, diff) -- deleted_lines = {} -- 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 +- old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file) +- num_lines = old_lines.length %div.text-file-parallel - %table{ style: "table-layout: fixed;" } - - 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] + %div.diff-side.diff-side-left + %table + - old_lines.each do |line| + + %tr.line_holder.parallel + - if line.type == :file_created + %td.line_content.parallel= "File was created" + - 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 - - elsif deleted_line - - new_line = nil - - offset2 += 1 - - elsif added_line - - old_line = nil - - offset1 += 1 + - if new_lines[index].type == :added + %td.new_line.new= new_lines[index].num + - else + %td.new_line= new_lines[index].num - %tr.line_holder.parallel - - if line_index == 0 && diff.new_file - %td.line_content.parallel= "File was created" - %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= "" + %div.diff-side.diff-side-right + %table + - new_lines.each do |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 - %td.new_line= "" - %td.line_content.parallel= "File was deleted" - - elsif added_line - %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= "" +:javascript + $('.diff-side-right').on('scroll', function(){ + $('.diff-side-left, .diff-middle').scrollTop($(this).scrollTop()); + $('.diff-side-left').scrollLeft($(this).scrollLeft()); + }); - - if @reply_allowed - - comments1 = [] - - comments2 = [] - - 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 + $('.diff-side-left').on('scroll', function(){ + $('.diff-side-right, .diff-middle').scrollTop($(this).scrollTop()); // might never be relevant + $('.diff-side-right').scrollLeft($(this).scrollLeft()); + }); diff --git a/app/views/projects/commits/_text_file.html.haml b/app/views/projects/commits/_text_file.html.haml index c827d96d855847b8738c65a1546a5121f421c2a6..ba83d2e5a0f0ba3341599056bdfc86be7a30a2f3 100644 --- a/app/views/projects/commits/_text_file.html.haml +++ b/app/views/projects/commits/_text_file.html.haml @@ -1,6 +1,6 @@ - too_big = diff.diff.lines.count > 1000 - 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}"} - each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line| diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml index 9bd498553697169a56881c5a7dc3b224f022ad48..57331bff31b87886843ef07807d7a51162a2c345 100644 --- a/app/views/projects/compare/show.html.haml +++ b/app/views/projects/compare/show.html.haml @@ -18,17 +18,17 @@ - else %ul.well-list= render Commit.decorate(@commits), project: @project - %h4 Diff + %h4 Changes - if @diffs.present? = render "projects/commits/diffs", diffs: @diffs, project: @project - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE .bs-callout.bs-callout-danger %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 .bs-callout.bs-callout-danger - %h4 Diff for this comparison is extremely large. - %p Use command line to browse diff for this comparison. + %h4 Number of changed files for this comparison is extremely large. + %p Use command line to browse through changes for this comparison. - else diff --git a/app/views/projects/deploy_keys/show.html.haml b/app/views/projects/deploy_keys/show.html.haml index 67615e1781fd0dc4acccc2dac5ca5530f41da25f..c66e6bc69c30f7a0b790b2c25737ee1883b573d7 100644 --- a/app/views/projects/deploy_keys/show.html.haml +++ b/app/views/projects/deploy_keys/show.html.haml @@ -2,7 +2,7 @@ Deploy key: = @key.title %small - created at + created on = @key.created_at.stamp("Aug 21, 2011") .back-link = link_to project_deploy_keys_path(@project) do diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index dd091302c8eea0eca175e89d42a28eb3407b05d4..05cae80e50c957592d907664873fd3e48a041004 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -1,7 +1,7 @@ %div.issue-form-holder %h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}" %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)) .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 diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 51fa29ddcbee4e1bdc5934b895d3af0d0d97ff51..22502760e500e2d94bfdae5ea7954b13f8e977d2 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -1,13 +1,18 @@ -- 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| - -if @merge_request.errors.any? - .alert.alert-danger - %ul - - @merge_request.errors.full_messages.each do |msg| - %li= msg + .row + .col-sm-2 + .col-sm-10 + - 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 + 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 .form-group @@ -47,24 +52,6 @@ = 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'}. - %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) - - = 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 - if @merge_request.new_record? = f.submit 'Submit merge request', class: "btn btn-create" @@ -96,7 +83,3 @@ target_branch.on("change", function() { $.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(); - }); diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 809b01918cffab002d779007a4f55a56e533b7ff..7a21c0dd069c8ab1b1b312f564fe0fbc05ae465c 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -20,7 +20,7 @@ %li.diffs-tab{data: {action: 'diffs'}} = link_to diffs_project_merge_request_path(@project, @merge_request) do %i.icon-list-alt - Diff + Changes - content_for :note_actions do - if can?(current_user, :modify_merge_request, @merge_request) diff --git a/app/views/projects/merge_requests/branch_from.js.haml b/app/views/projects/merge_requests/branch_from.js.haml index d3147188d1c65c951c2949d566a7622a7426de32..1b1082baafeaf8202cfedf22f169cf5a491a267d 100644 --- a/app/views/projects/merge_requests/branch_from.js.haml +++ b/app/views/projects/merge_requests/branch_from.js.haml @@ -3,5 +3,5 @@ var mrTitle = $('#merge_request_title'); if(mrTitle.val().length == 0) { - mrTitle.val("#{params[:ref].titleize}"); + mrTitle.val("#{params[:ref].humanize}"); } diff --git a/app/views/projects/merge_requests/show/_diffs.html.haml b/app/views/projects/merge_requests/show/_diffs.html.haml index 7c4f43d2d80be6085a16bb4c2b6a7b9bcaffc961..3d48514f98b8355f9d2412e189b517c29a222406 100644 --- a/app/views/projects/merge_requests/show/_diffs.html.haml +++ b/app/views/projects/merge_requests/show/_diffs.html.haml @@ -5,7 +5,7 @@ - else .bs-callout.bs-callout-warning %h4 - Diff for this comparison is extremely large. + Changes view for this comparison is extremely large. %p You can = link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request, format: :diff), class: "vlink" diff --git a/app/views/projects/merge_requests/show/_mr_accept.html.haml b/app/views/projects/merge_requests/show/_mr_accept.html.haml index 97706a5a4b0b61a99ee58338a3302920206dd563..bd7c8435f4c7eb3af63180002aa94cc794c7c662 100644 --- a/app/views/projects/merge_requests/show/_mr_accept.html.haml +++ b/app/views/projects/merge_requests/show/_mr_accept.html.haml @@ -1,6 +1,13 @@ - unless @allowed_to_merge - .bs-callout - %strong You don't have permission to merge this MR + - if @project.archived? + .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 diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index 38db4363aded6d9e444ab6dbccfb1e37c7d90603..abdcd127a0c07b45ac0820717cbd339f8813a4fb 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -29,13 +29,13 @@ %span %i.icon-remove 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? .alert.alert-info %span %i.icon-ok 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? .alert.alert-info.alert-info %span diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 217e36e38d0b7aee8ac029ece769c09abc2356a7..81bf0611ec679ebc4eac8950a527f4024f0aac52 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -23,7 +23,7 @@ %i.icon-thumbs-up \+1 - if note.downvote? - %span.vote.downvote.label.label-error + %span.vote.downvote.label.label-danger %i.icon-thumbs-down \-1 diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 8a1e1d3354bb28ab56324f6ee2303fbb81b86f44..20879d69091fa1f7ca99c400fc9c7c5cb1a6f627 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -9,8 +9,8 @@ .col-md-3.project-side.hidden-sm .clearfix - if @project.archived? - .alert - %h5 + .alert.alert-warning + %h4 %i.icon-warning-sign Archived project! %p Repository is read-only diff --git a/config/application.rb b/config/application.rb index 4d7c1415c8eabb09d9e2f23565991a0fc3dd5500..a782dd1d01eee03e1256fda187afe5fd49836e6f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -19,7 +19,7 @@ module Gitlab # config.plugins = [ :exception_notification, :ssl_requirement, :all ] # Activate observers that should always be running. - config.active_record.observers = :activity_observer, + config.active_record.observers = :milestone_observer, :project_activity_cache_observer, :issue_observer, :key_observer, diff --git a/config/environments/production.rb b/config/environments/production.rb index 9ac4622abc28a158b78ab8c6e2b92383a9a749a7..ad3c03d8fc914e3728929fd57ccf8da801e1eccc 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -33,6 +33,12 @@ Gitlab::Application.configure do # See everything in the log (default is :info) # 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 # config.log_tags = [ :subdomain, :uuid ] diff --git a/config/initializers/gemoji.rb b/config/initializers/gemoji.rb deleted file mode 100644 index 6cc33aced77900ec0ec71f958837800e94b7ede8..0000000000000000000000000000000000000000 --- a/config/initializers/gemoji.rb +++ /dev/null @@ -1,3 +0,0 @@ -# Workaround for https://github.com/github/gemoji/pull/18 -require 'gemoji' -Gitlab::Application.config.assets.paths << Emoji.images_path diff --git a/config/routes.rb b/config/routes.rb index 628d1f631bc5bf0d6e28500320b8f66031670e93..709b66d3e06d268a017c27f752fb68dbe532adb2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -167,7 +167,7 @@ Gitlab::Application.routes.draw do 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 diff --git a/config/unicorn_development.rb b/config/unicorn_development.rb new file mode 100644 index 0000000000000000000000000000000000000000..94a7061451d55d014489eb7efb0a471f3613d70f --- /dev/null +++ b/config/unicorn_development.rb @@ -0,0 +1,2 @@ +worker_processes 2 +timeout 30 diff --git a/db/fixtures/development/11_keys.rb b/db/fixtures/development/11_keys.rb index 42171393d1b32847d4bcd0c87677079faa1abb71..61329f197c3f7d89f8a1d02b7bf8954fe16097bd 100644 --- a/db/fixtures/development/11_keys.rb +++ b/db/fixtures/development/11_keys.rb @@ -2,7 +2,7 @@ Gitlab::Seeder.quiet do User.first(30).each_with_index do |user, i| Key.seed(:id, [ { - id: i, + id: i + 1, title: "Sample key #{i}", key: "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt#{i + 100}6k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", user_id: user.id, diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000000000000000000000000000000000000..865806f297a1316fcff0df1eff99c9fe95b21288 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,17 @@ +## 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) diff --git a/doc/api/README.md b/doc/api/README.md index 850666953a3959d4bc2a9f8db205193f3ebebf22..fa5b12af14c3e1c2e8fe010426f9bbb8d859aad3 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -1,5 +1,31 @@ # 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. 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 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` - - - -## 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 diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 2ddaea5a584b04db72f5cf0bf86dde22c2ce2433..3ce0fb849170248944bb234f87a5f38812ebbd54 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -150,6 +150,7 @@ Parameters: + `target_branch` - The target branch + `assignee_id` - Assignee user ID + `title` - Title of MR ++ `state_event` - New state (close|reopen|merge) ```json @@ -210,3 +211,44 @@ Parameters: "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" + } + } +] +``` diff --git a/doc/api/projects.md b/doc/api/projects.md index 6e82ddd99034f166735e334dda386b901f349d23..54618d7c045aac3d9570a5c74f333b1e6759704a 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -621,3 +621,29 @@ Parameters: + query (required) - A string contained in the project name + per_page (optional) - number of projects to return per page + 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" + } +] +``` diff --git a/doc/api/system_hooks.md b/doc/api/system_hooks.md index 355ce31c1263d66cb580ea7d36472738e9ce6374..2e87ada1d70f4c6a0aba9f47b8270112cb99839f 100644 --- a/doc/api/system_hooks.md +++ b/doc/api/system_hooks.md @@ -1,70 +1,70 @@ -All methods require admin authorization. - -The url endpoint of the system hooks can be configured in [the admin area under hooks](/admin/hooks). - -## List system hooks - -Get list of system hooks - -``` -GET /hooks -``` - -Parameters: - -+ **none** - -```json -[ - { - "id":3, - "url":"http://example.com/hook", - "created_at":"2013-10-02T10:15:31Z" - } -] -``` - -## Add new system hook hook - -``` -POST /hooks -``` - -Parameters: - -+ `url` (required) - The hook URL - - -## Test system hook - -``` -GET /hooks/:id -``` - -Parameters: - -+ `id` (required) - The ID of hook - -```json -{ - "event_name":"project_create", - "name":"Ruby", - "path":"ruby", - "project_id":1, - "owner_name":"Someone", - "owner_email":"example@gitlabhq.com" -} -``` - -## Delete system 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. - -``` -DELETE /hooks/:id -``` - -Parameters: - -+ `id` (required) - The ID of hook +All methods require admin authorization. + +The url endpoint of the system hooks can be configured in [the admin area under hooks](/admin/hooks). + +## List system hooks + +Get list of system hooks + +``` +GET /hooks +``` + +Parameters: + ++ **none** + +```json +[ + { + "id":3, + "url":"http://example.com/hook", + "created_at":"2013-10-02T10:15:31Z" + } +] +``` + +## Add new system hook hook + +``` +POST /hooks +``` + +Parameters: + ++ `url` (required) - The hook URL + + +## Test system hook + +``` +GET /hooks/:id +``` + +Parameters: + ++ `id` (required) - The ID of hook + +```json +{ + "event_name":"project_create", + "name":"Ruby", + "path":"ruby", + "project_id":1, + "owner_name":"Someone", + "owner_email":"example@gitlabhq.com" +} +``` + +## Delete system 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. + +``` +DELETE /hooks/:id +``` + +Parameters: + ++ `id` (required) - The ID of hook diff --git a/doc/development/README.md b/doc/development/README.md new file mode 100644 index 0000000000000000000000000000000000000000..aa59eb2c3e1fa1f86b3432972b086cfec79a0de6 --- /dev/null +++ b/doc/development/README.md @@ -0,0 +1,2 @@ ++ [Architecture](architecture.md) ++ [Shell commands](shell_commands.md) diff --git a/doc/development/architecture.md b/doc/development/architecture.md index ab74af4faf93351d30c8e3950ee1bd43fe0d8462..6f832614d706a058d36448005c8f772ea3379696 100644 --- a/doc/development/architecture.md +++ b/doc/development/architecture.md @@ -18,7 +18,7 @@ New releases are generally around the same time as GitLab CE releases with excep # 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. 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 ## Components - + 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. @@ -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`. -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 diff --git a/doc/development/shell_commands.md b/doc/development/shell_commands.md index 57b1172d5e64294505fd917ae78d8b444d8bcf0d..af0d5ca44263da4a8efead083b6d2e637f838a7f 100644 --- a/doc/development/shell_commands.md +++ b/doc/development/shell_commands.md @@ -1,5 +1,11 @@ # 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 Sometimes we invoke basic Unix commands via the shell when there is also a Ruby API for doing it. diff --git a/doc/install/README.md b/doc/install/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ec80e3cd62a66604611ab10950739015e113f78a --- /dev/null +++ b/doc/install/README.md @@ -0,0 +1,4 @@ ++ [Installation](installation.md) ++ [Requirements](requirements.md) ++ [Structure](structure.md) ++ [Database MySQL](database_mysql.md) diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md index 4cf9b94c1a075a64647fcc3ca0490b09109724a5..bf8183729e7fb689e3fcdba8ddc8a40f9e9ae6e7 100644 --- a/doc/install/database_mysql.md +++ b/doc/install/database_mysql.md @@ -6,6 +6,9 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se # Install the database packages 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 # 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 # change $password in the command below to a real password you pick 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 mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; diff --git a/doc/install/installation.md b/doc/install/installation.md index a0eac76ab5b5228df1b241bb982d3fed65c75e75..addb21b50e0d7b0788f12b265bb8fef09b1d9df3 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -128,7 +128,7 @@ GitLab Shell is an ssh access and repository management software developed speci cd /home/git # 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 @@ -173,7 +173,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ## Clone the Source # 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 cd /home/git/gitlab diff --git a/doc/integration/external-issue-tracker.md b/doc/integration/external-issue-tracker.md index 02eadfd410acd18242fbc80cf9baebeebca547b5..3212ebd64b5feb831007f11b3e18163770404e58 100644 --- a/doc/integration/external-issue-tracker.md +++ b/doc/integration/external-issue-tracker.md @@ -5,3 +5,5 @@ GitLab has a great issue tracker but you can also use an external issue tracker - textual references to PROJECT-1234 in comments, commit messages get turned into HTML links to the corresponding JIRA issue.  + +You can configure the integration in the gitlab.yml configuration file. diff --git a/doc/legal/README.md b/doc/legal/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ebfdad13540845b82bc6330f7f313ac68272c7c5 --- /dev/null +++ b/doc/legal/README.md @@ -0,0 +1,2 @@ ++ [Corporate contributor license agreement](corporate_contributor_license_agreement.md) ++ [Individual contributor license agreement](individual_contributor_license_agreement.md) diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md index 73e1728a55983ab23d3bdf98558a78c63c47a0f4..ac4bdefddd502a043f40603dfd39b2e2a8bbfb91 100644 --- a/doc/permissions/permissions.md +++ b/doc/permissions/permissions.md @@ -38,7 +38,7 @@ If a user is a GitLab administrator they receive all permissions. |------|-----|--------|---------|------|-----| |Browse group|✓|✓|✓|✓|✓| |Edit group|||||✓| -|create project in group|||||✓| +|Create project in group|||||✓| |Manage group members|||||✓| |Remove group|||||✓| diff --git a/doc/raketasks/README.md b/doc/raketasks/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9aa80af12cc6a69644e2f086989a5dbb3ff78bf5 --- /dev/null +++ b/doc/raketasks/README.md @@ -0,0 +1,6 @@ ++ [Backup restore](backup_restore.md) ++ [Cleanup](cleanup.md) ++ [Features](features.md) ++ [Maintenance](maintenance.md) ++ [User management](user_management.md) ++ [Web hooks](web_hooks.md) diff --git a/doc/release/README.md b/doc/release/README.md new file mode 100644 index 0000000000000000000000000000000000000000..22510be3f18023ada89aa8e546ee50455cdd4a24 --- /dev/null +++ b/doc/release/README.md @@ -0,0 +1,2 @@ ++ [Monthly](monthly.md) ++ [Security](security.md) diff --git a/doc/release/monthly.md b/doc/release/monthly.md index 28fc260b334d9f806cb197fc0361225dbd73b122..08149b4da862a9980d34b19f50592f7fe06549c3 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -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: -* 17th: feature freeze (stop merging new features in master) -* 18th: UI freeze (stop merging changes to the user interface) -* 19th: code freeze (stop merging non-essential code improvements) -* 20th: release candidate 1 (VERSION x.x.0.rc1, tag and tweet about x.x.0.rc1) -* 21st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems) +* 1-7th: official merge window (see contributing guide) +* 8-14th: work on bugfixes, sponsored features and GitLab EE +* 15th: code freeze (stop merging into master except essential bugfixes) +* 18th: release candidate 1 (VERSION x.x.0.rc1, tag and tweet about x.x.0.rc1, release on GitLab Cloud) +* 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) * 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 -* 1-7th: official merge window (see contributing guide) -* 8-16th: bugfixes and sponsored features +* 24-end of month: release GitLab EE and GitLab CI # Write a blog post * 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. -* 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 +* 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. diff --git a/doc/security/README.md b/doc/security/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f8dd1291b9bf486e982b7e940e58b81ca559e4da --- /dev/null +++ b/doc/security/README.md @@ -0,0 +1,2 @@ ++ [Password length limits](password_length_limits.md) ++ [Rack attack](rack_attack.md) diff --git a/doc/ssh/deploy_keys.md b/doc/ssh/deploy_keys.md new file mode 100644 index 0000000000000000000000000000000000000000..56fe98bb101b01a5cc4b303f199f1c38ede0fb08 --- /dev/null +++ b/doc/ssh/deploy_keys.md @@ -0,0 +1,12 @@ +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. diff --git a/doc/update/4.2-to-5.0.md b/doc/update/4.2-to-5.0.md index 5bf8c36773465879a7c155b77460e482ec82df7f..f8fb607e0169b6c664e787594aa666cbe5e6ea1d 100644 --- a/doc/update/4.2-to-5.0.md +++ b/doc/update/4.2-to-5.0.md @@ -1,7 +1,7 @@ # From 4.2 to 5.0 ## 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 diff --git a/doc/update/5.0-to-5.1.md b/doc/update/5.0-to-5.1.md index 24d96e43bade42d6f42fca7ffe1d24a0daa2c009..ba56507dd816c5dd7607eeeaa1d6afeec79de69f 100644 --- a/doc/update/5.0-to-5.1.md +++ b/doc/update/5.0-to-5.1.md @@ -1,7 +1,7 @@ # From 5.0 to 5.1 ## 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: diff --git a/doc/update/5.1-to-5.2.md b/doc/update/5.1-to-5.2.md index e4eaee91b8e02d88a4e147609292332e5d21b54e..466c815195f6377132b311448f32e5fe716656b4 100644 --- a/doc/update/5.1-to-5.2.md +++ b/doc/update/5.1-to-5.2.md @@ -1,7 +1,7 @@ # From 5.1 to 5.2 ## 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 diff --git a/doc/update/5.1-to-5.4.md b/doc/update/5.1-to-5.4.md index 39cacd381a3e36c950a09a0fb324727176bf14a4..56f4854daf016adf34887723bafb6121b8884df1 100644 --- a/doc/update/5.1-to-5.4.md +++ b/doc/update/5.1-to-5.4.md @@ -1,9 +1,6 @@ # From 5.1 to 5.4 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 It's useful to make a backup just in case things go south: diff --git a/doc/update/5.1-to-6.0.md b/doc/update/5.1-to-6.0.md index fa0f9ce54b626dc61b765672a4619402924a3b48..5b74b1f893d93bd097fccd43f57003d9c3dd79a4 100644 --- a/doc/update/5.1-to-6.0.md +++ b/doc/update/5.1-to-6.0.md @@ -1,7 +1,7 @@ # From 5.1 to 6.0 ## 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 diff --git a/doc/update/5.2-to-5.3.md b/doc/update/5.2-to-5.3.md index 7f89f6bf8873904acad38c475493dc0ed0ef2bd5..e3c3016db64b514359b73645cde008f19156b3fc 100644 --- a/doc/update/5.2-to-5.3.md +++ b/doc/update/5.2-to-5.3.md @@ -1,7 +1,7 @@ # From 5.2 to 5.3 ## 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 diff --git a/doc/update/5.3-to-5.4.md b/doc/update/5.3-to-5.4.md index 7a24c11c2235e56f41467dfdd560e9b69dc84258..213ce77ec38dcec5117f3ca775b0872e826b34a6 100644 --- a/doc/update/5.3-to-5.4.md +++ b/doc/update/5.3-to-5.4.md @@ -1,8 +1,5 @@ # 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 It's useful to make a backup just in case things go south: diff --git a/doc/update/5.4-to-6.0.md b/doc/update/5.4-to-6.0.md index 312e9a4608093a6e4e8c702007f698ee90414764..c289fcb57fdec1291953aa7a5b71d8194ec86d9e 100644 --- a/doc/update/5.4-to-6.0.md +++ b/doc/update/5.4-to-6.0.md @@ -1,7 +1,7 @@ # From 5.4 to 6.0 ## 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 diff --git a/doc/update/6.0-to-6.1.md b/doc/update/6.0-to-6.1.md index b7fd76348599cd9854f588eced1363f311c04ec5..b4cc9203587bdfe561fae5bec6f43728c1141d5f 100644 --- a/doc/update/6.0-to-6.1.md +++ b/doc/update/6.0-to-6.1.md @@ -1,7 +1,7 @@ # From 6.0 to 6.1 ## 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. # You should update to 6.0 before installing 6.1 so all the necessary conversions are run. diff --git a/doc/update/6.0-to-6.5.md b/doc/update/6.0-to-6.7.md similarity index 84% rename from doc/update/6.0-to-6.5.md rename to doc/update/6.0-to-6.7.md index 4a23fbfdd94371eae5c873668b5aa1dfacab3ec1..5023e34f189486fe3b07501c2ff4a6f7bd1306bf 100644 --- a/doc/update/6.0-to-6.5.md +++ b/doc/update/6.0-to-6.7.md @@ -1,4 +1,4 @@ -# From 6.0 to 6.5 +# From 6.0 to 6.7 # 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. @@ -28,8 +28,20 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production ```bash cd /home/git/gitlab 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 ```bash cd /home/git/gitlab-shell 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. @@ -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 +# Run database migrations 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 assets:clean RAILS_ENV=production -sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production -sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production + +# Clean up assets and cache +sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production ``` ### 6. Update config files @@ -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: ``` -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/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/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-7-stable/config/unicorn.rb.example but with your settings. * Copy rack attack middleware config ```bash diff --git a/doc/update/6.1-to-6.2.md b/doc/update/6.1-to-6.2.md index 3c453a5ab42739d79be1ecbfd3795a39f693b2f1..c618e599dcb6713d41e2342f55375f74ac31cb70 100644 --- a/doc/update/6.1-to-6.2.md +++ b/doc/update/6.1-to-6.2.md @@ -1,8 +1,5 @@ # 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. ### 0. Backup diff --git a/doc/update/6.6-to-6.7.md b/doc/update/6.6-to-6.7.md index e4a0cafa9110a5f1c399b349344013e82232b777..8a16e5d67be37d033ebaf07f2f94e5b484a1de2c 100644 --- a/doc/update/6.6-to-6.7.md +++ b/doc/update/6.6-to-6.7.md @@ -37,7 +37,7 @@ sudo -u git -H git checkout 6-7-stable-ee ```bash cd /home/git/gitlab-shell 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. diff --git a/doc/update/README.md b/doc/update/README.md new file mode 100644 index 0000000000000000000000000000000000000000..97afd71bbe30d193321cf86cd605f9df3f8e6734 --- /dev/null +++ b/doc/update/README.md @@ -0,0 +1,5 @@ ++ [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) diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index 64cce45945e3d38bdc8377cf9136167a20eebf88..f80891e264d0ff54d515906e67db2b13410fcd03 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -5,6 +5,7 @@ Project web hooks allow you to trigger an URL if new code is pushed or a new iss 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. 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. --- diff --git a/features/project/service.feature b/features/project/service.feature index 46b983e8f9a36d0312d47357b892dafd3dec915b..a5af065c9e7da2438d0f244208ca7418d516dde9 100644 --- a/features/project/service.feature +++ b/features/project/service.feature @@ -37,6 +37,12 @@ Feature: Project Services And I fill Assembla settings 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 When I visit project "Shop" services page And I click email on push service link diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb index 3526006c94af2bf0e5f0419b2d3b455d92832846..394acd3fe8f14d3632db666501501a664b5146f8 100644 --- a/features/steps/dashboard/dashboard.rb +++ b/features/steps/dashboard/dashboard.rb @@ -25,7 +25,7 @@ class Dashboard < Spinach::FeatureSteps find("#merge_request_target_project_id").value.should == @project.id.to_s find("#merge_request_source_branch").value.should == "new_design" 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 Given 'user with name "John Doe" joined project "Shop"' do diff --git a/features/steps/project/redirects.rb b/features/steps/project/redirects.rb index 76ffea1bb6fae2c475f8f5122cfb1f8347dec480..cfa4ce82be387217375dfbb95b207c3fba353e4f 100644 --- a/features/steps/project/redirects.rb +++ b/features/steps/project/redirects.rb @@ -4,7 +4,7 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps include SharedProject step 'public project "Community"' do - create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC + create :project, :public, name: 'Community' end step 'private project "Enterprise"' do diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb index 54b3f18e0845a2f9503cb0f788c86fca9e1d60a2..0594a08a5e735c87009dcac9b03d8bd7a8516cf8 100644 --- a/features/steps/project/services.rb +++ b/features/steps/project/services.rb @@ -100,4 +100,22 @@ class ProjectServices < Spinach::FeatureSteps step 'I should see email on push service settings saved' do find_field('Recipients').value.should == 'qa@company.name' 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 diff --git a/features/steps/public/projects.rb b/features/steps/public/projects.rb index eb1d235f435e32207ca3d2d6e632dd19e709584e..7c7311bb91c7404ec8afb909a02efb8866578d55 100644 --- a/features/steps/public/projects.rb +++ b/features/steps/public/projects.rb @@ -4,7 +4,7 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps include SharedProject 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 step 'I should see project "Empty Public Project"' do diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index f35beab8af2590e2cf74cc841668b1e0f4183974..f8cb753b78f123fb4948194aaa70701c5c9d0e99 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -79,7 +79,7 @@ module SharedProject end step 'internal project "Internal"' do - create :project, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL + create :project, :internal, name: 'Internal' end step 'I should see project "Internal"' do @@ -91,7 +91,7 @@ module SharedProject end step 'public project "Community"' do - create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC + create :project, :public, name: 'Community' end step 'I should see project "Community"' do @@ -112,14 +112,14 @@ module SharedProject step '"John Doe" is authorized to internal project "Internal"' do user = user_exists("John Doe", username: "john_doe") 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] end step '"John Doe" is authorized to public project "Community"' do user = user_exists("John Doe", username: "john_doe") 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] end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 8b4519af2d1ca9ad621544980f8db39671a3d701..9fa8506926c2b64eb5f738374a3c35397bbd1673 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -187,5 +187,9 @@ module API end end end + + class Label < Grape::Entity + expose :name + end end end diff --git a/lib/api/internal.rb b/lib/api/internal.rb index 69aad3748b37c19b4d503a83d3b2edbeb92ecda7..bcf975746739078e24e5a6728662bcf5f691292c 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -1,16 +1,12 @@ module API # Internal access API class Internal < Grape::API - - DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive } - PUSH_COMMANDS = %w{ git-receive-pack } - namespace 'internal' do - # - # Check if ssh key has access to project code + # Check if git command is allowed to project # # 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 # action - git action (git-upload-pack or git-receive-pack) # ref - branch name @@ -22,43 +18,25 @@ module API # the wiki repository as well. project_path = params[:project] project_path.gsub!(/\.wiki/,'') if project_path =~ /\.wiki/ - - key = Key.find(params[:key_id]) project = Project.find_with_namespace(project_path) - git_cmd = params[:action] return false unless project - - if key.is_a? DeployKey - key.projects.include?(project) && DOWNLOAD_COMMANDS.include?(git_cmd) - else - user = key.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 - - 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 + actor = if params[:key_id] + Key.find(params[:key_id]) + elsif params[:user_id] + User.find(params[:user_id]) + end + + return false unless actor + + Gitlab::GitAccess.new.allowed?( + actor, + params[:action], + project, + params[:ref], + params[:oldrev], + params[:newrev] + ) end # diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index e245819841149c1ae3613fc70ef568a2376666bb..3a1a00d071910993cdeb6b5f96e0d5f6ab03d9e8 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -125,6 +125,22 @@ module API 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 # # Parameters: diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 4d48d2194f8007b75f62af269fb05092fcd897fd..9d290c75ba950be49e93134aec54d84d186ecd79 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -215,6 +215,17 @@ module API @users = paginate @users present @users, with: Entities::User 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 diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index 60c03ce1c04f36f0c6a2bf12467f2b8a5afd402c..c2f3b851c070bc93cd8ae958fb24b5a2e95224ef 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -1,11 +1,9 @@ require_relative 'shell_env' -require_relative 'grack_helpers' module Grack class Auth < Rack::Auth::Basic - include Helpers - attr_accessor :user, :project, :ref, :env + attr_accessor :user, :project, :env def call(env) @env = env @@ -24,14 +22,16 @@ module Grack @env['SCRIPT_NAME'] = "" - auth! + if project + auth! + else + render_not_found + end end private def auth! - return render_not_found unless project - if @auth.provided? return bad_request unless @auth.basic? @@ -40,12 +40,8 @@ module Grack # Allow authentication for GitLab CI service # if valid token passed - if login == "gitlab-ci-token" && project.gitlab_ci? - token = project.gitlab_ci_service.token - - if token.present? && token == password && service_name == 'git-upload-pack' - return @app.call(env) - end + if gitlab_ci_request?(login, password) + return @app.call(env) end @user = authenticate_user(login, password) @@ -53,23 +49,26 @@ module Grack if @user Gitlab::ShellEnv.set_env(@user) @env['REMOTE_USER'] = @auth.username - else - return unauthorized end - - else - return unauthorized unless project.public? end - if authorized_git_request? + if authorized_request? @app.call(env) else unauthorized end end - def authorized_git_request? - authorize_request(service_name) + def gitlab_ci_request?(login, password) + 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 def authenticate_user(login, password) @@ -77,31 +76,31 @@ module Grack auth.find(login, password) end - def authorize_request(service) - case service - when 'git-upload-pack' - can?(user, :download_code, project) - when'git-receive-pack' - refs.each do |ref| - action = if project.protected_branch?(ref) - :push_code_to_protected_branches - else - :push_code - end - - return false unless can?(user, action, project) + def authorized_request? + case git_cmd + when *Gitlab::GitAccess::DOWNLOAD_COMMANDS + if user + Gitlab::GitAccess.new.download_allowed?(user, project) + elsif project.public? + # Allow clone/fetch for public projects + true + else + false + end + when *Gitlab::GitAccess::PUSH_COMMANDS + if user + # Skip user authorization on upload request. + # It will be serverd by update hook in repository + true + else + false 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 false end end - def service_name + def git_cmd if @request.get? @request.params['service'] elsif @request.post? @@ -115,28 +114,17 @@ module Grack @project ||= project_by_path(@request.path_info) end - def refs - @refs ||= parse_refs - end - - 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 + def project_by_path(path) + if m = /^([\w\.\/-]+)\.git/.match(path).to_a + path_with_namespace = m.last + path_with_namespace.gsub!(/\.wiki$/, '') - # Cleanup grabare from refs - # if push to multiple branches - refs.map do |ref| - ref.gsub(/00.*/, "") + Project.find_with_namespace(path_with_namespace) end end + + def render_not_found + [404, {"Content-Type" => "text/plain"}, ["Not Found"]] + end end end diff --git a/lib/gitlab/backend/grack_helpers.rb b/lib/gitlab/backend/grack_helpers.rb deleted file mode 100644 index cb747fe0137a3c0354d1b487a1c8cb9e1eff1e52..0000000000000000000000000000000000000000 --- a/lib/gitlab/backend/grack_helpers.rb +++ /dev/null @@ -1,28 +0,0 @@ -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 diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb new file mode 100644 index 0000000000000000000000000000000000000000..1ab8f9213a3624aa940b15db0e0e12ffb5b1c215 --- /dev/null +++ b/lib/gitlab/git_access.rb @@ -0,0 +1,74 @@ +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 diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index e72f4f5d0ce268a89f5c8aad5c16c8da8fe328c9..de14a3eca275611cf7ada45224116049f31bf8e7 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -152,7 +152,7 @@ module Gitlab # # Returns boolean def valid_emoji?(emoji) - Emoji.names.include? emoji + Emoji.find_by_name emoji end # Private: Dispatches to a dedicated processing method based on reference @@ -166,8 +166,8 @@ module Gitlab end def reference_user(identifier) - if member = @project.team_members.find { |user| user.username == identifier } - link_to("@#{identifier}", user_url(identifier), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member + if user = User.find_by_username(identifier) + link_to("@#{identifier}", user_url(identifier), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) end end diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb index bcf3012bd9242db7e060c1c3bfac69956d2aabc8..bdfcf254e9ef2324025b6ad32b824e1b1d52497a 100644 --- a/lib/gitlab/satellite/satellite.rb +++ b/lib/gitlab/satellite/satellite.rb @@ -1,5 +1,9 @@ module Gitlab - class SatelliteNotExistError < StandardError; end + class SatelliteNotExistError < StandardError + def initialize(msg = "Satellite doesn't exist") + super + end + end module Satellite class Satellite @@ -17,14 +21,9 @@ module Gitlab Gitlab::Satellite::Logger.error(message) end - def raise_no_satellite - raise SatelliteNotExistError.new("Satellite doesn't exist") - end - def clear_and_update! - raise_no_satellite unless exists? + raise SatelliteNotExistError unless exists? - File.exists? path @repo = nil clear_working_dir! delete_heads! @@ -55,7 +54,7 @@ module Gitlab # * Changes the current directory to the satellite's working dir # * Yields def lock - raise_no_satellite unless exists? + raise SatelliteNotExistError unless exists? File.open(lock_file, "w+") do |f| begin @@ -77,7 +76,7 @@ module Gitlab end def repo - raise_no_satellite unless exists? + raise SatelliteNotExistError unless exists? @repo ||= Grit::Repo.new(path) end diff --git a/lib/gitlab/upgrader.rb b/lib/gitlab/upgrader.rb index 0fe4888665d73ddf0ff6e377b07c8ed7cd73512d..0846359f9b14e221b7f4156a1cc9eea881b489e3 100644 --- a/lib/gitlab/upgrader.rb +++ b/lib/gitlab/upgrader.rb @@ -1,3 +1,4 @@ +require_relative "popen" require_relative "version_info" module Gitlab diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index c6e570784e07f4aafa01254e6f7bd163ddfeda6c..3dd4465a6d8a0d5b0843ea4b2c8a0865d85483f3 100755 --- a/lib/support/init.d/gitlab +++ b/lib/support/init.d/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. if [ "$USER" != "$app_user" ]; then - sudo -u "$app_user" -H -i $0 "$@"; exit; + eval su - "$app_user" -c $(echo \")$0 "$@"$(echo \"); exit; fi # Switch to the gitlab path, exit on failure. @@ -131,7 +131,7 @@ check_stale_pids(){ fi fi 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 echo "Unable to remove stale pid, exiting" exit 1 @@ -149,15 +149,15 @@ exit_if_not_running(){ } ## Starts Unicorn and Sidekiq if they're not running. -start() { +start_gitlab() { check_stale_pids if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then echo -n "Starting both the GitLab Unicorn and Sidekiq" elif [ "$web_status" != "0" ]; then - echo -n "Starting GitLab Sidekiq" - elif [ "$sidekiq_status" != "0" ]; then echo -n "Starting GitLab Unicorn" + elif [ "$sidekiq_status" != "0" ]; then + echo -n "Starting GitLab Sidekiq" fi # Then check if the service is running. If it is: don't start again. @@ -167,7 +167,7 @@ start() { # Remove old socket if it exists rm -f "$socket_path"/gitlab.socket 2>/dev/null # Start the web server - RAILS_ENV=$RAILS_ENV script/web start & + RAILS_ENV=$RAILS_ENV script/web start fi # If sidekiq is already running, don't start it again. @@ -184,15 +184,15 @@ start() { } ## 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 if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then echo -n "Shutting down both Unicorn and Sidekiq" elif [ "$web_status" = "0" ]; then - echo -n "Shutting down Sidekiq" - elif [ "$sidekiq_status" = "0" ]; then echo -n "Shutting down Unicorn" + elif [ "$sidekiq_status" = "0" ]; then + echo -n "Shutting down Sidekiq" fi # If the Unicorn web server is running, tell it to stop; @@ -246,7 +246,7 @@ print_status() { } ## Tells unicorn to reload it's config and Sidekiq to restart -reload(){ +reload_gitlab(){ exit_if_not_running if [ "$wpid" = "0" ];then echo "The GitLab Unicorn Web server is not running thus its configuration can't be reloaded." @@ -263,12 +263,12 @@ reload(){ } ## Restarts Sidekiq and Unicorn. -restart(){ +restart_gitlab(){ check_status if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then - stop + stop_gitlab fi - start + start_gitlab } @@ -276,16 +276,16 @@ restart(){ case "$1" in start) - start + start_gitlab ;; stop) - stop + stop_gitlab ;; restart) - restart + restart_gitlab ;; reload|force-reload) - reload + reload_gitlab ;; status) print_status diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 7a0f3efbb531cad84e3ef441b9fb9cd0cad91e5d..5bff362da0e64c5357a393b67381d4dfb5f35f86 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -54,6 +54,14 @@ server { 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; } diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index d4d5e48ce3f47294134ed97faa509eb8dc0e92cd..3b9b2531bf799b0fbd4b2c4bfb423d68cf2b55b4 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -677,7 +677,20 @@ namespace :gitlab do end 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 def ldap @@ -742,7 +755,7 @@ namespace :gitlab do end 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) print "GitLab Shell version >= #{required_version} ? ... " diff --git a/script/background_jobs b/script/background_jobs index 06125c11ffeb6cef0541afba4461e9790f3e14e9..52732f5532be65f1894069bd1116a49ea78d810a 100755 --- a/script/background_jobs +++ b/script/background_jobs @@ -6,6 +6,11 @@ sidekiq_pidfile="$app_root/tmp/pids/sidekiq.pid" sidekiq_logfile="$app_root/log/sidekiq.log" gitlab_user=$(ls -l config.ru | awk '{print $3}') +function warn +{ + echo "$@" 1>&2 +} + function stop { bundle exec sidekiqctl stop $sidekiq_pidfile >> $sidekiq_logfile 2>&1 @@ -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 } +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 stop) stop @@ -51,6 +72,9 @@ case "$1" in 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 diff --git a/spec/controllers/profile_keys_controller_spec.rb b/spec/controllers/profile_keys_controller_spec.rb index 121012d5d499cb935636b88d9c40d261c4ca6826..593d3e9eb56af120129125ae213e2e668838857c 100644 --- a/spec/controllers/profile_keys_controller_spec.rb +++ b/spec/controllers/profile_keys_controller_spec.rb @@ -24,6 +24,11 @@ describe Profiles::KeysController do expect(response.body).to eq("") 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 describe "user with keys" do @@ -44,6 +49,11 @@ describe Profiles::KeysController do expect(response.body).not_to eq("") expect(response.body).to eq(user.all_ssh_keys.join("\n")) 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 diff --git a/spec/factories.rb b/spec/factories.rb index 7fc2b7c5e97864a1077d76e0a420303d9819f506..148477d6389017fc7c19a36b0b816f986d3bed35 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -32,6 +32,18 @@ FactoryGirl.define do path { name.downcase.gsub(/\s/, '_') } namespace 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 # Generates a test repository from the repository stored under `spec/seed_project.tar.gz`. @@ -146,6 +158,11 @@ FactoryGirl.define do state :reopened end + trait :simple do + source_branch "simple_merge_request" + target_branch "master" + end + factory :closed_merge_request, traits: [:closed] factory :reopened_merge_request, traits: [:reopened] factory :merge_request_with_diffs, traits: [:with_diffs] @@ -161,7 +178,6 @@ FactoryGirl.define do factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note] 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_with_attachment, traits: [:on_merge_request, :with_attachment] trait :on_commit do project factory: :project diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index a3d8c462bf63e7f400df0ecb13c0152c73fcc16d..25a86b11fa9d9a3cdb0b50b2d700e3b0def0db2a 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -1,14 +1,12 @@ require 'spec_helper' describe "On a merge request", js: true do - let!(:project) { create(:project) } - let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } - let!(:note) { create(:note_on_merge_request_with_attachment, project: project) } + let!(:merge_request) { create(:merge_request, :simple) } + let!(:project) { merge_request.source_project } + let!(:note) { create(:note_on_merge_request, :with_attachment, project: project) } before do - login_as :user - project.team << [@user, :master] - + login_as :admin visit project_merge_request_path(project, merge_request) end @@ -134,22 +132,20 @@ describe "On a merge request", js: true do end end -describe "On a merge request diff", js: true, focus: true do - let!(:project) { create(:project) } - let!(:merge_request) { create(:merge_request_with_diffs, source_project: project, target_project: project) } +describe "On a merge request diff", js: true do + let(:merge_request) { create(:merge_request, :with_diffs, :simple) } + let(:project) { merge_request.source_project } before do - login_as :user - project.team << [@user, :master] + login_as :admin visit diffs_project_merge_request_path(project, merge_request) end - subject { page } describe "when adding a note" do before do - find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185"]').click + find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7"]').click end describe "the notes holder" do @@ -160,13 +156,13 @@ describe "On a merge request diff", js: true, focus: true do describe "the note form" 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 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") end @@ -176,12 +172,9 @@ describe "On a merge request diff", js: true, focus: true do end 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 - find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185"]').click - find('a[data-line-code="342e16cbbd482ac2047dc679b2749d248cc1428f_18_17"]').click + find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7"]').click + find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_10_10"]').click end 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 describe "previewing them separately" do before do # add two separate texts and trigger previews on both - within("tr[id='4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185'] + .js-temp-notes-holder") do - fill_in "note[note]", with: "One comment on line 185" + within("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7'] + .js-temp-notes-holder") do + fill_in "note[note]", with: "One comment on line 7" find(".js-note-preview-button").trigger("click") end - within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do - fill_in "note[note]", with: "Another comment on line 17" + within("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_10_10'] + .js-temp-notes-holder") do + fill_in "note[note]", with: "Another comment on line 10" find(".js-note-preview-button").trigger("click") end end @@ -202,14 +195,14 @@ describe "On a merge request diff", js: true, focus: true do describe "posting a note" do before do - within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do - fill_in "note[note]", with: "Another comment on line 17" + within("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_10_10'] + .js-temp-notes-holder") do + fill_in "note[note]", with: "Another comment on line 10" click_button("Add Comment") end end 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 .note", count: 1) should have_link("Reply") diff --git a/spec/features/security/group/internal_group_access_spec.rb b/spec/features/security/group/internal_group_access_spec.rb index 0c354f02456de06f20b2e2f87128a40f40c78f1c..79a6aee41b58de6c0b7f313f167ec3012dfb21cf 100644 --- a/spec/features/security/group/internal_group_access_spec.rb +++ b/spec/features/security/group/internal_group_access_spec.rb @@ -16,7 +16,7 @@ describe "Group with internal project access" do group.add_user(reporter, Gitlab::Access::REPORTER) group.add_user(guest, Gitlab::Access::GUEST) - create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) + create(:project, :internal, group: group) end describe "GET /groups/:path" do diff --git a/spec/features/security/group/mixed_group_access_spec.rb b/spec/features/security/group/mixed_group_access_spec.rb index 82e816e388a5d2345de248f5b7d382851fcc1fab..028cd32d2bbf1d50a94435e410c9b60d7e075962 100644 --- a/spec/features/security/group/mixed_group_access_spec.rb +++ b/spec/features/security/group/mixed_group_access_spec.rb @@ -16,8 +16,8 @@ describe "Group access" do group.add_user(reporter, Gitlab::Access::REPORTER) group.add_user(guest, Gitlab::Access::GUEST) - create(:project, path: "internal_project", group: group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) - create(:project, path: "public_project", group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) + create(:project, :internal, path: "internal_project", group: group) + create(:project, :public, path: "public_project", group: group) end describe "GET /groups/:path" do diff --git a/spec/features/security/group/public_group_access_spec.rb b/spec/features/security/group/public_group_access_spec.rb index a9c0afbb60f2d4169caf9bb21cc19c6c419a7a58..f0ed7649eb5b74e68721807a2170542a9d97d631 100644 --- a/spec/features/security/group/public_group_access_spec.rb +++ b/spec/features/security/group/public_group_access_spec.rb @@ -16,7 +16,7 @@ describe "Group with public project access" do group.add_user(reporter, Gitlab::Access::REPORTER) group.add_user(guest, Gitlab::Access::GUEST) - create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) + create(:project, :public, group: group) end describe "GET /groups/:path" do diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index 8bb1e259efa07a29ec680b0ef6be3ef0c9b4e301..152cf66dcfda57c3ff9ea09800982c639f23858a 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -1,23 +1,18 @@ require 'spec_helper' describe "Internal Project Access" do - let(:project) { create(:project) } + let(:project) { create(:project, :internal) } let(:master) { create(:user) } let(:guest) { create(:user) } let(:reporter) { create(:user) } before do - # internal project - project.visibility_level = Gitlab::VisibilityLevel::INTERNAL - project.save! - # full access project.team << [master, :master] # readonly project.team << [reporter, :reporter] - end describe "Project should be internal" do diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 76f9e753dd27838ce11ab6d5ad28799ae81eeca3..0bd2ccafcc1255424c5c98c04dba65a4d162ff83 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -1,13 +1,15 @@ require 'spec_helper' describe MergeRequestsFinder do - let(:user) { create :user } + let(:user) { create :user } let(:user2) { create :user } + let(:project1) { 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_request3) { create(:merge_request, author: user, source_project: project2, target_project: project2) } + + let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project1, 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 project1.team << [user, :master] @@ -15,13 +17,7 @@ describe MergeRequestsFinder do project2.team << [user2, :developer] end - describe :execute do - before :each do - merge_request1 - merge_request2 - merge_request3 - end - + describe "#execute" do it 'should filter by scope' do params = { scope: 'authored', state: 'opened' } merge_requests = MergeRequestsFinder.new.execute(user, params) diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index cc6ee82ab7577a79a3359245415a8427f8bf699b..6e3ae4d615b087a7634c8d213fddd191cd0b1a00 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -4,10 +4,10 @@ describe ProjectsFinder do let(:user) { create :user } let(:group) { create :group } - let(:project1) { create(:empty_project, group: group, visibility_level: Project::PUBLIC) } - let(:project2) { create(:empty_project, group: group, visibility_level: Project::INTERNAL) } - let(:project3) { create(:empty_project, group: group, visibility_level: Project::PRIVATE) } - let(:project4) { create(:empty_project, group: group, visibility_level: Project::PRIVATE) } + let(:project1) { create(:empty_project, :public, group: group) } + let(:project2) { create(:empty_project, :internal, group: group) } + let(:project3) { create(:empty_project, :private, group: group) } + let(:project4) { create(:empty_project, :private, group: group) } context 'non authenticated' do subject { ProjectsFinder.new.execute(nil, group: group) } diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index f990ed659b86a8a5fb1f98c39daeb6376426c587..22d60429ccd636f495a750e55aa6cf3fde1711ea 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -229,6 +229,7 @@ describe Notify do end 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_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 it 'contains a link to the merge request' do should have_body_text /#{project_merge_request_path project, merge_request}/ 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 diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 0827e4f162b2238f345ab16e521d2c3eb03e8ddd..9cbc899067689d4f92056367995f8e92d9cc92df 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -25,11 +25,6 @@ describe Issue, "Issuable" do it { described_class.should respond_to(:assigned) } 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 let!(:searchable_issue) { create(:issue, title: "Searchable issue") } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 6bae5951b7b2113ddcdd6ef5591ed50fe22a8ada..839350bafbfa2208360c8285f32c7f475359dc5d 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -47,6 +47,7 @@ describe Project do it { should have_many(:hooks).dependent(:destroy) } it { should have_many(:protected_branches).dependent(:destroy) } it { should have_one(:forked_project_link).dependent(:destroy) } + it { should have_one(:slack_service).dependent(:destroy) } end describe "Mass assignment" do diff --git a/spec/models/slack_message_spec.rb b/spec/models/slack_message_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..b39cd4edf820279d3c2fac76e775cb33ea044b62 --- /dev/null +++ b/spec/models/slack_message_spec.rb @@ -0,0 +1,56 @@ +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 diff --git a/spec/models/slack_service_spec.rb b/spec/models/slack_service_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..387455cb25e01825012187c577fd372328515b7f --- /dev/null +++ b/spec/models/slack_service_spec.rb @@ -0,0 +1,69 @@ +# == 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 diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index fd8d7133ae94d13a53411537798b04890fd5bc3e..fef6314f23abab1ee2770ba3b75026d57be47c6c 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -292,6 +292,20 @@ describe User do 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 let(:user1) { create(:user, username: 'foo') } diff --git a/spec/observers/activity_observer_spec.rb b/spec/observers/activity_observer_spec.rb deleted file mode 100644 index dc14ab86b6d8e5716da8404c922ab290b26df4b1..0000000000000000000000000000000000000000 --- a/spec/observers/activity_observer_spec.rb +++ /dev/null @@ -1,61 +0,0 @@ -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 diff --git a/spec/observers/merge_request_observer_spec.rb b/spec/observers/merge_request_observer_spec.rb index 6ad7c4d81da0c9c738643d22f575240f9865410b..18df8b78513e15abd0d3981e336ece084afa0e4d 100644 --- a/spec/observers/merge_request_observer_spec.rb +++ b/spec/observers/merge_request_observer_spec.rb @@ -120,7 +120,7 @@ describe MergeRequestObserver do end 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 end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index c55025d72b5fe12b50831b0021d22917774f492c..e9422cd26439845f5d8037e4d0200e30c98a5cbf 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -100,16 +100,4 @@ describe API::API do response.status.should == 405 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 diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 1a9e4809e7d627dcad63c99204a83d8d890801e5..138f218d46cd34f281011d37c6b3a35b731f6c11 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -7,6 +7,7 @@ describe API::API do let(:user) { create(:user) } 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!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") } before { project.team << [user, :reporters] } @@ -205,4 +206,20 @@ describe API::API do response.status.should == 404 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 diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index cb30c98b4d22e682bbe25ce6d42c51b10060adea..7fe65639657ad85c193cfa9f593534845bc9ff53 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -13,6 +13,7 @@ describe API::API do 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_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 before { project } @@ -133,7 +134,7 @@ describe API::API do end 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 json_response['public'].should be_true json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC @@ -147,21 +148,21 @@ describe API::API do end 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 json_response['public'].should be_false json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL end 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 json_response['public'].should be_false json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL end 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 json_response['public'].should be_false json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE @@ -215,7 +216,7 @@ describe API::API do end 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 json_response['public'].should be_true json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC @@ -229,21 +230,21 @@ describe API::API do end 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 json_response['public'].should be_false json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL end 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 json_response['public'].should be_false json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL end 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 json_response['public'].should be_false json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE @@ -490,10 +491,10 @@ describe API::API do describe :fork_admin do 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 - 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 post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user) @@ -562,10 +563,10 @@ describe API::API do 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!(: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!(:unfound_internal) { create(:empty_project, name: 'unfound internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL) } - let!(:public) { create(:empty_project, name: "public #{query}", visibility_level: Gitlab::VisibilityLevel::PUBLIC) } - let!(:unfound_public) { create(:empty_project, name: 'unfound public', visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + let!(:internal) { create(:empty_project, :internal, name: "internal #{query}") } + let!(:unfound_internal) { create(:empty_project, :internal, name: 'unfound internal') } + let!(:public) { create(:empty_project, :public, name: "public #{query}") } + let!(:unfound_public) { create(:empty_project, :public, name: 'unfound public') } context "when unauthenticated" do it "should return authentication error" do @@ -632,4 +633,16 @@ describe API::API do 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 diff --git a/spec/seed_project.tar.gz b/spec/seed_project.tar.gz index 92b9587e3f76bd27d26d910c49238a64caf93887..8d32a927da847db0fe30df05c080ada83fb006e4 100644 Binary files a/spec/seed_project.tar.gz and b/spec/seed_project.tar.gz differ diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..713aa3e7e747714b25dc9bb5c8c852c28103fa30 --- /dev/null +++ b/spec/services/event_create_service_spec.rb @@ -0,0 +1,103 @@ +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 diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 077ad8b6e127d563de4bd423a482b8c60441e5a0..fbd73a7086fd5b437a6d51f6c14b9fce06271646 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -32,6 +32,7 @@ describe NotificationService do describe 'Notes' do context 'issue note' do 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') } before do @@ -50,6 +51,13 @@ describe NotificationService do notification.new_note(note) 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) Notify.should_receive(:note_issue_email).with(user_id, note.id) end @@ -233,15 +241,15 @@ describe NotificationService do should_email(@u_watcher.id) should_not_email(@u_participating.id) should_not_email(@u_disabled.id) - notification.merge_mr(merge_request) + notification.merge_mr(merge_request, @u_disabled) end 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 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 diff --git a/spec/services/projects_create_service_spec.rb b/spec/services/projects/create_service_spec.rb similarity index 100% rename from spec/services/projects_create_service_spec.rb rename to spec/services/projects/create_service_spec.rb diff --git a/spec/services/project_transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb similarity index 100% rename from spec/services/project_transfer_service_spec.rb rename to spec/services/projects/transfer_service_spec.rb diff --git a/spec/services/projects_update_service_spec.rb b/spec/services/projects/update_service_spec.rb similarity index 100% rename from spec/services/projects_update_service_spec.rb rename to spec/services/projects/update_service_spec.rb diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index 457cb3c0ca36a958e740c818620c63cdd0306378..b467282a5d66217349cf6805cd77cbbfddf2556d 100644 --- a/spec/services/search_service_spec.rb +++ b/spec/services/search_service_spec.rb @@ -1,28 +1,26 @@ require 'spec_helper' describe 'Search::GlobalService' do - let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') } 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_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(:internal_user) { create(:user, namespace: internal_namespace) } - let!(:internal_project) { create(:project, name: 'searchable_internal_project', creator_id: internal_user.id, namespace: internal_namespace, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } - - 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) } + let!(:found_project) { create(:project, :private, name: 'searchable_project', creator_id: user.id, namespace: found_namespace) } + let!(:unfound_project) { create(:project, :private, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace) } + 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) } describe '#execute' do context 'unauthenticated' do it 'should return public projects only' do context = Search::GlobalService.new(nil, search: "searchable") results = context.execute - results[:projects].should have(1).items - results[:projects].should include(public_project) + results[:projects].should match_array [public_project] end end @@ -30,24 +28,19 @@ describe 'Search::GlobalService' do it 'should return public, internal and private projects' do context = Search::GlobalService.new(user, search: "searchable") results = context.execute - results[:projects].should have(3).items - results[:projects].should include(public_project) - results[:projects].should include(found_project) - results[:projects].should include(internal_project) + results[:projects].should match_array [public_project, found_project, internal_project] end it 'should return only public & internal projects' do context = Search::GlobalService.new(internal_user, search: "searchable") results = context.execute - results[:projects].should have(2).items - results[:projects].should include(internal_project) - results[:projects].should include(public_project) + results[:projects].should match_array [internal_project, public_project] end it 'namespace name should be searchable' do context = Search::GlobalService.new(user, search: "searchable namespace") results = context.execute - results[:projects].should == [found_project] + results[:projects].should match_array [found_project] end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index dd008ed02ad870789a6414af1810b1785f06a946..e6b1f816df037dae3e787abfea7c93b22fe38ca2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,63 +1,50 @@ -require 'rubygems' -require 'spork' +# This file is copied to spec/ when you run 'rails generate rspec:install' +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'] - require 'coveralls' - 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 +if ENV['TRAVIS'] + require 'coveralls' + Coveralls.wear! end -Spork.each_run do - # This code will be run each time you run your specs. +require 'rspec/rails' +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 diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index e2c3f648ccfa0937a803aa3026ad4ff0d998a707..d237f7ad094b65e5168b50dd283e7c480d338319 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -29,7 +29,6 @@ module TestEnv disable_mailer if opts[:mailer] == false setup_stubs - clear_test_repo_dir if opts[:init_repos] == true setup_test_repos(opts) if opts[:repos] == true end @@ -91,7 +90,7 @@ module TestEnv size: 12.45 ) - ActivityObserver.any_instance.stub( + BaseObserver.any_instance.stub( current_user: double("current_user", id: 1) ) end @@ -165,8 +164,7 @@ module TestEnv def clear_test_repo_dir setup_stubs - # Use tmp dir for FS manipulations - repos_path = testing_path() + # Remove tmp/test-git-base-path FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path