diff options
287 files changed, 2876 insertions, 1455 deletions
@@ -1 +1 @@ ---color --drb +--color diff --git a/CHANGELOG b/CHANGELOG index c8bbce73f5a..441f36a5bde 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,7 +9,40 @@ 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) + - Fixes a bug with group member administration (Matt DeTullio) + - Sort tag names using VersionSorter (Robert Speicher) + - Add GFM autocompletion for MergeRequests (Robert Speicher) + - Add webhook when a new tag is pushed (Jeroen van Baarsen) + - Add button for toggling inline comments in diff view + - Add retry feature for repository import + - Reuse the GitLab LDAP connection within each request + - Changed markdown new line behaviour to conform to markdown standards + - Fix global search + - 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) + - Hide mr close button for comment form if merge request was closed or inline comment + - Adds ability to reopen closed merge request + +v 6.6.4 + - Add missing html escape for highlighted code blocks in comments, issues + +v 6.6.3 + - Fix 500 error when edit yourself from admin area + - Hide private groups for public profiles v 6.6.2 - Fix 500 error on branch/tag create or remove via UI diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c1788657b95..d816f8e72a0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,11 +22,14 @@ Issues and merge requests should be in English and contain appropriate language ## Issue tracker -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). Professional [support subscriptions](http://www.gitlab.com/subscription/) and [consulting services](http://www.gitlab.com/consultancy/) are available from [GitLab.com](http://www.gitlab.com/). +To get support for your particular problem please use the channels as detailed in the [getting help section of the readme](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/README.md#getting-help). Professional [support subscriptions](http://www.gitlab.com/subscription/) and [consulting services](http://www.gitlab.com/consultancy/) are available from [GitLab.com](http://www.gitlab.com/). -The [issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) is only for obvious bugs or misbehavior in the latest [stable or development release of GitLab](MAINTENANCE.md). When submitting an issue please conform to the issue submission guidelines listed below. Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue. +The [issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) is only for obvious bugs in the latest [stable or development release of GitLab](MAINTENANCE.md). +If something is missing 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,7 @@ 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. 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 +77,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) @@ -48,7 +48,8 @@ gem "gitlab-linguist", "~> 3.0.0", require: "linguist" # API gem "grape", "~> 0.6.1" -gem "grape-entity", "~> 0.3.0" +# Replace with rubygems when nesteted entities get released +gem "grape-entity", "~> 0.4.1", ref: 'd904381c951e86250c3f44213b349a3dd8e83fb1', git: 'https://github.com/intridea/grape-entity.git' gem 'rack-cors', require: 'rack/cors' # Email validation @@ -131,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" @@ -161,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" @@ -213,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 709f5711916..1a0bce98ac5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,6 +5,15 @@ GIT specs: github-markup (0.7.6) +GIT + remote: https://github.com/intridea/grape-entity.git + revision: d904381c951e86250c3f44213b349a3dd8e83fb1 + ref: d904381c951e86250c3f44213b349a3dd8e83fb1 + specs: + grape-entity (0.4.1) + activesupport + multi_json (>= 1.3.2) + GEM remote: https://rubygems.org/ specs: @@ -119,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) @@ -156,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) @@ -181,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) @@ -206,9 +218,6 @@ GEM rack-accept rack-mount virtus (>= 1.0.0) - grape-entity (0.3.0) - activesupport - multi_json (>= 1.3.2) growl (1.0.3) guard (2.2.4) formatador (>= 0.2.4) @@ -235,7 +244,7 @@ GEM httparty httparty http_parser.rb (0.5.3) - httparty (0.12.0) + httparty (0.13.0) json (~> 1.8) multi_xml (>= 0.5.2) httpauth (0.2.0) @@ -291,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) @@ -462,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) @@ -473,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) @@ -585,18 +595,18 @@ 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) gon (~> 5.0.0) grape (~> 0.6.1) - grape-entity (~> 0.3.0) + grape-entity (~> 0.4.1)! growl guard-rspec guard-spinach @@ -614,6 +624,7 @@ DEPENDENCIES minitest (~> 4.7.0) modernizr (= 2.6.2) mysql2 + nprogress-rails omniauth (~> 1.1.3) omniauth-github omniauth-google-oauth2 @@ -646,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 7055b2af2ca..2266d50b238 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -12,7 +12,7 @@ Below we describe the contributing process to GitLab for two reasons. So that co - Closes invalid issues and merge requests with a comment (duplicates, [feature requests](#feature-requests), [fixed in newer version](#issue-fixed-in-newer-version), [issue report for old version](#issue-report-for-old-version), not a problem in GitLab, etc.) - Assigns appropriate [labels](#how-we-handle-issues) - Asks for feedback from issue reporter/merge request initiator ([invalid issue reports](#improperly-formatted-issue), [format code](#code-format), etc.) -- Asks for feedback from the relevant developer(s) based on the [list of members and their specialities](http://gitlab.org/team/) +- Asks for feedback from the relevant developer(s) based on the [list of members and their specialities](https://www.gitlab.com/core-team/) - Monitors all issues/merge requests for feedback (but especially ones commented on since automatically watching them): - Closes issues with no feedback from the reporter for two weeks - Closes stale merge requests @@ -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 @@ -37,7 +35,7 @@ Below we describe the contributing process to GitLab for two reasons. So that co ## Mentioning people -The most important thing is making sure valid issues receive feedback from the development team. Therefore the priority is mentioning developers that can help on those issue. Please select someone with relevant experience from [GitLab core team](http://gitlab.org/team/). If there is nobody mentioned with that expertise look in the commit history for the affected files to find someone. Avoid mentioning the lead developer, this is the person that is least likely to give a timely response. If the involvement of the lead developer is needed the other core team members will mention this person. +The most important thing is making sure valid issues receive feedback from the development team. Therefore the priority is mentioning developers that can help on those issue. Please select someone with relevant experience from [GitLab core team](https://www.gitlab.com/core-team/). If there is nobody mentioned with that expertise look in the commit history for the affected files to find someone. Avoid mentioning the lead developer, this is the person that is least likely to give a timely response. If the involvement of the lead developer is needed the other core team members will mention this person. ## Workflow labels @@ -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. Things that are wrong but are not a regression compared to older versions of GitLab are considered feature requests and not issues. 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 @@ -89,7 +87,7 @@ Please use ``` to format console output, logs, and code as it's very hard to rea ### Issue fixed in newer version -Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(http://blog.gitlab.org/). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines). +Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://github.com/gitlabhq/gitlabhq/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines). ### Improperly formatted merge request @@ -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 3bffdbe1f3a..7818d40ac55 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,11 @@ ### Resources -* GitLab.org community site: [Homepage](http://gitlab.org) | [Screenshots](http://gitlab.org/screenshots/) | [Blog](http://blog.gitlab.org/) | [Demo](http://demo.gitlabhq.com/users/sign_in) +* [GitLab.com](https://www.gitlab.com/) includes information about [subscriptions](https://www.gitlab.com/subscription/), [consultancy](https://www.gitlab.com/consultancy/), the [community](https://www.gitlab.com/community/) and the [hosted GitLab Cloud](https://www.gitlab.com/cloud/). -* GitLab.com commercial services: [Homepage](http://www.gitlab.com/) | [Subscription](http://www.gitlab.com/subscription/) | [Consultancy](http://www.gitlab.com/consultancy/) | [GitLab Cloud](http://www.gitlab.com/cloud/) | [Blog](http://blog.gitlab.com/) +* [GitLab Enterprise Edition](https://www.gitlab.com/gitlab-ce/) offers additional features that are useful for larger organizations (100+ users). -* [GitLab Enterprise Edition](https://www.gitlab.com/features/) offers additional features that are useful for larger organizations (100+ users). - -* [GitLab CI](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/README.md) is a continuous integration (CI) server that is easy to integrate with GitLab. +* [GitLab CI](https://www.gitlab.com/gitlab-ci/) is a continuous integration (CI) server that is easy to integrate with GitLab. * Unofficial third-party [iPhone app](http://gitlabcontrol.com/) and [Android app](https://play.google.com/store/apps/details?id=com.bd.gitlab&hl=en) for GitLab @@ -73,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 @@ -98,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 @@ -142,7 +135,7 @@ or start each component separately * [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. +* [Feature request 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. @@ -155,12 +148,3 @@ or start each component separately * [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. - - -### Getting in touch - -* [Core team](http://gitlab.org/team/) - -* [Contributors](http://contributors.gitlab.org/) - -* [Community](http://gitlab.org/community/) @@ -1 +1 @@ -6.7.0.pre +6.7.0 diff --git a/vendor/assets/images/authbuttons/github_32.png b/app/assets/images/authbuttons/github_32.png Binary files differindex c56eef05eb9..c56eef05eb9 100644 --- a/vendor/assets/images/authbuttons/github_32.png +++ b/app/assets/images/authbuttons/github_32.png diff --git a/vendor/assets/images/authbuttons/github_64.png b/app/assets/images/authbuttons/github_64.png Binary files differindex 39de55bc796..39de55bc796 100644 --- a/vendor/assets/images/authbuttons/github_64.png +++ b/app/assets/images/authbuttons/github_64.png diff --git a/vendor/assets/images/authbuttons/google_32.png b/app/assets/images/authbuttons/google_32.png Binary files differindex 6225cc9c2d7..6225cc9c2d7 100644 --- a/vendor/assets/images/authbuttons/google_32.png +++ b/app/assets/images/authbuttons/google_32.png diff --git a/vendor/assets/images/authbuttons/google_64.png b/app/assets/images/authbuttons/google_64.png Binary files differindex 4d608f71008..4d608f71008 100644 --- a/vendor/assets/images/authbuttons/google_64.png +++ b/app/assets/images/authbuttons/google_64.png diff --git a/vendor/assets/images/authbuttons/twitter_32.png b/app/assets/images/authbuttons/twitter_32.png Binary files differindex 696eb02484d..696eb02484d 100644 --- a/vendor/assets/images/authbuttons/twitter_32.png +++ b/app/assets/images/authbuttons/twitter_32.png diff --git a/vendor/assets/images/authbuttons/twitter_64.png b/app/assets/images/authbuttons/twitter_64.png Binary files differindex 2893274766f..2893274766f 100644 --- a/vendor/assets/images/authbuttons/twitter_64.png +++ b/app/assets/images/authbuttons/twitter_64.png diff --git a/vendor/assets/images/bg_fallback.png b/app/assets/images/bg_fallback.png Binary files differindex d9066ad7d7b..d9066ad7d7b 100644 --- a/vendor/assets/images/bg_fallback.png +++ b/app/assets/images/bg_fallback.png diff --git a/vendor/assets/images/icon_sprite.png b/app/assets/images/icon_sprite.png Binary files differindex 9ad65fc443b..9ad65fc443b 100644 --- a/vendor/assets/images/icon_sprite.png +++ b/app/assets/images/icon_sprite.png diff --git a/vendor/assets/images/progress_bar.gif b/app/assets/images/progress_bar.gif Binary files differindex c3d43fa40b2..c3d43fa40b2 100644 --- a/vendor/assets/images/progress_bar.gif +++ b/app/assets/images/progress_bar.gif diff --git a/vendor/assets/images/slider_handles.png b/app/assets/images/slider_handles.png Binary files differindex a6d477033fa..a6d477033fa 100644 --- a/vendor/assets/images/slider_handles.png +++ b/app/assets/images/slider_handles.png diff --git a/vendor/assets/images/ui-icons_222222_256x240.png b/app/assets/images/ui-icons_222222_256x240.png Binary files differindex 8bc06cbf03b..8bc06cbf03b 100644 --- a/vendor/assets/images/ui-icons_222222_256x240.png +++ b/app/assets/images/ui-icons_222222_256x240.png diff --git a/vendor/assets/images/ui-icons_454545_256x240.png b/app/assets/images/ui-icons_454545_256x240.png Binary files differindex cfd1eaffaae..cfd1eaffaae 100644 --- a/vendor/assets/images/ui-icons_454545_256x240.png +++ b/app/assets/images/ui-icons_454545_256x240.png diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js deleted file mode 100644 index 4a393dbfe81..00000000000 --- 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 index 69c731ff1a1..5042221abe4 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 @@ -62,11 +88,6 @@ $ -> $('.remove-row').bind 'ajax:success', -> $(this).closest('li').fadeOut() - # Click a .appear-link, appear-data fadeout - $(".appear-link").on 'click', (e) -> - $('.appear-data').fadeIn() - e.preventDefault() - # Initialize select2 selects $('select.select2').select2(width: 'resolve', dropdownAutoWidth: true) @@ -121,10 +142,6 @@ $ -> $(@).next('table').show() $(@).remove() - $(".diff-content").on "click", ".js-details-expand", -> - $(@).next('.js-details-contain').removeClass("hide") - $(@).remove() - (($) -> # Disable an element and add the 'disabled' Bootstrap class $.fn.extend disable: -> diff --git a/app/assets/javascripts/behaviors/details_behavior.coffee b/app/assets/javascripts/behaviors/details_behavior.coffee index 7ad5c818946..decab3e1bed 100644 --- a/app/assets/javascripts/behaviors/details_behavior.coffee +++ b/app/assets/javascripts/behaviors/details_behavior.coffee @@ -1,5 +1,15 @@ $ -> $("body").on "click", ".js-details-target", -> container = $(@).closest(".js-details-container") - container.toggleClass("open") + + # Show details content. Hides link after click. + # + # %div + # %a.js-details-expand + # %div.js-details-content + # + $("body").on "click", ".js-details-expand", (e) -> + $(@).next('.js-details-content').removeClass("hide") + $(@).hide() + e.preventDefault() diff --git a/app/assets/javascripts/behaviors/toggler_behavior.coffee b/app/assets/javascripts/behaviors/toggler_behavior.coffee index 5afb656e696..d06cb116dfe 100644 --- a/app/assets/javascripts/behaviors/toggler_behavior.coffee +++ b/app/assets/javascripts/behaviors/toggler_behavior.coffee @@ -1,17 +1,18 @@ $ -> $("body").on "click", ".js-toggler-target", -> container = $(@).closest(".js-toggler-container") - container.toggleClass("on") - $("body").on "click", ".js-toggle-visibility-link", (e) -> + # Toggle button. Show/hide content inside parent container. + # Button does not change visibility. If button has icon - it changes chevron style. + # + # %div.js-toggle-container + # %a.js-toggle-button + # %div.js-toggle-content + # + $("body").on "click", ".js-toggle-button", (e) -> $(@).find('i'). toggleClass('icon-chevron-down'). toggleClass('icon-chevron-up') - container = $(".js-toggle-visibility-container") - container.toggleClass("hide") - e.preventDefault() - - $("body").on "click", ".js-toggle-button", (e) -> $(@).closest(".js-toggle-container").find(".js-toggle-content").toggle() e.preventDefault() diff --git a/app/assets/javascripts/gfm_auto_complete.js.coffee b/app/assets/javascripts/gfm_auto_complete.js.coffee index dd12000a1cf..00d56ae5b4b 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 33466714681..4b7103010bb 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 6183cefa594..4e660d0b1e0 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -298,10 +298,6 @@ img.emoji { width: 20px; } -.appear-data { - display: none; -} - .chart { overflow: hidden; height: 220px; @@ -359,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/print.scss b/app/assets/stylesheets/print.scss new file mode 100644 index 00000000000..42dbf4d6ef3 --- /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 6fc394e2e2b..e088ef82203 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/events.scss b/app/assets/stylesheets/sections/events.scss index 6ef12b20a5a..fe7ab426e8d 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; diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss index f8da4f0f87b..06709bd7ef6 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 f39a9341060..a65771974c6 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -189,7 +189,6 @@ ul.notes { } - /** * Line note button on the side of diffs */ diff --git a/app/controllers/admin/background_jobs_controller.rb b/app/controllers/admin/background_jobs_controller.rb index f2b8277efea..4c1d0df4110 100644 --- a/app/controllers/admin/background_jobs_controller.rb +++ b/app/controllers/admin/background_jobs_controller.rb @@ -1,6 +1,6 @@ class Admin::BackgroundJobsController < Admin::ApplicationController def show - ps_output, _ = Gitlab::Popen.popen(%W(ps -U #{Settings.gitlab.user} -o euser,pid,pcpu,pmem,stat,start,command)) + ps_output, _ = Gitlab::Popen.popen(%W(ps -U #{Settings.gitlab.user} -o pid,pcpu,pmem,stat,start,command)) @sidekiq_processes = ps_output.split("\n").grep(/sidekiq/) end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9ed46c23942..5f8b2da06f8 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base before_filter :check_password_expiration around_filter :set_current_user_for_thread before_filter :add_abilities + before_filter :ldap_security_check before_filter :dev_tools if Rails.env == 'development' before_filter :default_headers before_filter :add_gon_variables @@ -179,11 +180,30 @@ class ApplicationController < ActionController::Base end end + def ldap_security_check + if current_user && current_user.requires_ldap_check? + gitlab_ldap_access do |access| + if access.allowed?(current_user) + current_user.last_credential_check_at = Time.now + current_user.save + else + sign_out current_user + flash[:alert] = "Access denied for your LDAP account." + redirect_to new_user_session_path + end + end + end + end + def event_filter filters = cookies['event_filter'].split(',') if cookies['event_filter'].present? @event_filter ||= EventFilter.new(filters) end + def gitlab_ldap_access(&block) + Gitlab::LDAP::Access.open { |access| block.call(access) } + end + # JSON for infinite scroll via Pager object def pager_json(partial, count) html = render_to_string( diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index a74e97ac253..233b91680f6 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -22,6 +22,8 @@ class DashboardController < ApplicationController @last_push = current_user.recent_push + @publicish_project_count = Project.publicish(current_user).count + respond_to do |format| format.html format.json { pager_json("events/_events", @events.count) } diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb new file mode 100644 index 00000000000..988ede3007b --- /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 b4f14e649e2..6713cd7c8c7 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_controller.rb b/app/controllers/projects_controller.rb index f1c0336e6ea..e181a0ec7fa 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -5,7 +5,7 @@ class ProjectsController < ApplicationController # Authorize before_filter :authorize_read_project!, except: [:index, :new, :create] - before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive] + before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive, :retry_import] before_filter :require_non_empty_project, only: [:blob, :tree, :graph] layout 'navless', only: [:new, :create, :fork] @@ -21,16 +21,9 @@ class ProjectsController < ApplicationController def create @project = ::Projects::CreateService.new(current_user, params[:project]).execute + flash[:notice] = 'Project was successfully created.' if @project.saved? respond_to do |format| - flash[:notice] = 'Project was successfully created.' if @project.saved? - format.html do - if @project.saved? - redirect_to @project - else - render "new" - end - end format.js end end @@ -55,6 +48,11 @@ class ProjectsController < ApplicationController end def show + if @project.import_in_progress? + redirect_to import_project_path(@project) + return + end + return authenticate_user! unless @project.public? || current_user limit = (params[:limit] || 20).to_i @@ -67,9 +65,7 @@ class ProjectsController < ApplicationController if @project.empty_repo? render "projects/empty", layout: user_layout else - if current_user - @last_push = current_user.recent_push(@project.id) - end + @last_push = current_user.recent_push(@project.id) if current_user render :show, layout: user_layout end end @@ -77,6 +73,28 @@ class ProjectsController < ApplicationController end end + def import + if project.import_finished? + redirect_to @project + return + end + end + + def retry_import + unless @project.import_failed? + redirect_to import_project_path(@project) + end + + @project.import_url = params[:project][:import_url] + + if @project.save + @project.reload + @project.import_retry + end + + redirect_to import_project_path(@project) + end + def destroy return access_denied! unless can?(current_user, :remove_project, project) @@ -106,7 +124,7 @@ class ProjectsController < ApplicationController def autocomplete_sources @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 } } diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index c1648d6c387..8df84e9884a 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -7,6 +7,7 @@ class SearchController < ApplicationController if @project return access_denied! unless can?(current_user, :download_code, @project) + @search_results = Search::ProjectService.new(@project, current_user, params).execute else @search_results = Search::GlobalService.new(current_user, params).execute diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index e54a968326f..0dd941a48e2 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -19,6 +19,9 @@ class SnippetsController < ApplicationController def user_index @user = User.find_by(username: params[:username]) + + render_404 and return unless @user + @snippets = @user.snippets.fresh.non_expired if @user == current_user diff --git a/app/finders/base_finder.rb b/app/finders/base_finder.rb index d20716fb170..7fc5840561c 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/application_helper.rb b/app/helpers/application_helper.rb index 90b05027155..faecde299c1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -146,8 +146,7 @@ module ApplicationHelper def authbutton(provider, size = 64) file_name = "#{provider.to_s.split('_').first}_#{size}.png" - image_tag("authbuttons/#{file_name}", - alt: "Sign in with #{provider.to_s.titleize}") + image_tag(image_path("authbuttons/#{file_name}"), alt: "Sign in with #{provider.to_s.titleize}") end def simple_sanitize(str) diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 6cad5e4658e..69425bc171d 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -35,7 +35,6 @@ module GitlabMarkdownHelper # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch- filter_html: true, with_toc_data: true, - hard_wrap: true, safe_links_only: true }.merge(options)) @markdown = Redcarpet::Markdown.new(gitlab_renderer, @@ -45,7 +44,7 @@ module GitlabMarkdownHelper fenced_code_blocks: true, autolink: true, strikethrough: true, - lax_html_blocks: true, + lax_spacing: true, space_after_headers: true, superscript: true) end @@ -64,8 +63,7 @@ module GitlabMarkdownHelper # project_path_with_namespace - namespace/projectname, eg. gitlabhq/gitlabhq # ref - name of the branch or reference, eg. stable # requested_path - path of request, eg. doc/api/README.md, used in special case when path is pointing to the .md file were the original request is coming from - # wiki - whether the markdown is from wiki or not - def create_relative_links(text, project, ref, requested_path, wiki = false) + def create_relative_links(text, project, ref, requested_path) @path_to_satellite = project.satellite.path project_path_with_namespace = project.path_with_namespace paths = extract_paths(text) @@ -135,12 +133,12 @@ module GitlabMarkdownHelper end # Checks if the path exists in the repo - # eg. checks if doc/README.md exists, if it doesn't then it is a wiki link + # eg. checks if doc/README.md exists, if not then link to blob def path_with_ref(path, ref) if file_exists?(path) "#{local_path(path)}/#{correct_ref(ref)}" else - "wikis" + "blob/#{correct_ref(ref)}" end end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 1381b0220d6..ba25a87f392 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/profile_helper.rb b/app/helpers/profile_helper.rb index dd9e03d95a8..297ae83d895 100644 --- a/app/helpers/profile_helper.rb +++ b/app/helpers/profile_helper.rb @@ -10,7 +10,7 @@ module ProfileHelper end def show_profile_social_tab? - Gitlab.config.omniauth.enabled && !current_user.ldap_user? + enabled_social_providers.any? && !current_user.ldap_user? end def show_profile_remove_tab? diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 2dbc1cffb16..50501dffefb 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/groups.rb b/app/mailers/emails/groups.rb index 1c8ae122c46..1654fc55bca 100644 --- a/app/mailers/emails/groups.rb +++ b/app/mailers/emails/groups.rb @@ -3,7 +3,7 @@ module Emails def group_access_granted_email(user_group_id) @membership = UsersGroup.find(user_group_id) @group = @membership.group - + @target_url = group_url(@group) mail(to: @membership.user.email, subject: subject("Access to group was granted")) end diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb index 3adb47dc5b1..d684e354452 100644 --- a/app/mailers/emails/issues.rb +++ b/app/mailers/emails/issues.rb @@ -3,6 +3,7 @@ module Emails def new_issue_email(recipient_id, issue_id) @issue = Issue.find(issue_id) @project = @issue.project + @target_url = project_issue_url(@project, @issue) mail(from: sender(@issue.author_id), to: recipient(recipient_id), subject: subject("#{@issue.title} (##{@issue.iid})")) @@ -12,6 +13,7 @@ module Emails @issue = Issue.find(issue_id) @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id @project = @issue.project + @target_url = project_issue_url(@project, @issue) mail(from: sender(updated_by_user_id), to: recipient(recipient_id), subject: subject("#{@issue.title} (##{@issue.iid})")) @@ -21,6 +23,7 @@ module Emails @issue = Issue.find issue_id @project = @issue.project @updated_by = User.find updated_by_user_id + @target_url = project_issue_url(@project, @issue) mail(from: sender(updated_by_user_id), to: recipient(recipient_id), subject: subject("#{@issue.title} (##{@issue.iid})")) @@ -31,6 +34,7 @@ module Emails @issue_status = status @project = @issue.project @updated_by = User.find updated_by_user_id + @target_url = project_issue_url(@project, @issue) mail(from: sender(updated_by_user_id), to: recipient(recipient_id), subject: subject("#{@issue.title} (##{@issue.iid})")) diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb index 0845e14edc7..a97d55f1b50 100644 --- a/app/mailers/emails/merge_requests.rb +++ b/app/mailers/emails/merge_requests.rb @@ -3,6 +3,7 @@ module Emails def new_merge_request_email(recipient_id, merge_request_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), to: recipient(recipient_id), subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) @@ -12,6 +13,7 @@ module Emails @merge_request = MergeRequest.find(merge_request_id) @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id @project = @merge_request.project + @target_url = project_merge_request_url(@project, @merge_request) mail(from: sender(updated_by_user_id), to: recipient(recipient_id), subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) @@ -21,15 +23,17 @@ module Emails @merge_request = MergeRequest.find(merge_request_id) @updated_by = User.find updated_by_user_id @project = @merge_request.project + @target_url = project_merge_request_url(@project, @merge_request) mail(from: sender(updated_by_user_id), to: recipient(recipient_id), 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 - mail(from: sender(@merge_request.author_id_of_changes), + @target_url = project_merge_request_url(@project, @merge_request) + mail(from: sender(updated_by_user_id), to: recipient(recipient_id), subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) end diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb index 00b127da429..ccbdadf010f 100644 --- a/app/mailers/emails/notes.rb +++ b/app/mailers/emails/notes.rb @@ -4,6 +4,7 @@ module Emails @note = Note.find(note_id) @commit = @note.noteable @project = @note.project + @target_url = project_commit_url(@project, @commit, anchor: "note_#{@note.id}") mail(from: sender(@note.author_id), to: recipient(recipient_id), subject: subject("#{@commit.title} (#{@commit.short_id})")) @@ -13,6 +14,7 @@ module Emails @note = Note.find(note_id) @issue = @note.noteable @project = @note.project + @target_url = project_issue_url(@project, @issue, anchor: "note_#{@note.id}") mail(from: sender(@note.author_id), to: recipient(recipient_id), subject: subject("#{@issue.title} (##{@issue.iid})")) @@ -22,6 +24,7 @@ module Emails @note = Note.find(note_id) @merge_request = @note.noteable @project = @note.project + @target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}") mail(from: sender(@note.author_id), to: recipient(recipient_id), subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) @@ -30,6 +33,7 @@ module Emails def note_wall_email(recipient_id, note_id) @note = Note.find(note_id) @project = @note.project + @target_url = project_wall_url(@note.project, anchor: "note_#{@note.id}") mail(from: sender(@note.author_id), to: recipient(recipient_id), subject: subject("Note on wall")) diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb index c91660a02b5..f02d95fd557 100644 --- a/app/mailers/emails/profile.rb +++ b/app/mailers/emails/profile.rb @@ -3,6 +3,7 @@ module Emails def new_user_email(user_id, password) @user = User.find(user_id) @password = password + @target_url = user_url(@user) mail(to: @user.email, subject: subject("Account was created for you")) end @@ -15,6 +16,7 @@ module Emails def new_ssh_key_email(key_id) @key = Key.find(key_id) @user = @key.user + @target_url = user_url(@user) mail(to: @user.email, subject: subject("SSH key was added to your account")) end end diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index 46f24e9fb7c..46aa34d13da 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -3,6 +3,7 @@ module Emails def project_access_granted_email(user_project_id) @users_project = UsersProject.find user_project_id @project = @users_project.project + @target_url = project_url(@project) mail(to: @users_project.user.email, subject: subject("Access to project was granted")) end @@ -10,6 +11,7 @@ module Emails def project_was_moved_email(project_id, user_id) @user = User.find user_id @project = Project.find project_id + @target_url = project_url(@project) mail(to: @user.email, subject: subject("Project was moved")) end @@ -21,6 +23,11 @@ module Emails @commits = Commit.decorate(compare.commits) @diffs = compare.diffs @branch = branch + if @commits.length > 1 + @target_url = project_compare_url(@project, from: @commits.first, to: @commits.last) + else + @target_url = project_commit_url(@project, @compare.commit) + end mail(from: sender(author_id), to: recipient, diff --git a/app/models/ability.rb b/app/models/ability.rb index 69ada753d02..1afe8a4638f 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -240,6 +240,7 @@ class Ability can_manage = group_abilities(user, group).include?(:manage_group) if can_manage && (user != target_user) rules << :modify + rules << :destroy end if !group.last_owner?(user) && (can_manage || (user == target_user)) rules << :destroy diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 4774cbcf3aa..7c2648d8c7a 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -133,7 +133,7 @@ class MergeRequest < ActiveRecord::Base end def reload_code - if merge_request_diff && opened? + if merge_request_diff && open? merge_request_diff.reload_content end end diff --git a/app/models/note.rb b/app/models/note.rb index 48c03c9d587..906de4855ab 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 47dc8a1fdb0..769ab217625 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -28,7 +28,6 @@ class Project < ActiveRecord::Base include Gitlab::VisibilityLevel extend Enumerize - default_value_for :imported, false default_value_for :archived, false ActsAsTaggableOn.strict_case_match = true @@ -57,15 +56,13 @@ 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 has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id" - # Merge requests from source project should be kept when source project was removed has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest - has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy has_many :services, dependent: :destroy has_many :events, dependent: :destroy @@ -74,10 +71,8 @@ class Project < ActiveRecord::Base has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet" has_many :hooks, dependent: :destroy, class_name: "ProjectHook" has_many :protected_branches, dependent: :destroy - has_many :users_projects, dependent: :destroy has_many :users, through: :users_projects - has_many :deploy_keys_projects, dependent: :destroy has_many :deploy_keys, through: :deploy_keys_projects @@ -97,15 +92,12 @@ class Project < ActiveRecord::Base validates :issues_enabled, :wall_enabled, :merge_requests_enabled, :wiki_enabled, inclusion: { in: [true, false] } validates :issues_tracker_id, length: { maximum: 255 }, allow_blank: true - validates :namespace, presence: true validates_uniqueness_of :name, scope: :namespace_id validates_uniqueness_of :path, scope: :namespace_id - validates :import_url, format: { with: URI::regexp(%w(git http https)), message: "should be a valid url" }, if: :import? - validate :check_limit, on: :create # Scopes @@ -118,14 +110,36 @@ class Project < ActiveRecord::Base scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") } scope :personal, ->(user) { where(namespace_id: user.namespace_id) } scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) } - scope :public_only, -> { where(visibility_level: Project::PUBLIC) } scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) } - scope :non_archived, -> { where(archived: false) } enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab + state_machine :import_status, initial: :none do + event :import_start do + transition :none => :started + end + + event :import_finish do + transition :started => :finished + end + + event :import_fail do + transition :started => :failed + end + + event :import_retry do + transition :failed => :started + end + + state :started + state :finished + state :failed + + after_transition any => :started, :do => :add_import_job + end + class << self def public_and_internal_levels [Project::PUBLIC, Project::INTERNAL] @@ -164,15 +178,13 @@ class Project < ActiveRecord::Base end def find_with_namespace(id) - if id.include?("/") - id = id.split("/") - namespace = Namespace.find_by(path: id.first) - return nil unless namespace - - where(namespace_id: namespace.id).find_by(path: id.second) - else - where(path: id, namespace_id: nil).last - end + return nil unless id.include?("/") + + id = id.split("/") + namespace = Namespace.find_by(path: id.first) + return nil unless namespace + + where(namespace_id: namespace.id).find_by(path: id.second) end def visibility_levels @@ -202,12 +214,28 @@ class Project < ActiveRecord::Base id && persisted? end + def add_import_job + RepositoryImportWorker.perform_in(2.seconds, id) + end + def import? import_url.present? end def imported? - imported + import_finished? + end + + def import_in_progress? + import? && import_status == 'started' + end + + def import_failed? + import_status == 'failed' + end + + def import_finished? + import_status == 'finished' end def check_limit @@ -277,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? @@ -361,18 +389,17 @@ class Project < ActiveRecord::Base branch_name = ref.gsub("refs/heads/", "") c_ids = self.repository.commits_between(oldrev, newrev).map(&:id) + # Close merge requests + mrs = self.merge_requests.opened.where(target_branch: branch_name).to_a + mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } + mrs.each { |merge_request| MergeRequests::MergeService.new.execute(merge_request, user, nil) } + # Update code for merge requests into project between project branches mrs = self.merge_requests.opened.by_branch(branch_name).to_a # Update code for merge requests between project and project fork mrs += self.fork_merge_requests.opened.by_branch(branch_name).to_a - mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } - # Close merge requests - mrs = self.merge_requests.opened.where(target_branch: branch_name).to_a - mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } - mrs.each { |merge_request| MergeRequests::MergeService.new.execute(merge_request, user, nil) } - true end diff --git a/app/models/project_hook.rb b/app/models/project_hook.rb index e1c9ed01bc5..4e9b22532cb 100644 --- a/app/models/project_hook.rb +++ b/app/models/project_hook.rb @@ -17,9 +17,10 @@ class ProjectHook < WebHook belongs_to :project - attr_accessible :push_events, :issues_events, :merge_requests_events + attr_accessible :push_events, :issues_events, :merge_requests_events, :tag_push_events scope :push_hooks, -> { where(push_events: true) } + scope :tag_push_hooks, -> { where(tag_push_events: true) } scope :issue_hooks, -> { where(issues_events: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true) } end diff --git a/app/models/project_services/slack_message.rb b/app/models/project_services/slack_message.rb new file mode 100644 index 00000000000..b2b8d6fed7a --- /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 00000000000..754fd87db02 --- /dev/null +++ b/app/models/project_services/slack_service.rb @@ -0,0 +1,67 @@ +# == 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.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/snippet.rb b/app/models/snippet.rb index edc179b20fd..c1c9ba257f2 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -20,6 +20,8 @@ class Snippet < ActiveRecord::Base attr_accessible :title, :content, :file_name, :expires_at, :private + default_value_for :private, true + belongs_to :author, class_name: "User" has_many :notes, as: :noteable, dependent: :destroy diff --git a/app/models/user.rb b/app/models/user.rb index 855fe58ffe8..d9f420759d2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -185,7 +185,7 @@ class User < ActiveRecord::Base where(conditions).first end end - + def find_for_commit(email, name) # Prefer email match over name match User.where(email: email).first || @@ -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 @@ -275,7 +275,9 @@ class User < ActiveRecord::Base # Projects user has access to def authorized_projects @authorized_projects ||= begin - project_ids = (personal_projects.pluck(:id) + groups_projects.pluck(:id) + projects.pluck(:id)).uniq + project_ids = personal_projects.pluck(:id) + project_ids += groups_projects.pluck(:id) + project_ids += projects.pluck(:id).uniq Project.where(id: project_ids).joins(:namespace).order('namespaces.name ASC') end end @@ -406,6 +408,14 @@ class User < ActiveRecord::Base end end + def requires_ldap_check? + if ldap_user? + !last_credential_check_at || (last_credential_check_at + 1.hour) < Time.now + else + false + end + end + def solo_owned_groups @solo_owned_groups ||= owned_groups.select do |group| group.owners == [self] diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb index 4e3deec01bf..ad41ddad58f 100644 --- a/app/observers/project_observer.rb +++ b/app/observers/project_observer.rb @@ -1,30 +1,6 @@ class ProjectObserver < BaseObserver def after_create(project) - project.update_column(:last_activity_at, project.created_at) - - return true if project.forked? - - if project.import? - RepositoryImportWorker.perform_in(5.seconds, project.id) - else - GitlabShellWorker.perform_async( - :add_repository, - project.path_with_namespace - ) - - log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"") - end - - if project.wiki_enabled? - begin - # force the creation of a wiki, - GollumWiki.new(project, project.owner).wiki - rescue GollumWiki::CouldNotCreateWikiError => ex - # Prevent project observer crash - # if failed to create wiki - nil - end - end + log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"") end def after_update(project) diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb new file mode 100644 index 00000000000..62eaf9b4f51 --- /dev/null +++ b/app/services/git_tag_push_service.rb @@ -0,0 +1,40 @@ +class GitTagPushService + attr_accessor :project, :user, :push_data + + def execute(project, user, oldrev, newrev, ref) + @project, @user = project, user + @push_data = create_push_data(oldrev, newrev, ref) + + create_push_event + project.repository.expire_cache + project.execute_hooks(@push_data.dup, :tag_push_hooks) + end + + private + + def create_push_data(oldrev, newrev, ref) + data = { + ref: ref, + before: oldrev, + after: newrev, + user_id: user.id, + user_name: user.name, + project_id: project.id, + repository: { + name: project.name, + url: project.url_to_repo, + description: project.description, + homepage: project.web_url + } + } + end + + def create_push_event + Event.create!( + project: project, + action: Event::PUSHED, + data: push_data, + author_id: push_data[: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 d60d61ed54a..9c9117f4687 100644 --- a/app/services/merge_requests/auto_merge_service.rb +++ b/app/services/merge_requests/auto_merge_service.rb @@ -12,7 +12,7 @@ module MergeRequests merge_request.author_id_of_changes = current_user.id merge_request.merge - notification.merge_mr(merge_request) + notification.merge_mr(merge_request, current_user) create_merge_event(merge_request) execute_project_hooks(merge_request) diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index 1d5af04cdbb..80487f661e0 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -10,7 +10,7 @@ module MergeRequests merge_request.author_id_of_changes = current_user.id merge_request.merge - notification.merge_mr(merge_request) + notification.merge_mr(merge_request, current_user) create_merge_event(merge_request) execute_project_hooks(merge_request) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 5daf573630d..6fda9868aa5 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/services/projects/create_service.rb b/app/services/projects/create_service.rb index ba131d8ffbe..4d3d518a509 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -58,6 +58,29 @@ module Projects user: current_user ) end + + @project.update_column(:last_activity_at, @project.created_at) + + if @project.import? + @project.import_start + else + GitlabShellWorker.perform_async( + :add_repository, + @project.path_with_namespace + ) + + end + + if @project.wiki_enabled? + begin + # force the creation of a wiki, + GollumWiki.new(@project, @project.owner).wiki + rescue GollumWiki::CouldNotCreateWikiError => ex + # Prevent project observer crash + # if failed to create wiki + nil + end + end end @project diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb index 09c7cb25dd5..8a1fce17ce7 100644 --- a/app/services/search/global_service.rb +++ b/app/services/search/global_service.rb @@ -14,10 +14,9 @@ module Search group = Group.find_by(id: params[:group_id]) if params[:group_id].present? projects = Project.accessible_to(current_user) projects = projects.where(namespace_id: group.id) if group - projects = projects.search(query) project_ids = projects.pluck(:id) - result[:projects] = projects.limit(20) + result[:projects] = projects.search(query).limit(20) result[:merge_requests] = MergeRequest.in_projects(project_ids).search(query).order('updated_at DESC').limit(20) result[:issues] = Issue.where(project_id: project_ids).search(query).order('updated_at DESC').limit(20) result[:total_results] = %w(projects issues merge_requests).sum { |items| result[items.to_sym].size } diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml index e5af56ffc5c..32f77cc1a70 100644 --- a/app/views/admin/background_jobs/show.html.haml +++ b/app/views/admin/background_jobs/show.html.haml @@ -14,27 +14,21 @@ %table.table %thead %th USER - %th %th PID - %th %th CPU - %th %th MEM - %th %th STATE - %th %th START - %th %th COMMAND - %th - - @sidekiq_processes.split("\n").each do |process| + %tbody + - @sidekiq_processes.each do |process| - next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/) - - data = process.gsub!(/\s+/m, '|').strip.split('|') + - data = process.strip.split(' ') %tr - - 6.times do + %td= Settings.gitlab.user + - 5.times do %td= data.shift - %td - %td= data.join(" ") + %td= data.join(' ') .clearfix %p diff --git a/app/views/dashboard/_zero_authorized_projects.html.haml b/app/views/dashboard/_zero_authorized_projects.html.haml index bb62a1c24fc..e0993293eab 100644 --- a/app/views/dashboard/_zero_authorized_projects.html.haml +++ b/app/views/dashboard/_zero_authorized_projects.html.haml @@ -34,3 +34,18 @@ = link_to new_group_path, class: "btn btn-new" do New group » +-if @publicish_project_count > 0 + %hr + %div + .dashboard-intro-icon + %i.icon-globe + %div + %p.slead + There are + %strong= @publicish_project_count + public projects on this server. + %br + Public projects are an easy way to allow everyone to have read-only access. + .link_holder + = link_to public_projects_path, class: "btn btn-new" do + Browse public projects » diff --git a/app/views/devise/sessions/_oauth_providers.html.haml b/app/views/devise/sessions/_oauth_providers.html.haml index 935bc6af505..2b1cb9c694f 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/groups/members.html.haml b/app/views/groups/members.html.haml index 0c972622f88..2d0605b18ad 100644 --- a/app/views/groups/members.html.haml +++ b/app/views/groups/members.html.haml @@ -9,7 +9,7 @@ %hr -.clearfix +.clearfix.js-toggle-container = form_tag members_group_path(@group), method: :get, class: 'form-inline member-search-form' do .form-group = search_field_tag :search, params[:search], { placeholder: 'Find member by name', class: 'form-control search-text-input input-mn-300' } @@ -17,11 +17,11 @@ - if current_user && current_user.can?(:manage_group, @group) .pull-right - = link_to '#', class: 'btn btn-new js-toggle-visibility-link' do + = link_to '#', class: 'btn btn-new js-toggle-button' do Add members %i.icon-chevron-down - .js-toggle-visibility-container.hide.new-group-member-holder + .js-toggle-content.hide.new-group-member-holder = render "new_group_member" .ui-box.prepend-top-20 diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 9ba20f1347d..d9a7a2d31cf 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 5080a1b7ef6..d8001fd76d7 100644 --- a/app/views/layouts/_head_panel.html.haml +++ b/app/views/layouts/_head_panel.html.haml @@ -15,10 +15,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 = link_to search_path, title: "Search", class: 'has_bottom_tooltip', 'data-original-title' => 'Search area' do diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml index 6a20dedf62f..9e728b462bb 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.setup(); diff --git a/app/views/layouts/_public_head_panel.html.haml b/app/views/layouts/_public_head_panel.html.haml index 99e11cd2b5c..63992a22f32 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/notify.html.haml b/app/views/layouts/notify.html.haml index f88abeca887..09d84a3eb9f 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -3,20 +3,24 @@ %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} %title GitLab + :css + p.details { + font-style:italic; + color:#777 + } + .footer p { + font-size:small; + color:#777 + } %body - %h1{style: "background: #EEE; border-bottom: 1px solid #DDD; color: #474D57; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} - GitLab - - if @project - \| - = link_to @project.name_with_namespace, project_url(@project), style: 'color: #29B; text-decoration: none' - %table{align: "left", border: "0", cellpadding: "0", cellspacing: "0", style: "padding: 10px 0;", width: "100%"} - %tr - %td{align: "left", style: "margin: 0; padding: 10px;"} - = yield - %br - %tr - %td{align: "left", style: "margin: 0; padding: 10px;"} - %p{style: "font-size:small;color:#777"} - - if @project - You're receiving this notification because you are a member of the #{@project.name_with_namespace} project team. + %div.content + = yield + %div.footer{style: "margin-top: 10px;"} + %p + \— + %br + - if @project + You're receiving this notification because you are a member of the #{link_to @project.name_with_namespace, project_url(@project)} project team. + - if @target_url + #{link_to "View in GitLab", @target_url} diff --git a/app/views/layouts/public_projects.html.haml b/app/views/layouts/public_projects.html.haml index cf4ca9c7a84..2a9230244f8 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/_note_message.html.haml b/app/views/notify/_note_message.html.haml index 9e329af2d47..5272dfa0ede 100644 --- a/app/views/notify/_note_message.html.haml +++ b/app/views/notify/_note_message.html.haml @@ -1,6 +1,2 @@ -%p - %strong #{@note.author_name} - wrote: - -%cite{style: 'color: #666'} +%div = markdown(@note.note) diff --git a/app/views/notify/closed_issue_email.html.haml b/app/views/notify/closed_issue_email.html.haml index 325cd44eb4b..56c18cd83cd 100644 --- a/app/views/notify/closed_issue_email.html.haml +++ b/app/views/notify/closed_issue_email.html.haml @@ -1,5 +1,2 @@ %p = "Issue was closed by #{@updated_by.name}" -%p - = "Issue ##{@issue.iid}" - = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title diff --git a/app/views/notify/closed_merge_request_email.html.haml b/app/views/notify/closed_merge_request_email.html.haml index 45770cc85de..809d46f31be 100644 --- a/app/views/notify/closed_merge_request_email.html.haml +++ b/app/views/notify/closed_merge_request_email.html.haml @@ -1,9 +1,2 @@ %p - = "Merge Request #{@merge_request.iid} was closed by #{@updated_by.name}" -%p - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) -%p - != merge_path_description(@merge_request, '→') -%p - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} - + = "Merge Request !#{@merge_request.iid} was closed by #{@updated_by.name}" diff --git a/app/views/notify/group_access_granted_email.html.haml b/app/views/notify/group_access_granted_email.html.haml index 5023ec737a5..823ebf77347 100644 --- a/app/views/notify/group_access_granted_email.html.haml +++ b/app/views/notify/group_access_granted_email.html.haml @@ -1,5 +1,4 @@ %p = "You have been granted #{@membership.human_access} access to group" -%p = link_to group_url(@group) do = @group.name diff --git a/app/views/notify/issue_status_changed_email.html.haml b/app/views/notify/issue_status_changed_email.html.haml index 7706b3f7516..482c884a9db 100644 --- a/app/views/notify/issue_status_changed_email.html.haml +++ b/app/views/notify/issue_status_changed_email.html.haml @@ -1,5 +1,2 @@ %p = "Issue was #{@issue_status} by #{@updated_by.name}" -%p - = "Issue ##{@issue.iid}" - = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title diff --git a/app/views/notify/merged_merge_request_email.html.haml b/app/views/notify/merged_merge_request_email.html.haml index e2bc9cf5c04..0c62d439aed 100644 --- a/app/views/notify/merged_merge_request_email.html.haml +++ b/app/views/notify/merged_merge_request_email.html.haml @@ -1,9 +1,2 @@ %p - = "Merge Request #{@merge_request.iid} was merged" -%p - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) -%p - != merge_path_description(@merge_request, '→') -%p - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} - + = "Merge Request !#{@merge_request.iid} was merged" diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml index 3b3c148517a..f2f8eee18c4 100644 --- a/app/views/notify/new_issue_email.html.haml +++ b/app/views/notify/new_issue_email.html.haml @@ -1,9 +1,6 @@ -%p - New Issue was created. -%p - = "Issue ##{@issue.iid}" - = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title -%p - Author: #{@issue.author_name} -%p - Assignee: #{@issue.assignee_name} +-if @issue.description + = markdown(@issue.description) + +- if @issue.assignee_id.present? + %p + Assignee: #{@issue.assignee_name} diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml index 321f9418ded..f02d5111b22 100644 --- a/app/views/notify/new_merge_request_email.html.haml +++ b/app/views/notify/new_merge_request_email.html.haml @@ -1,9 +1,9 @@ -%p - = "New Merge Request ##{@merge_request.iid}" -%p - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) -%p +%p.details != merge_path_description(@merge_request, '→') -%p - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} +- if @merge_request.assignee_id.present? + %p + Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} + +-if @merge_request.description + = markdown(@merge_request.description) diff --git a/app/views/notify/note_commit_email.html.haml b/app/views/notify/note_commit_email.html.haml index 620b258fc15..1d961e4424c 100644 --- a/app/views/notify/note_commit_email.html.haml +++ b/app/views/notify/note_commit_email.html.haml @@ -1,5 +1,2 @@ -%p - = "New comment for Commit #{@commit.short_id}" - = link_to_gfm truncate(@commit.title, length: 16), project_commit_url(@note.project, id: @commit.id, anchor: "note_#{@note.id}") = render 'note_message' diff --git a/app/views/notify/note_issue_email.html.haml b/app/views/notify/note_issue_email.html.haml index b3230953e7d..2fa2f784661 100644 --- a/app/views/notify/note_issue_email.html.haml +++ b/app/views/notify/note_issue_email.html.haml @@ -1,4 +1 @@ -%p - = "New comment for Issue ##{@issue.iid}" - = link_to_gfm truncate(@issue.title, length: 35), project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}") = render 'note_message' diff --git a/app/views/notify/note_merge_request_email.html.haml b/app/views/notify/note_merge_request_email.html.haml index d587b068486..65f0e4c4068 100644 --- a/app/views/notify/note_merge_request_email.html.haml +++ b/app/views/notify/note_merge_request_email.html.haml @@ -1,8 +1,7 @@ -%p - - if @note.for_diff_line? - = link_to "New comment on diff", diffs_project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}") - - else - = link_to "New comment", project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}") - for Merge Request ##{@merge_request.iid} - %cite "#{truncate(@merge_request.title, length: 20)}" +- if @note.diff_file_name + %p.details + New comment on diff for + = link_to @note.diff_file_name, @target_url + \: + = render 'note_message' diff --git a/app/views/notify/note_wall_email.html.haml b/app/views/notify/note_wall_email.html.haml index 92200e83efa..2fa2f784661 100644 --- a/app/views/notify/note_wall_email.html.haml +++ b/app/views/notify/note_wall_email.html.haml @@ -1,5 +1 @@ -%p - New message on - = link_to "Project Wall", project_wall_url(@note.project, anchor: "note_#{@note.id}") - = render 'note_message' diff --git a/app/views/notify/reassigned_issue_email.html.haml b/app/views/notify/reassigned_issue_email.html.haml index b0edff44ce6..07227a3e68c 100644 --- a/app/views/notify/reassigned_issue_email.html.haml +++ b/app/views/notify/reassigned_issue_email.html.haml @@ -1,7 +1,4 @@ %p - = "Reassigned Issue ##{@issue.iid}" - = link_to_gfm truncate(@issue.title, length: 30), project_issue_url(@issue.project, @issue) -%p Assignee changed - if @previous_assignee from diff --git a/app/views/notify/reassigned_merge_request_email.html.haml b/app/views/notify/reassigned_merge_request_email.html.haml index d2d82d36c48..00aee6bc952 100644 --- a/app/views/notify/reassigned_merge_request_email.html.haml +++ b/app/views/notify/reassigned_merge_request_email.html.haml @@ -1,7 +1,4 @@ %p - = "Reassigned Merge Request ##{@merge_request.iid}" - = link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.target_project, @merge_request) -%p Assignee changed - if @previous_assignee from diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index df44c30c005..f2b0699f136 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -1,7 +1,7 @@ .pull-right %div - if @notes_count > 0 - %span.btn.disabled.grouped + %span.btn.disabled.btn-grouped %i.icon-comment = @notes_count .pull-left.btn-group @@ -47,7 +47,7 @@ - if @branches.any? and in = link_to("#{pluralize(@branches.count, "other branch")}", "#", class: "js-details-expand") - %span.js-details-contain.hide + %span.js-details-content.hide = commit_branches_links(@project, @branches) .commit-box diff --git a/app/views/projects/commits/_diffs.html.haml b/app/views/projects/commits/_diffs.html.haml index dd287fcc153..466085139f9 100644 --- a/app/views/projects/commits/_diffs.html.haml +++ b/app/views/projects/commits/_diffs.html.haml @@ -44,7 +44,7 @@ - file = project.repository.blob_at(@commit.id, diff.new_path) - file = project.repository.blob_at(@commit.parent_id, diff.old_path) unless file - next unless file - .diff-file{id: "diff-#{i}"} + .diff-file.js-toggle-container{id: "diff-#{i}"} .diff-header{id: "file-path-#{hexdigest(diff.new_path || diff.old_path)}"} - if diff.deleted_file %span= diff.old_path @@ -60,6 +60,11 @@ %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" .diff-btn-group + = link_to "#", class: "js-toggle-button btn btn-small" do + %i.icon-chevron-down + Diff comments + + - if @merge_request && @merge_request.source_project = link_to project_edit_tree_path(@merge_request.source_project, tree_join(@merge_request.source_branch, diff.new_path), from_merge_request_id: @merge_request.id), { class: 'btn btn-small' } do Edit diff --git a/app/views/projects/create.js.haml b/app/views/projects/create.js.haml index a444b8b59a6..89710d3a09a 100644 --- a/app/views/projects/create.js.haml +++ b/app/views/projects/create.js.haml @@ -1,6 +1,10 @@ - if @project.saved? - :plain - location.href = "#{project_path(@project)}"; + - if @project.import? + :plain + location.href = "#{import_project_path(@project)}"; + - else + :plain + location.href = "#{project_path(@project)}"; - else :plain $(".project-edit-errors").html("#{escape_javascript(render('errors'))}"); diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 005d994806e..10674ccae46 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -93,100 +93,101 @@ - .centered-light-block - %h3 - %i.icon-warning-sign - Dangerous settings - - %p Project settings below may result in data loss! - = link_to '#', class: 'btn js-toggle-visibility-link' do - Show it to me - %i.icon-chevron-down - - .js-toggle-visibility-container.hide - - if can? current_user, :archive_project, @project - .ui-box.ui-box-danger - .title - - if @project.archived? - Unarchive project - - else - Archive project - .body - - if @project.archived? - %p - Unarchiving the project will mark its repository as active. - %br - The project can be committed to. - %br - %strong Once active this project shows up in the search and on the dashboard. - = link_to 'Unarchive', unarchive_project_path(@project), - data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be comitted to again." }, - method: :post, class: "btn btn-remove" - - else - %p - Archiving the project will mark its repository as read-only. - %br - It is hidden from the dashboard and doesn't show up in searches. - %br - %strong Archived projects cannot be committed to! - = link_to 'Archive', archive_project_path(@project), - data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, - method: :post, class: "btn btn-remove" - - else - .nothing-here-block Only the project owner can archive a project - - - if can?(current_user, :change_namespace, @project) + .danger-settings.js-toggle-container + .centered-light-block + %h3 + %i.icon-warning-sign + Dangerous settings + + %p Project settings below may result in data loss! + = link_to '#', class: 'btn js-toggle-button' do + Show it to me + %i.icon-chevron-down + + .js-toggle-content.hide + - if can? current_user, :archive_project, @project + .ui-box.ui-box-danger + .title + - if @project.archived? + Unarchive project + - else + Archive project + .body + - if @project.archived? + %p + Unarchiving the project will mark its repository as active. + %br + The project can be committed to. + %br + %strong Once active this project shows up in the search and on the dashboard. + = link_to 'Unarchive', unarchive_project_path(@project), + data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be comitted to again." }, + method: :post, class: "btn btn-remove" + - else + %p + Archiving the project will mark its repository as read-only. + %br + It is hidden from the dashboard and doesn't show up in searches. + %br + %strong Archived projects cannot be committed to! + = link_to 'Archive', archive_project_path(@project), + data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, + method: :post, class: "btn btn-remove" + - else + .nothing-here-block Only the project owner can archive a project + + - if can?(current_user, :change_namespace, @project) + .ui-box.ui-box-danger + .title Transfer project + .errors-holder + .form-holder + = form_for(@project, url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f| + .form-group + = f.label :namespace_id, class: 'control-label' do + %span Namespace + .col-sm-10 + .form-group + = f.select :namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace' }, { class: 'select2' } + %ul + %li Be careful. Changing the project's namespace can have unintended side effects. + %li You can only transfer the project to namespaces you manage. + %li You will need to update your local repositories to point to the new location. + .form-actions + = f.submit 'Transfer', class: "btn btn-remove" + - else + .nothing-here-block Only the project owner can transfer a project + .ui-box.ui-box-danger - .title Transfer project + .title Rename repository .errors-holder .form-holder - = form_for(@project, url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f| + = form_for(@project, html: { class: 'form-horizontal' }) do |f| .form-group - = f.label :namespace_id, class: 'control-label' do - %span Namespace - .col-sm-10 + = f.label :path, class: 'control-label' do + %span Path + .col-sm-9 .form-group - = f.select :namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace' }, { class: 'select2' } + .input-group + = f.text_field :path, class: 'form-control' + %span.input-group-addon .git %ul - %li Be careful. Changing the project's namespace can have unintended side effects. - %li You can only transfer the project to namespaces you manage. + %li Be careful. Renaming a project's repository can have unintended side effects. %li You will need to update your local repositories to point to the new location. .form-actions - = f.submit 'Transfer', class: "btn btn-remove" - - else - .nothing-here-block Only the project owner can transfer a project - - .ui-box.ui-box-danger - .title Rename repository - .errors-holder - .form-holder - = form_for(@project, html: { class: 'form-horizontal' }) do |f| - .form-group - = f.label :path, class: 'control-label' do - %span Path - .col-sm-9 - .form-group - .input-group - = f.text_field :path, class: 'form-control' - %span.input-group-addon .git - %ul - %li Be careful. Renaming a project's repository can have unintended side effects. - %li You will need to update your local repositories to point to the new location. - .form-actions - = f.submit 'Rename', class: "btn btn-remove" - - - if can?(current_user, :remove_project, @project) - .ui-box.ui-box-danger - .title Remove project - .body - %p - Removing the project will delete its repository and all related resources including issues, merge requests etc. - %br - %strong Removed projects cannot be restored! - - = link_to 'Remove project', @project, data: { confirm: remove_project_message(@project) }, method: :delete, class: "btn btn-remove" - - else - .nothing-here-block Only project owner can remove a project + = f.submit 'Rename', class: "btn btn-remove" + + - if can?(current_user, :remove_project, @project) + .ui-box.ui-box-danger + .title Remove project + .body + %p + Removing the project will delete its repository and all related resources including issues, merge requests etc. + %br + %strong Removed projects cannot be restored! + + = link_to 'Remove project', @project, data: { confirm: remove_project_message(@project) }, method: :delete, class: "btn btn-remove" + - else + .nothing-here-block Only project owner can remove a project .save-project-loader.hide %center diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 489b9b0e951..97dc73bce14 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -1,46 +1,34 @@ = render "home_panel" -- if @project.import? && !@project.imported - .save-project-loader - %center - %h2 - %i.icon-spinner.icon-spin - Importing repository. - %p.monospace git clone --bare #{@project.import_url} - %p Please wait while we import the repository for you. Refresh at will. - :javascript - new ProjectImport(); +%div.git-empty + %fieldset + %legend Git global setup: + %pre.dark + :preserve + git config --global user.name "#{git_user_name}" + git config --global user.email "#{git_user_email}" -- else - %div.git-empty - %fieldset - %legend Git global setup: - %pre.dark - :preserve - git config --global user.name "#{git_user_name}" - git config --global user.email "#{git_user_email}" + %fieldset + %legend Create Repository + %pre.dark + :preserve + mkdir #{@project.path} + cd #{@project.path} + git init + touch README + git add README + git commit -m 'first commit' + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} + git push -u origin master - %fieldset - %legend Create Repository - %pre.dark - :preserve - mkdir #{@project.path} - cd #{@project.path} - git init - touch README - git add README - git commit -m 'first commit' - git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} - git push -u origin master + %fieldset + %legend Existing Git Repo? + %pre.dark + :preserve + cd existing_git_repo + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} + git push -u origin master - %fieldset - %legend Existing Git Repo? - %pre.dark - :preserve - cd existing_git_repo - git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} - git push -u origin master - - - if can? current_user, :remove_project, @project - .prepend-top-20 - = link_to 'Remove project', @project, data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" +- if can? current_user, :remove_project, @project + .prepend-top-20 + = link_to 'Remove project', @project, data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml index 00e5ae27779..866fd6f6066 100644 --- a/app/views/projects/hooks/index.html.haml +++ b/app/views/projects/hooks/index.html.haml @@ -27,6 +27,13 @@ %p.light This url will be triggered by a push to the repository %div + = f.check_box :tag_push_events, class: 'pull-left' + .prepend-left-20 + = f.label :tag_push_events, class: 'list-label' do + %strong Tag push events + %p.light + This url will be triggered when a new tag is pushed to the repository + %div = f.check_box :issues_events, class: 'pull-left' .prepend-left-20 = f.label :issues_events, class: 'list-label' do @@ -56,6 +63,6 @@ .clearfix %span.monospace= hook.url %p - - %w(push_events issues_events merge_requests_events).each do |trigger| + - %w(push_events tag_push_events issues_events merge_requests_events).each do |trigger| - if hook.send(trigger) %span.label.label-gray= trigger.titleize diff --git a/app/views/projects/import.html.haml b/app/views/projects/import.html.haml new file mode 100644 index 00000000000..d11372be61b --- /dev/null +++ b/app/views/projects/import.html.haml @@ -0,0 +1,30 @@ +- if @project.import_in_progress? + .save-project-loader + %center + %h2 + %i.icon-spinner.icon-spin + Import in progress. + %p.monospace git clone --bare #{@project.import_url} + %p Please wait while we import the repository for you. Refresh at will. + :javascript + new ProjectImport(); + +- elsif @project.import_failed? + .save-project-loader + %center + %h2 + Import failed. Retry? + %hr + - if can?(current_user, :admin_project, @project) + = form_for @project, url: retry_import_project_path(@project), method: :put, html: { class: 'form-horizontal' } do |f| + .form-group.import-url-data + = f.label :import_url, class: 'control-label' do + %span Import existing repo + .col-sm-10 + = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' + .bs-callout.bs-callout-info + This url must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. + %br + The import will time out after 2 minutes. For big repositories, use a clone/push combination. + .form-actions + = f.submit 'Retry import', class: "btn btn-create", tabindex: 4 diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 51fa29ddcbe..22502760e50 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/branch_from.js.haml b/app/views/projects/merge_requests/branch_from.js.haml index d3147188d1c..1b1082baafe 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/_mr_accept.html.haml b/app/views/projects/merge_requests/show/_mr_accept.html.haml index a9cbe43fb16..4b1857ccb68 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,10 @@ - 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 + %strong You don't have permission to merge this MR - if @show_merge_controls @@ -15,18 +19,19 @@ = link_to "click here", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" for instructions. - %br - If you want to modify merge commit message - - %strong - = link_to "click here", "#", class: "modify-merge-commit-link js-toggle-visibility-link", title: "Modify merge commit message" - .js-toggle-visibility-container.hide - .form-group - = label_tag :merge_commit_message, "Commit message", class: 'control-label' - .col-sm-10 - = text_area_tag :merge_commit_message, @merge_request.merge_commit_message, class: "form-control js-gfm-input", rows: 14, required: true - %p.hint - The recommended maximum line length is 52 characters for the first line and 72 characters for all following lines. + .js-toggle-container + %p + If you want to modify merge commit message - + %strong + = link_to "click here", "#", class: "modify-merge-commit-link js-toggle-button", title: "Modify merge commit message" + .js-toggle-content.hide + .form-group + = label_tag :merge_commit_message, "Commit message", class: 'control-label' + .col-sm-10 + = text_area_tag :merge_commit_message, @merge_request.merge_commit_message, class: "form-control js-gfm-input", rows: 14, required: true + %p.hint + The recommended maximum line length is 52 characters for the first line and 72 characters for all following lines. .accept-group .pull-left 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 38db4363ade..abdcd127a0c 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/new.html.haml b/app/views/projects/new.html.haml index 9ee54fef062..5d5637c1588 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -9,18 +9,6 @@ %strong Project name .col-sm-10 = f.text_field :name, placeholder: "Example Project", class: "form-control", tabindex: 1, autofocus: true - .help-inline - = link_to "#", class: 'js-toggle-visibility-link' do - %span Customize repository name? - - .form-group.js-toggle-visibility-container.hide - = f.label :path, class: 'control-label' do - %span Repository name - .col-sm-10 - .input-group - = f.text_field :path, class: 'form-control' - %span.input-group-addon .git - - if current_user.can_select_namespace? .form-group @@ -29,19 +17,42 @@ .col-sm-10 = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2} - .form-group - .col-sm-2 - .col-sm-10 - = link_to "#", class: 'appear-link' do - %i.icon-upload-alt - %span Import existing repository? - .form-group.appear-data.import-url-data - = f.label :import_url, class: 'control-label' do - %span Import existing repo - .col-sm-10 - = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' - .light - URL must be cloneable + %hr + .js-toggle-container + .form-group + .col-sm-2 + .col-sm-10 + = link_to "#", class: 'js-toggle-button' do + %i.icon-edit + %span Customize repository name? + .js-toggle-content.hide + .form-group + = f.label :path, class: 'control-label' do + %span Repository name + .col-sm-10 + .input-group + = f.text_field :path, class: 'form-control' + %span.input-group-addon .git + + .js-toggle-container + .form-group + .col-sm-2 + .col-sm-10 + = link_to "#", class: 'js-toggle-button' do + %i.icon-upload-alt + %span Import existing repository? + .js-toggle-content.hide + .form-group.import-url-data + = f.label :import_url, class: 'control-label' do + %span Import existing repo + .col-sm-10 + = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' + .bs-callout.bs-callout-info + This url must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. + %br + The import will time out after 2 minutes. For big repositories, use a clone/push combination. + %hr + .form-group = f.label :description, class: 'control-label' do Description diff --git a/app/views/projects/notes/_diff_notes_with_reply.html.haml b/app/views/projects/notes/_diff_notes_with_reply.html.haml index 9537ab18caa..9acadc6a14e 100644 --- a/app/views/projects/notes/_diff_notes_with_reply.html.haml +++ b/app/views/projects/notes/_diff_notes_with_reply.html.haml @@ -1,7 +1,7 @@ - note = notes.first # example note -# Check if line want not changed since comment was left - if !defined?(line) || line == note.diff_line - %tr.notes_holder + %tr.notes_holder.js-toggle-content %td.notes_line{ colspan: 2 } %span.btn.disabled %i.icon-comment diff --git a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml index 936dbb354cd..2012aa021b9 100644 --- a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml +++ b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml @@ -1,6 +1,6 @@ - note1 = notes1.first # example note - note2 = notes2.first # example note -%tr.notes_holder +%tr.notes_holder.js-toggle-content -# Check if line want not changed since comment was left /- if !defined?(line1) || line1 == note1.diff_line - if note1 diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 217e36e38d0..81bf0611ec6 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 8a1e1d3354b..20879d69091 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/app/workers/post_receive.rb b/app/workers/post_receive.rb index 6416aa608ec..3b0cf77d42e 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -29,10 +29,20 @@ class PostReceive return false end - GitPushService.new.execute(project, user, oldrev, newrev, ref) + if tag?(ref) + GitTagPushService.new.execute(project, user, oldrev, newrev, ref) + else + GitPushService.new.execute(project, user, oldrev, newrev, ref) + end end def log(message) Gitlab::GitLogger.error("POST-RECEIVE: #{message}") end + + private + + def tag?(ref) + !!(/refs\/tags\/(.*)/.match(ref)) + end end diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 95b80bca7c0..e66df8c4db1 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -11,11 +11,11 @@ class RepositoryImportWorker project.import_url) if result - project.imported = true + project.import_finish project.save project.satellite.create unless project.satellite.exists? else - project.imported = false + project.import_fail end end end diff --git a/config/environments/production.rb b/config/environments/production.rb index 9ac4622abc2..ad3c03d8fc9 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/gitlab.yml.example b/config/gitlab.yml.example index a40ce7212fe..9364181eaa4 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -121,7 +121,6 @@ production: &base ldap: enabled: false host: '_your_ldap_server' - base: '_the_base_where_you_search_for_users' port: 636 uid: 'sAMAccountName' method: 'ssl' # "tls" or "ssl" or "plain" @@ -138,6 +137,20 @@ production: &base # disable this setting, because the userPrincipalName contains an '@'. allow_username_or_email_login: true + # Base where we can search for users + # + # Ex. ou=People,dc=gitlab,dc=example + # + base: '' + + # Filter LDAP users + # + # Format: RFC 4515 + # Ex. (employeeType=developer) + # + user_filter: '' + + ## OmniAuth settings omniauth: # Allow login via Twitter, Google, etc. using OmniAuth providers diff --git a/config/initializers/gemoji.rb b/config/initializers/gemoji.rb deleted file mode 100644 index 6cc33aced77..00000000000 --- 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 fdca1e62661..709b66d3e06 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 @@ -179,6 +179,8 @@ Gitlab::Application.routes.draw do post :archive post :unarchive get :autocomplete_sources + get :import + put :retry_import end scope module: :projects do diff --git a/config/unicorn_development.rb b/config/unicorn_development.rb new file mode 100644 index 00000000000..94a7061451d --- /dev/null +++ b/config/unicorn_development.rb @@ -0,0 +1,2 @@ +worker_processes 2 +timeout 30 diff --git a/db/fixtures/development/01_admin.rb b/db/fixtures/development/01_admin.rb index 3e76d76e838..42d18435340 100644 --- a/db/fixtures/development/01_admin.rb +++ b/db/fixtures/development/01_admin.rb @@ -8,8 +8,7 @@ User.seed(:id, [ password_confirmation: "5iveL!fe", admin: true, projects_limit: 100, - theme_id: Gitlab::Theme::MARS + theme_id: Gitlab::Theme::MARS, + confirmed_at: DateTime.now } ]) - -User.find(1).confirm! diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb index d0d0774e0c8..9303ab93300 100644 --- a/db/fixtures/development/04_project.rb +++ b/db/fixtures/development/04_project.rb @@ -1,53 +1,55 @@ -project_urls = [ - 'https://github.com/documentcloud/underscore.git', - 'https://github.com/diaspora/diaspora.git', - 'https://github.com/diaspora/diaspora-project-site.git', - 'https://github.com/diaspora/diaspora-client.git', - 'https://github.com/brightbox/brightbox-cli.git', - 'https://github.com/brightbox/puppet.git', - 'https://github.com/gitlabhq/gitlabhq.git', - 'https://github.com/gitlabhq/gitlab-ci.git', - 'https://github.com/gitlabhq/gitlab-recipes.git', - 'https://github.com/gitlabhq/gitlab-shell.git', - 'https://github.com/gitlabhq/grack.git', - 'https://github.com/gitlabhq/testme.git', - 'https://github.com/twitter/flight.git', - 'https://github.com/twitter/typeahead.js.git', - 'https://github.com/h5bp/html5-boilerplate.git', - 'https://github.com/h5bp/mobile-boilerplate.git', -] - -project_urls.each_with_index do |url, i| - group_path, project_path = url.split('/')[-2..-1] - - group = Group.find_by(path: group_path) - - unless group - group = Group.new( - name: group_path.titleize, - path: group_path - ) - group.description = Faker::Lorem.sentence - group.save - - group.add_owner(User.first) - end - - project_path.gsub!(".git", "") - - params = { - import_url: url, - namespace_id: group.id, - name: project_path.titleize, - description: Faker::Lorem.sentence - } - - project = Projects::CreateService.new(User.first, params).execute - - if project.valid? - print '.' - else - puts project.errors.full_messages - print 'F' +Gitlab::Seeder.quiet do + project_urls = [ + 'https://github.com/documentcloud/underscore.git', + 'https://github.com/diaspora/diaspora.git', + 'https://github.com/diaspora/diaspora-project-site.git', + 'https://github.com/diaspora/diaspora-client.git', + 'https://github.com/brightbox/brightbox-cli.git', + 'https://github.com/brightbox/puppet.git', + 'https://github.com/gitlabhq/gitlabhq.git', + 'https://github.com/gitlabhq/gitlab-ci.git', + 'https://github.com/gitlabhq/gitlab-recipes.git', + 'https://github.com/gitlabhq/gitlab-shell.git', + 'https://github.com/gitlabhq/grack.git', + 'https://github.com/gitlabhq/testme.git', + 'https://github.com/twitter/flight.git', + 'https://github.com/twitter/typeahead.js.git', + 'https://github.com/h5bp/html5-boilerplate.git', + 'https://github.com/h5bp/mobile-boilerplate.git', + ] + + project_urls.each_with_index do |url, i| + group_path, project_path = url.split('/')[-2..-1] + + group = Group.find_by(path: group_path) + + unless group + group = Group.new( + name: group_path.titleize, + path: group_path + ) + group.description = Faker::Lorem.sentence + group.save + + group.add_owner(User.first) + end + + project_path.gsub!(".git", "") + + params = { + import_url: url, + namespace_id: group.id, + name: project_path.titleize, + description: Faker::Lorem.sentence + } + + project = Projects::CreateService.new(User.first, params).execute + + if project.valid? + print '.' + else + puts project.errors.full_messages + print 'F' + end end end diff --git a/db/fixtures/development/05_users.rb b/db/fixtures/development/05_users.rb index 3440a645408..d736408dbf5 100644 --- a/db/fixtures/development/05_users.rb +++ b/db/fixtures/development/05_users.rb @@ -6,6 +6,7 @@ Gitlab::Seeder.quiet do username: Faker::Internet.user_name, name: Faker::Name.name, email: Faker::Internet.email, + confirmed_at: DateTime.now }]) print '.' rescue ActiveRecord::RecordNotSaved diff --git a/db/fixtures/development/06_teams.rb b/db/fixtures/development/06_teams.rb index a1e01879db5..dfbe75fd20e 100644 --- a/db/fixtures/development/06_teams.rb +++ b/db/fixtures/development/06_teams.rb @@ -1,5 +1,3 @@ -ActiveRecord::Base.observers.disable :all - Gitlab::Seeder.quiet do Group.all.each do |group| User.all.sample(4).each do |user| diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb index 2b81d7a2597..635878622d0 100644 --- a/db/fixtures/development/09_issues.rb +++ b/db/fixtures/development/09_issues.rb @@ -1,5 +1,3 @@ -ActiveRecord::Base.observers.disable :all - Gitlab::Seeder.quiet do (1..300).each do |i| # Random Project @@ -12,9 +10,7 @@ Gitlab::Seeder.quiet do user_id = user.id - begin - Thread.current[:current_user] = user - + Gitlab::Seeder.by_user(user) do Issue.seed(:id, [{ id: i, project_id: project.id, @@ -25,8 +21,6 @@ Gitlab::Seeder.quiet do title: Faker::Lorem.sentence(6), description: Faker::Lorem.sentence }]) - ensure - Thread.current[:current_user] = nil end print('.') end diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb index 2b1f4160211..cb08a7c2537 100644 --- a/db/fixtures/development/10_merge_requests.rb +++ b/db/fixtures/development/10_merge_requests.rb @@ -1,5 +1,3 @@ -ActiveRecord::Base.observers.disable :all - Gitlab::Seeder.quiet do (1..100).each do |i| # Random Project @@ -17,9 +15,8 @@ Gitlab::Seeder.quiet do next if branches.uniq.size < 2 user_id = user.id - begin - Thread.current[:current_user] = user + Gitlab::Seeder.by_user(user) do MergeRequest.seed(:id, [{ id: i, source_branch: branches.first, @@ -31,8 +28,6 @@ Gitlab::Seeder.quiet do milestone: project.milestones.sample, title: Faker::Lorem.sentence(6) }]) - ensure - Thread.current[:current_user] = nil end print('.') end diff --git a/db/fixtures/development/11_keys.rb b/db/fixtures/development/11_keys.rb index 4b53ff411f2..61329f197c3 100644 --- a/db/fixtures/development/11_keys.rb +++ b/db/fixtures/development/11_keys.rb @@ -1,10 +1,8 @@ -ActiveRecord::Base.observers.enable :all - 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/db/fixtures/development/12_snippets.rb b/db/fixtures/development/12_snippets.rb index 4ca8afe294e..dced2706264 100644 --- a/db/fixtures/development/12_snippets.rb +++ b/db/fixtures/development/12_snippets.rb @@ -1,5 +1,3 @@ -ActiveRecord::Base.observers.disable :all - Gitlab::Seeder.quiet do contents = [ `curl https://gist.github.com/randx/4275756/raw/da2f262920c96d1a970d48bf2e99147954b1f4bd/glus1204.sh`, diff --git a/db/fixtures/development/13_comments.rb b/db/fixtures/development/13_comments.rb new file mode 100644 index 00000000000..626aba200d1 --- /dev/null +++ b/db/fixtures/development/13_comments.rb @@ -0,0 +1,19 @@ +Gitlab::Seeder.quiet do + Issue.all.limit(10).each_with_index do |issue, i| + 5.times do + user = issue.project.team.users.sample + + Gitlab::Seeder.by_user(user) do + Note.seed(:id, [{ + project_id: issue.project.id, + author_id: user.id, + note: Faker::Lorem.sentence, + noteable_id: issue.id, + noteable_type: 'Issue' + }]) + + print '.' + end + end + end +end diff --git a/db/migrate/20130809124851_add_permission_check_to_user.rb b/db/migrate/20130809124851_add_permission_check_to_user.rb new file mode 100644 index 00000000000..c26157904c7 --- /dev/null +++ b/db/migrate/20130809124851_add_permission_check_to_user.rb @@ -0,0 +1,5 @@ +class AddPermissionCheckToUser < ActiveRecord::Migration + def change + add_column :users, :last_credential_check_at, :datetime + end +end diff --git a/db/migrate/20140305193308_add_tag_push_hooks_to_project_hook.rb b/db/migrate/20140305193308_add_tag_push_hooks_to_project_hook.rb new file mode 100644 index 00000000000..7017148702a --- /dev/null +++ b/db/migrate/20140305193308_add_tag_push_hooks_to_project_hook.rb @@ -0,0 +1,5 @@ +class AddTagPushHooksToProjectHook < ActiveRecord::Migration + def change + add_column :web_hooks, :tag_push_events, :boolean, default: false + end +end diff --git a/db/migrate/20140312145357_add_import_status_to_project.rb b/db/migrate/20140312145357_add_import_status_to_project.rb new file mode 100644 index 00000000000..ef972e8342a --- /dev/null +++ b/db/migrate/20140312145357_add_import_status_to_project.rb @@ -0,0 +1,5 @@ +class AddImportStatusToProject < ActiveRecord::Migration + def change + add_column :projects, :import_status, :string + end +end diff --git a/db/migrate/20140313092127_migrate_already_imported_projects.rb b/db/migrate/20140313092127_migrate_already_imported_projects.rb new file mode 100644 index 00000000000..f4392c0f05e --- /dev/null +++ b/db/migrate/20140313092127_migrate_already_imported_projects.rb @@ -0,0 +1,12 @@ +class MigrateAlreadyImportedProjects < ActiveRecord::Migration + def up + Project.where(imported: true).update_all(import_status: "finished") + Project.where(imported: false).update_all(import_status: "none") + remove_column :projects, :imported + end + + def down + add_column :projects, :imported, :boolean, default: false + Project.where(import_status: 'finished').update_all(imported: true) + end +end diff --git a/db/schema.rb b/db/schema.rb index 9f1de4d2120..d8a9d1863fc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,15 +11,18 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20140304005354) do +ActiveRecord::Schema.define(version: 20140313092127) do + + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" create_table "broadcast_messages", force: true do |t| t.text "message", null: false t.datetime "starts_at" t.datetime "ends_at" t.integer "alert_type" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.string "color" t.string "font" end @@ -27,8 +30,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do create_table "deploy_keys_projects", force: true do |t| t.integer "deploy_key_id", null: false t.integer "project_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" end add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree @@ -49,8 +52,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do t.string "title" t.text "data" t.integer "project_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.integer "action" t.integer "author_id" end @@ -65,8 +68,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do create_table "forked_project_links", force: true do |t| t.integer "forked_to_project_id", null: false t.integer "forked_from_project_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" end add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree @@ -76,8 +79,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do t.integer "assignee_id" t.integer "author_id" t.integer "project_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.integer "position", default: 0 t.string "branch_name" t.text "description" @@ -95,8 +98,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do create_table "keys", force: true do |t| t.integer "user_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.text "key" t.string "title" t.string "type" @@ -106,10 +109,10 @@ ActiveRecord::Schema.define(version: 20140304005354) do add_index "keys", ["user_id"], name: "index_keys_on_user_id", using: :btree create_table "merge_request_diffs", force: true do |t| - t.string "state", default: "collected", null: false - t.text "st_commits", limit: 2147483647 - t.text "st_diffs", limit: 2147483647 - t.integer "merge_request_id", null: false + t.string "state", default: "collected", null: false + t.text "st_commits" + t.text "st_diffs" + t.integer "merge_request_id", null: false t.datetime "created_at" t.datetime "updated_at" end @@ -123,8 +126,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do t.integer "author_id" t.integer "assignee_id" t.string "title" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.integer "milestone_id" t.string "state" t.string "merge_status" @@ -138,7 +141,7 @@ ActiveRecord::Schema.define(version: 20140304005354) do add_index "merge_requests", ["created_at"], name: "index_merge_requests_on_created_at", using: :btree add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree - add_index "merge_requests", ["source_project_id"], name: "index_merge_requests_on_project_id", using: :btree + add_index "merge_requests", ["source_project_id"], name: "index_merge_requests_on_source_project_id", using: :btree add_index "merge_requests", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree @@ -147,8 +150,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do t.integer "project_id", null: false t.text "description" t.date "due_date" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.string "state" t.integer "iid" end @@ -160,8 +163,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do t.string "name", null: false t.string "path", null: false t.integer "owner_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.string "type" t.string "description", default: "", null: false t.string "avatar" @@ -176,15 +179,15 @@ ActiveRecord::Schema.define(version: 20140304005354) do t.text "note" t.string "noteable_type" t.integer "author_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.integer "project_id" t.string "attachment" t.string "line_code" t.string "commit_id" t.integer "noteable_id" - t.text "st_diff" t.boolean "system", default: false, null: false + t.text "st_diff" end add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree @@ -199,8 +202,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do t.string "name" t.string "path" t.text "description" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.integer "creator_id" t.boolean "issues_enabled", default: true, null: false t.boolean "wall_enabled", default: true, null: false @@ -211,21 +214,21 @@ ActiveRecord::Schema.define(version: 20140304005354) do t.string "issues_tracker_id" t.boolean "snippets_enabled", default: true, null: false t.datetime "last_activity_at" - t.boolean "imported", default: false, null: false t.string "import_url" t.integer "visibility_level", default: 0, null: false t.boolean "archived", default: false, null: false + t.string "import_status" end - add_index "projects", ["creator_id"], name: "index_projects_on_owner_id", using: :btree + add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree create_table "protected_branches", force: true do |t| t.integer "project_id", null: false t.string "name", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" end add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree @@ -235,8 +238,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do t.string "title" t.string "token" t.integer "project_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.boolean "active", default: false, null: false t.string "project_url" t.string "subdomain" @@ -249,14 +252,14 @@ ActiveRecord::Schema.define(version: 20140304005354) do create_table "snippets", force: true do |t| t.string "title" - t.text "content", limit: 2147483647 - t.integer "author_id", null: false + t.text "content" + t.integer "author_id", null: false t.integer "project_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.string "file_name" t.datetime "expires_at" - t.boolean "private", default: true, null: false + t.boolean "private", default: true, null: false t.string "type" end @@ -283,46 +286,47 @@ ActiveRecord::Schema.define(version: 20140304005354) do end create_table "users", force: true do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0 + t.integer "sign_in_count", default: 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.string "name" - t.boolean "admin", default: false, null: false - t.integer "projects_limit", default: 10 - t.string "skype", default: "", null: false - t.string "linkedin", default: "", null: false - t.string "twitter", default: "", null: false + t.boolean "admin", default: false, null: false + t.integer "projects_limit", default: 10 + t.string "skype", default: "", null: false + t.string "linkedin", default: "", null: false + t.string "twitter", default: "", null: false t.string "authentication_token" - t.integer "theme_id", default: 1, null: false + t.integer "theme_id", default: 1, null: false t.string "bio" - t.integer "failed_attempts", default: 0 + t.integer "failed_attempts", default: 0 t.datetime "locked_at" t.string "extern_uid" t.string "provider" t.string "username" - t.boolean "can_create_group", default: true, null: false - t.boolean "can_create_team", default: true, null: false + t.boolean "can_create_group", default: true, null: false + t.boolean "can_create_team", default: true, null: false t.string "state" - t.integer "color_scheme_id", default: 1, null: false - t.integer "notification_level", default: 1, null: false + t.integer "color_scheme_id", default: 1, null: false + t.integer "notification_level", default: 1, null: false t.datetime "password_expires_at" t.integer "created_by_id" + t.datetime "last_credential_check_at" t.string "avatar" t.string "confirmation_token" t.datetime "confirmed_at" t.datetime "confirmation_sent_at" t.string "unconfirmed_email" - t.boolean "hide_no_ssh_key", default: false - t.string "website_url", default: "", null: false + t.boolean "hide_no_ssh_key", default: false + t.string "website_url", default: "", null: false end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree @@ -338,8 +342,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do t.integer "group_access", null: false t.integer "group_id", null: false t.integer "user_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.integer "notification_level", default: 3, null: false end @@ -348,8 +352,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do create_table "users_projects", force: true do |t| t.integer "user_id", null: false t.integer "project_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.integer "project_access", default: 0, null: false t.integer "notification_level", default: 3, null: false end @@ -361,13 +365,14 @@ ActiveRecord::Schema.define(version: 20140304005354) do create_table "web_hooks", force: true do |t| t.string "url" t.integer "project_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at" + t.datetime "updated_at" t.string "type", default: "ProjectHook" t.integer "service_id" t.boolean "push_events", default: true, null: false t.boolean "issues_events", default: false, null: false t.boolean "merge_requests_events", default: false, null: false + t.boolean "tag_push_events", default: false end add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree diff --git a/doc/api/projects.md b/doc/api/projects.md index e795298c672..54618d7c045 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -148,6 +148,16 @@ Parameters: "path": "diaspora", "updated_at": "2013-09-30T13: 46: 02Z" } + "permissions": { + "project_access": { + "access_level": 10, + "notification_level": 3 + }, + "group_access": { + "access_level": 50, + "notification_level": 3 + } + } } ``` @@ -611,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 355ce31c126..2e87ada1d70 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/architecture.md b/doc/development/architecture.md index 8a772344b79..805e115047a 100644 --- a/doc/development/architecture.md +++ b/doc/development/architecture.md @@ -3,7 +3,7 @@ # Software delivery -There are two editions of GitLab: [Enterprise Edition](https://www.gitlab.com/features/) (EE) and [Community Edition](http://gitlab.org/gitlab-ce/) (CE). +There are two editions of GitLab: [Enterprise Edition](https://www.gitlab.com/gitlab-ee/) (EE) and [Community Edition](https://www.gitlab.com/gitlab-ce/) (CE). GitLab CE is delivered via git from the [gitlabhq repository](https://gitlab.com/gitlab-org/gitlab-ce/tree/master). New versions of GitLab are released in stable branches and the master branch is for bleeding edge development. @@ -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). diff --git a/doc/development/shell_commands.md b/doc/development/shell_commands.md index 57b1172d5e6..af0d5ca4426 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/installation.md b/doc/install/installation.md index d6e0a8a0012..addb21b50e0 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -4,7 +4,7 @@ this should be the highest numbered stable branch (example shown below). ![capture](http://i.imgur.com/d2AlIVj.png) -If this is unclear check the [GitLab Blog](http://blog.gitlab.org/) for installation guide links by version. +If this is unclear check the [GitLab Blog](https://www.gitlab.com/blog/) for installation guide links by version. # Important notes @@ -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 @@ -144,7 +144,7 @@ GitLab Shell is an ssh access and repository management software developed speci # 5. Database -We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](doc/install/database_mysql.md). +We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](database_mysql.md). # Install the database packages sudo apt-get install -y postgresql-9.1 postgresql-client libpq-dev @@ -173,7 +173,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](do ## 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 @@ -351,7 +351,7 @@ If all items are green, then congratulations on successfully installing GitLab! Visit YOUR_SERVER in your web browser for your first GitLab login. The setup has created an admin account for you. You can use it to log in: - admin@local.host + root 5iveL!fe **Important Note:** diff --git a/doc/integration/external-issue-tracker.md b/doc/integration/external-issue-tracker.md index 02eadfd410a..3212ebd64b5 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. ![jira screenshot](jira-intergration-points.png) + +You can configure the integration in the gitlab.yml configuration file. diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md index f49f09756ab..e7ebc613431 100644 --- a/doc/markdown/markdown.md +++ b/doc/markdown/markdown.md @@ -8,23 +8,38 @@ Table of Contents **[GitLab Flavored Markdown](#gitlab-flavored-markdown-gfm)** [Newlines](#newlines) + [Multiple underscores in words](#multiple-underscores-in-words) + [URL autolinking](#url-autolinking) + [Code and Syntax Highlighting](#code-and-syntax-highlighting) + [Emoji](#emoji) + [Special GitLab references](#special-gitlab-references) + **[Standard Markdown](#standard-markdown)** [Headers](#headers) + [Emphasis](#emphasis) + [Lists](#lists) + [Links](#links) + [Images](#images) + [Blockquotes](#blockquotes) + [Inline HTML](#inline-html) + [Horizontal Rule](#horizontal-rule) + [Line Breaks](#line-breaks) + [Tables](#tables) **[References](#references)** @@ -33,7 +48,8 @@ Table of Contents GitLab Flavored Markdown (GFM) ============================== -For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). It extends the standard Markdown in a few significant ways to add some useful functionality. +For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). +It extends the standard Markdown in a few significant ways to add some useful functionality. You can use GFM in @@ -51,16 +67,20 @@ Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#mar Newlines -------- -The biggest difference that GFM introduces is in the handling of linebreaks. With traditional Markdown you can hard wrap paragraphs of text and they will be combined into a single paragraph. We find this to be the cause of a huge number of unintentional formatting errors. GFM treats newlines in paragraph-like content as real line breaks, which is probably what you intended. +GFM honors the markdown specification in how [paragraphs and line breaks are handled](http://daringfireball.net/projects/markdown/syntax#p). -The next paragraph contains two phrases separated by a single newline character: +A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.: Roses are red Violets are blue + Sugar is sweet + Roses are red Violets are blue +Sugar is sweet + Multiple underscores in words ----------------------------- It is not reasonable to italicize just _part_ of a word, especially when you're dealing with code and names that often appear with multiple underscores. Therefore, GFM ignores multiple underscores in words. @@ -319,13 +339,13 @@ Strikethrough uses two tildes. ~~Scratch this.~~ ## Links -There are two ways to create links. +There are two ways to create links, inline-style and reference-style. [I'm an inline-style link](https://www.google.com) [I'm a reference-style link][Arbitrary case-insensitive reference text] - [I'm a relative reference to a repository file](../blob/master/LICENSE) + [I'm a relative reference to a repository file](LICENSE) [You can use numbers for reference-style link definitions][1] @@ -341,7 +361,7 @@ There are two ways to create links. [I'm a reference-style link][Arbitrary case-insensitive reference text] -[I'm a relative reference to a repository file](../blob/master/LICENSE) +[I'm a relative reference to a repository file](LICENSE) [You can use numbers for reference-style link definitions][1] @@ -353,6 +373,15 @@ Some text to show that the reference links can follow later. [1]: http://slashdot.org [link text itself]: http://www.reddit.com +**Note** + +Relative links do not allow referencing project files in a wiki page or wiki page in a project file. +The reason for this is that, in GitLab, wiki is always a separate git repository. For example: + +`[I'm a reference-style link][style]` + +will point the link to `wikis/style` when the link is inside of a wiki markdown file. + ## Images Here's our logo (hover to see the title text): @@ -365,15 +394,15 @@ Some text to show that the reference links can follow later. [logo]: assets/logo-white.png -Here's our logo (hover to see the title text): +Here's our logo: Inline-style: -![alt text](/assets/logo-white.png "Logo Title Text 1") +![alt text](/assets/logo-white.png) Reference-style: ![alt text][logo] -[logo]: /assets/logo-white.png "Logo Title Text 2" +[logo]: /assets/logo-white.png ## Blockquotes diff --git a/doc/release/monthly.md b/doc/release/monthly.md index 28fc260b334..08149b4da86 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/release/security.md b/doc/release/security.md index 7ec3991de85..8e5a7e32099 100644 --- a/doc/release/security.md +++ b/doc/release/security.md @@ -15,7 +15,7 @@ Please report suspected security vulnerabilities in private to support@gitlab.co 1. Acknowledge the issue to the researcher that disclosed it 1. Fix the issue on a feature branch, do this on the private GitLab development server and update the VERSION and CHANGELOG in this branch 1. Consider creating and testing workarounds -1. Create feature branches for the blog posts on GitLab.org and GitLab.com and link them from the code branch +1. Create feature branches for the blog post on GitLab.com and link them from the code branch 1. Merge the code feature branch into master 1. Cherry-pick the code into the latest stable branch 1. Create a git tag vX.X.X for CE and another patch release for EE diff --git a/doc/ssh/deploy_keys.md b/doc/ssh/deploy_keys.md new file mode 100644 index 00000000000..56fe98bb101 --- /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/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md index fe32ca1a9fa..76ca2a59911 100644 --- a/doc/system_hooks/system_hooks.md +++ b/doc/system_hooks/system_hooks.md @@ -11,8 +11,8 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser "created_at": "2012-07-21T07:30:54Z", "event_name": "project_create", "name": "StoreCloud", - "owner_email": "johnsmith@gmail.com" - "owner_name": "John Smit", + "owner_email": "johnsmith@gmail.com", + "owner_name": "John Smith", "path": "stormcloud", "path_with_namespace": "jsmith/stormcloud", "project_id": 74, @@ -26,7 +26,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser "created_at": "2012-07-21T07:30:58Z", "event_name": "project_destroy", "name": "Underscore", - "owner_email": "johnsmith@gmail.com" + "owner_email": "johnsmith@gmail.com", "owner_name": "John Smith", "path": "underscore", "path_with_namespace": "jsmith/underscore", @@ -36,7 +36,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser **New Team Member:** -```ruby +```json { "created_at": "2012-07-21T07:30:56Z", "event_name": "user_add_to_team", diff --git a/doc/update/4.2-to-5.0.md b/doc/update/4.2-to-5.0.md index 5bf8c367734..f8fb607e016 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 24d96e43bad..ba56507dd81 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 e4eaee91b8e..466c815195f 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 39cacd381a3..56f4854daf0 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 fa0f9ce54b6..5b74b1f893d 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 7f89f6bf887..e3c3016db64 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 7a24c11c223..213ce77ec38 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 312e9a46080..c289fcb57fd 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 b7fd7634859..b4cc9203587 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 index e01cc589a83..68878bb9cd9 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 3c453a5ab42..c618e599dcb 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 new file mode 100644 index 00000000000..8a16e5d67be --- /dev/null +++ b/doc/update/6.6-to-6.7.md @@ -0,0 +1,98 @@ +# From 6.6 to 6.7 + +### 0. Backup + +```bash +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production +``` + +### 1. Stop server + + sudo service gitlab stop + +### 2. Get latest code + +```bash +cd /home/git/gitlab +sudo -u git -H git fetch --all +``` + +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 +``` + +### 3. Update gitlab-shell (and its config) + +```bash +cd /home/git/gitlab-shell +sudo -u git -H git fetch +sudo -u git -H git checkout v1.9.1 +``` + +### 4. Install libs, migrations, etc. + +```bash +cd /home/git/gitlab + +# MySQL installations (note: the line below states '--without ... postgres') +sudo -u git -H bundle install --without development test postgres --deployment + +# PostgreSQL installations (note: the line below states '--without ... mysql') +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 + +# Clean up assets and cache +sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production + +# Update init.d script +sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab + +# Update the logrotate configuration (keep logs for 90 days instead of 52 weeks) +sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab +``` + + +### 5. Start application + + sudo service gitlab start + sudo service nginx restart + +### 6. Check application status + +Check if GitLab and its environment are configured correctly: + + sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production + +To make sure you didn't miss anything run a more thorough check with: + + sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production + +If all items are green, then congratulations upgrade is complete! + +## Things went south? Revert to previous version (6.6) + +### 1. Revert the code to the previous version +Follow the [`upgrade guide from 6.5 to 6.6`](6.5-to-6.6.md), except for the database migration +(The backup is already migrated to the previous version) + +### 2. Restore from the backup: + +```bash +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production +``` +If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. diff --git a/doc/update/mysql-to-postgresql.md b/doc/update/mysql-to-postgresql.md new file mode 100644 index 00000000000..9a324545eb0 --- /dev/null +++ b/doc/update/mysql-to-postgresql.md @@ -0,0 +1,9 @@ +# Use the shell commands below to convert a MySQL GitLab database to a PostgreSQL one. + +``` +git clone https://github.com/lanyrd/mysql-postgresql-converter.git +cd mysql-postgresql-converter +mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production +python db_converter.py databasename.mysql databasename.psql +psql -f databasename.psql -d gitlabhq_production +``` diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index 21b93c0b929..2b947adaa13 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -18,10 +18,11 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production ```bash cd /home/git/gitlab -sudo -u git -H git pull origin STABLE_BRANCH +sudo -u git -H git fetch --all +sudo -u git -H git checkout LATEST_TAG ``` -Replace STABLE_BRANCH with the minor version you want to upgrade to, for example `6-3-stable`. +Replace LATEST_TAG with the latest GitLab tag you want to upgrade to, for example `v6.6.3`. ### 3. Update gitlab-shell if it is not the latest version diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index 64cce45945e..f80891e264d 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/group.feature b/features/group.feature index 71282fb75ba..4e11bcba939 100644 --- a/features/group.feature +++ b/features/group.feature @@ -74,7 +74,7 @@ Feature: Groups When I visit group "Owned" members page Then I should see user "John Doe" in team list Then I should see user "Mary Jane" in team list - Then I should not see the "Remove User From Group" button for "Mary Jane" + Then I should not see the "Remove User From Group" button for "John Doe" @javascript Scenario: Guest should be able to remove himself from group diff --git a/features/project/archived_projects.feature b/features/project/archived.feature index 9aac29384ba..9aac29384ba 100644 --- a/features/project/archived_projects.feature +++ b/features/project/archived.feature diff --git a/features/project/commits/commit_comments.feature b/features/project/commits/comments.feature index a1aa745a681..a1aa745a681 100644 --- a/features/project/commits/commit_comments.feature +++ b/features/project/commits/comments.feature diff --git a/features/project/commits/commit_diff_comments.feature b/features/project/commits/diff_comments.feature index b26019f832f..b26019f832f 100644 --- a/features/project/commits/commit_diff_comments.feature +++ b/features/project/commits/diff_comments.feature diff --git a/features/project/commits/commits_user_lookup.feature b/features/project/commits/user_lookup.feature index f3864c0ab38..f3864c0ab38 100644 --- a/features/project/commits/commits_user_lookup.feature +++ b/features/project/commits/user_lookup.feature diff --git a/features/project/create_project.feature b/features/project/create.feature index 395a3218b2b..bb8e3a368ed 100644 --- a/features/project/create_project.feature +++ b/features/project/create.feature @@ -3,6 +3,7 @@ Feature: Create Project A user with ability to create a project Should be able to create a new one + @javascript Scenario: User create a project Given I sign in as a user When I visit new project page @@ -19,4 +20,4 @@ Feature: Create Project And I click on HTTP Then Remote url should update to http link And If I click on SSH - Then Remote url should update to ssh link
\ No newline at end of file + Then Remote url should update to ssh link diff --git a/features/project/fork_project.feature b/features/project/fork.feature index dc477ca3bf3..dc477ca3bf3 100644 --- a/features/project/fork_project.feature +++ b/features/project/fork.feature diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index 435c47a276c..6a2ba7e3b28 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -95,3 +95,47 @@ Feature: Project Merge Requests Given I visit merge request page "Bug NS-04" And I leave a comment with a header containing "Comment with a header" Then The comment with the header should not have an ID + + # Toggling inline comments + + @javascript + Scenario: I hide comments on a merge request diff with comments in a single file + Given project "Shop" have "Bug NS-05" open merge request with diffs inside + And I visit merge request page "Bug NS-05" + And I switch to the diff tab + And I leave a comment like "Line is wrong" on line 39 of the second file + And I click link "Hide inline discussion" of the second file + Then I should not see a comment like "Line is wrong" in the second file + + @javascript + Scenario: I show comments on a merge request diff with comments in a single file + Given project "Shop" have "Bug NS-05" open merge request with diffs inside + And I visit merge request page "Bug NS-05" + And I switch to the diff tab + And I leave a comment like "Line is wrong" on line 39 of the second file + And I click link "Hide inline discussion" of the second file + And I click link "Show inline discussion" of the second file + Then I should see a comment like "Line is wrong" in the second file + + @javascript + Scenario: I hide comments on a merge request diff with comments in multiple files + Given project "Shop" have "Bug NS-05" open merge request with diffs inside + And I visit merge request page "Bug NS-05" + And I switch to the diff tab + And I leave a comment like "Line is correct" on line 12 of the first file + And I leave a comment like "Line is wrong" on line 39 of the second file + And I click link "Hide inline discussion" of the second file + Then I should not see a comment like "Line is wrong" in the second file + And I should still see a comment like "Line is correct" in the first file + + @javascript + Scenario: I show comments on a merge request diff with comments in multiple files + Given project "Shop" have "Bug NS-05" open merge request with diffs inside + And I visit merge request page "Bug NS-05" + And I switch to the diff tab + And I leave a comment like "Line is correct" on line 12 of the first file + And I leave a comment like "Line is wrong" on line 39 of the second file + And I click link "Hide inline discussion" of the second file + And I click link "Show inline discussion" of the second file + Then I should see a comment like "Line is wrong" in the second file + And I should still see a comment like "Line is correct" in the first file diff --git a/features/project/public_projects.feature b/features/project/public.feature index c5a9da14c54..c5a9da14c54 100644 --- a/features/project/public_projects.feature +++ b/features/project/public.feature diff --git a/features/project/service.feature b/features/project/service.feature index 46b983e8f9a..a5af065c9e7 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/public/public_projects.feature b/features/public/projects.feature index 57fe834b4bf..57fe834b4bf 100644 --- a/features/public/public_projects.feature +++ b/features/public/projects.feature diff --git a/features/snippets/discover_snippets.feature b/features/snippets/discover.feature index d6fd2cd7808..d6fd2cd7808 100644 --- a/features/snippets/discover_snippets.feature +++ b/features/snippets/discover.feature diff --git a/features/snippets/user_snippets.feature b/features/snippets/user.feature index 4c8a91501c4..4c8a91501c4 100644 --- a/features/snippets/user_snippets.feature +++ b/features/snippets/user.feature diff --git a/features/steps/admin/admin_active_tab.rb b/features/steps/admin/active_tab.rb index ccafe09c18f..ccafe09c18f 100644 --- a/features/steps/admin/admin_active_tab.rb +++ b/features/steps/admin/active_tab.rb diff --git a/features/steps/admin/admin_broadcast_messages.rb b/features/steps/admin/broadcast_messages.rb index a35fa34a3a2..a35fa34a3a2 100644 --- a/features/steps/admin/admin_broadcast_messages.rb +++ b/features/steps/admin/broadcast_messages.rb diff --git a/features/steps/admin/admin_groups.rb b/features/steps/admin/groups.rb index 9c1bcfefb9c..9c1bcfefb9c 100644 --- a/features/steps/admin/admin_groups.rb +++ b/features/steps/admin/groups.rb diff --git a/features/steps/admin/admin_logs.rb b/features/steps/admin/logs.rb index 83958545c4d..83958545c4d 100644 --- a/features/steps/admin/admin_logs.rb +++ b/features/steps/admin/logs.rb diff --git a/features/steps/admin/admin_projects.rb b/features/steps/admin/projects.rb index b410b23851b..b410b23851b 100644 --- a/features/steps/admin/admin_projects.rb +++ b/features/steps/admin/projects.rb diff --git a/features/steps/admin/admin_users.rb b/features/steps/admin/users.rb index 659008dd875..659008dd875 100644 --- a/features/steps/admin/admin_users.rb +++ b/features/steps/admin/users.rb diff --git a/features/steps/dashboard/dashboard_active_tab.rb b/features/steps/dashboard/active_tab.rb index 8f5f0eed816..8f5f0eed816 100644 --- a/features/steps/dashboard/dashboard_active_tab.rb +++ b/features/steps/dashboard/active_tab.rb diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb index 3526006c94a..394acd3fe8f 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/dashboard/dashboard_event_filters.rb b/features/steps/dashboard/event_filters.rb index d0fe5c9b64b..d0fe5c9b64b 100644 --- a/features/steps/dashboard/dashboard_event_filters.rb +++ b/features/steps/dashboard/event_filters.rb diff --git a/features/steps/dashboard/dashboard_issues.rb b/features/steps/dashboard/issues.rb index 1344edfa80b..1344edfa80b 100644 --- a/features/steps/dashboard/dashboard_issues.rb +++ b/features/steps/dashboard/issues.rb diff --git a/features/steps/dashboard/dashboard_merge_requests.rb b/features/steps/dashboard/merge_requests.rb index 62d84506c49..62d84506c49 100644 --- a/features/steps/dashboard/dashboard_merge_requests.rb +++ b/features/steps/dashboard/merge_requests.rb diff --git a/features/steps/dashboard/dashboard_projects.rb b/features/steps/dashboard/projects.rb index 85251565446..85251565446 100644 --- a/features/steps/dashboard/dashboard_projects.rb +++ b/features/steps/dashboard/projects.rb diff --git a/features/steps/dashboard/dashboard_search.rb b/features/steps/dashboard/search.rb index 32966a8617a..32966a8617a 100644 --- a/features/steps/dashboard/dashboard_search.rb +++ b/features/steps/dashboard/search.rb diff --git a/features/steps/dashboard/dashboard_with_archived_projects.rb b/features/steps/dashboard/with_archived_projects.rb index 1bc69555b56..1bc69555b56 100644 --- a/features/steps/dashboard/dashboard_with_archived_projects.rb +++ b/features/steps/dashboard/with_archived_projects.rb diff --git a/features/steps/profile/profile_active_tab.rb b/features/steps/profile/active_tab.rb index ee9f5f201cf..ee9f5f201cf 100644 --- a/features/steps/profile/profile_active_tab.rb +++ b/features/steps/profile/active_tab.rb diff --git a/features/steps/profile/profile_emails.rb b/features/steps/profile/emails.rb index 99588c85991..99588c85991 100644 --- a/features/steps/profile/profile_emails.rb +++ b/features/steps/profile/emails.rb diff --git a/features/steps/profile/profile_notifications.rb b/features/steps/profile/notifications.rb index e884df3098e..e884df3098e 100644 --- a/features/steps/profile/profile_notifications.rb +++ b/features/steps/profile/notifications.rb diff --git a/features/steps/profile/profile_ssh_keys.rb b/features/steps/profile/ssh_keys.rb index 65ca824bb5b..65ca824bb5b 100644 --- a/features/steps/profile/profile_ssh_keys.rb +++ b/features/steps/profile/ssh_keys.rb diff --git a/features/steps/project/project_active_tab.rb b/features/steps/project/active_tab.rb index dcc252f4765..dcc252f4765 100644 --- a/features/steps/project/project_active_tab.rb +++ b/features/steps/project/active_tab.rb diff --git a/features/steps/project/project_archived.rb b/features/steps/project/archived.rb index dfbe762c438..dfbe762c438 100644 --- a/features/steps/project/project_archived.rb +++ b/features/steps/project/archived.rb diff --git a/features/steps/project/project_browse_branches.rb b/features/steps/project/browse_branches.rb index 30c8cef80c8..30c8cef80c8 100644 --- a/features/steps/project/project_browse_branches.rb +++ b/features/steps/project/browse_branches.rb diff --git a/features/steps/project/project_browse_commits.rb b/features/steps/project/browse_commits.rb index d667b58240f..d667b58240f 100644 --- a/features/steps/project/project_browse_commits.rb +++ b/features/steps/project/browse_commits.rb diff --git a/features/steps/project/project_browse_commits_user_lookup.rb b/features/steps/project/browse_commits_user_lookup.rb index 328be373553..328be373553 100644 --- a/features/steps/project/project_browse_commits_user_lookup.rb +++ b/features/steps/project/browse_commits_user_lookup.rb diff --git a/features/steps/project/project_browse_files.rb b/features/steps/project/browse_files.rb index 069086d5eac..069086d5eac 100644 --- a/features/steps/project/project_browse_files.rb +++ b/features/steps/project/browse_files.rb diff --git a/features/steps/project/project_browse_git_repo.rb b/features/steps/project/browse_git_repo.rb index cd9a60f49cb..cd9a60f49cb 100644 --- a/features/steps/project/project_browse_git_repo.rb +++ b/features/steps/project/browse_git_repo.rb diff --git a/features/steps/project/project_browse_tags.rb b/features/steps/project/browse_tags.rb index 0cbfa0d80aa..0cbfa0d80aa 100644 --- a/features/steps/project/project_browse_tags.rb +++ b/features/steps/project/browse_tags.rb diff --git a/features/steps/project/create_project.rb b/features/steps/project/create.rb index b59345e7078..b42e5bd3623 100644 --- a/features/steps/project/create_project.rb +++ b/features/steps/project/create.rb @@ -8,8 +8,8 @@ class CreateProject < Spinach::FeatureSteps end Then 'I should see project page' do - current_path.should == project_path(Project.last) page.should have_content "Empty" + current_path.should == project_path(Project.last) end And 'I should see empty project instuctions' do diff --git a/features/steps/project/project_filter_labels.rb b/features/steps/project/filter_labels.rb index 5926d69d6c7..5926d69d6c7 100644 --- a/features/steps/project/project_filter_labels.rb +++ b/features/steps/project/filter_labels.rb diff --git a/features/steps/project/project_fork.rb b/features/steps/project/fork.rb index c00d9014b1d..c00d9014b1d 100644 --- a/features/steps/project/project_fork.rb +++ b/features/steps/project/fork.rb diff --git a/features/steps/project/project_forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index df69cb75437..df69cb75437 100644 --- a/features/steps/project/project_forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb diff --git a/features/steps/project/project_graph.rb b/features/steps/project/graph.rb index 89fe5fdeadf..89fe5fdeadf 100644 --- a/features/steps/project/project_graph.rb +++ b/features/steps/project/graph.rb diff --git a/features/steps/project/project_hooks.rb b/features/steps/project/hooks.rb index 19ff3244543..19ff3244543 100644 --- a/features/steps/project/project_hooks.rb +++ b/features/steps/project/hooks.rb diff --git a/features/steps/project/project_issue_tracker.rb b/features/steps/project/issue_tracker.rb index c2fd4e15c9e..c2fd4e15c9e 100644 --- a/features/steps/project/project_issue_tracker.rb +++ b/features/steps/project/issue_tracker.rb diff --git a/features/steps/project/project_issues.rb b/features/steps/project/issues.rb index a92fd50584d..a92fd50584d 100644 --- a/features/steps/project/project_issues.rb +++ b/features/steps/project/issues.rb diff --git a/features/steps/project/project_labels.rb b/features/steps/project/labels.rb index 0907cdb526f..0907cdb526f 100644 --- a/features/steps/project/project_labels.rb +++ b/features/steps/project/labels.rb diff --git a/features/steps/project/project_markdown_render.rb b/features/steps/project/markdown_render.rb index 8fbf2753aa7..c94de283069 100644 --- a/features/steps/project/project_markdown_render.rb +++ b/features/steps/project/markdown_render.rb @@ -203,7 +203,7 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps end And 'I add various links to the wiki page' do - fill_in "wiki[content]", with: "[test](test)\n[GitLab API doc](doc/api/README.md)\n[Rake tasks](doc/raketasks)\n" + fill_in "wiki[content]", with: "[test](test)\n[GitLab API doc](api)\n[Rake tasks](raketasks)\n" fill_in "wiki[message]", with: "Adding links to wiki" click_button "Create page" end @@ -242,8 +242,8 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps end Then 'I see Gitlab API document' do - current_path.should == project_blob_path(@project, "master/doc/api/README.md") - page.should have_content "Status codes" + current_path.should == project_wiki_path(@project, "api") + page.should have_content "Editing" end And 'I click on Rake tasks link' do @@ -251,9 +251,8 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps end Then 'I see Rake tasks directory' do - current_path.should == project_tree_path(@project, "master/doc/raketasks") - page.should have_content "backup_restore.md" - page.should have_content "maintenance.md" + current_path.should == project_wiki_path(@project, "raketasks") + page.should have_content "Editing" end Given 'I go directory which contains README file' do diff --git a/features/steps/project/project_merge_requests.rb b/features/steps/project/merge_requests.rb index 813e7439b2c..f42eb6377ce 100644 --- a/features/steps/project/project_merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -182,6 +182,62 @@ class ProjectMergeRequests < Spinach::FeatureSteps end end + step 'I click link "Hide inline discussion" of the second file' do + within '.files [id^=diff]:nth-child(2)' do + click_link "Diff comments" + end + end + + step 'I click link "Show inline discussion" of the second file' do + within '.files [id^=diff]:nth-child(2)' do + click_link "Diff comments" + end + end + + step 'I should not see a comment like "Line is wrong" in the second file' do + within '.files [id^=diff]:nth-child(2)' do + page.should_not have_visible_content "Line is wrong" + end + end + + step 'I should see a comment like "Line is wrong" in the second file' do + within '.files [id^=diff]:nth-child(2) .note-text' do + page.should have_visible_content "Line is wrong" + end + end + + step 'I leave a comment like "Line is correct" on line 12 of the first file' do + init_diff_note_first_file + + within(".js-discussion-note-form") do + fill_in "note_note", with: "Line is correct" + click_button "Add Comment" + end + + within ".files [id^=diff]:nth-child(1) .note-text" do + page.should have_content "Line is correct" + end + end + + step 'I leave a comment like "Line is wrong" on line 39 of the second file' do + init_diff_note_second_file + + within(".js-discussion-note-form") do + fill_in "note_note", with: "Line is wrong" + click_button "Add Comment" + end + + within ".files [id^=diff]:nth-child(2) .note-text" do + page.should have_content "Line is wrong" + end + end + + step 'I should still see a comment like "Line is correct" in the first file' do + within '.files [id^=diff]:nth-child(1) .note-text' do + page.should have_visible_content "Line is correct" + end + end + def project @project ||= Project.find_by!(name: "Shop") end @@ -204,4 +260,16 @@ class ProjectMergeRequests < Spinach::FeatureSteps page.should have_content message end end + + def init_diff_note_first_file + find('a[data-line-code="a5cc2925ca8258af241be7e5b0381edf30266302_12_12"]').click + end + + def init_diff_note_second_file + find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_28_39"]').click + end + + def have_visible_content (text) + have_css("*", text: text, visible: true) + end end diff --git a/features/steps/project/project_milestones.rb b/features/steps/project/milestones.rb index 9ce18fbaabd..9ce18fbaabd 100644 --- a/features/steps/project/project_milestones.rb +++ b/features/steps/project/milestones.rb diff --git a/features/steps/project/project_multiselect_blob.rb b/features/steps/project/multiselect_blob.rb index 3d330e837c1..3d330e837c1 100644 --- a/features/steps/project/project_multiselect_blob.rb +++ b/features/steps/project/multiselect_blob.rb diff --git a/features/steps/project/project_network_graph.rb b/features/steps/project/network_graph.rb index 1c5cfcc6c68..1c5cfcc6c68 100644 --- a/features/steps/project/project_network_graph.rb +++ b/features/steps/project/network_graph.rb diff --git a/features/steps/project/public_projects.rb b/features/steps/project/public.rb index 7063e7d56ae..7063e7d56ae 100644 --- a/features/steps/project/public_projects.rb +++ b/features/steps/project/public.rb diff --git a/features/steps/project/redirects.rb b/features/steps/project/redirects.rb index 76ffea1bb6f..cfa4ce82be3 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/project_search_code.rb b/features/steps/project/search_code.rb index d117b019a15..d117b019a15 100644 --- a/features/steps/project/project_search_code.rb +++ b/features/steps/project/search_code.rb diff --git a/features/steps/project/project_services.rb b/features/steps/project/services.rb index 54b3f18e084..0594a08a5e7 100644 --- a/features/steps/project/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/project/project_snippets.rb b/features/steps/project/snippets.rb index c3a76bea269..c3a76bea269 100644 --- a/features/steps/project/project_snippets.rb +++ b/features/steps/project/snippets.rb diff --git a/features/steps/project/project_team_management.rb b/features/steps/project/team_management.rb index ffc5016529f..ffc5016529f 100644 --- a/features/steps/project/project_team_management.rb +++ b/features/steps/project/team_management.rb diff --git a/features/steps/project/project_wall.rb b/features/steps/project/wall.rb index 7c61580eb2c..7c61580eb2c 100644 --- a/features/steps/project/project_wall.rb +++ b/features/steps/project/wall.rb diff --git a/features/steps/project/project_wiki.rb b/features/steps/project/wiki.rb index 6146599cc4a..6146599cc4a 100644 --- a/features/steps/project/project_wiki.rb +++ b/features/steps/project/wiki.rb diff --git a/features/steps/public/projects_feature.rb b/features/steps/public/projects.rb index eb1d235f435..7c7311bb91c 100644 --- a/features/steps/public/projects_feature.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 f35beab8af2..f8cb753b78f 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/features/steps/snippets/discover_snippets.rb b/features/steps/snippets/discover.rb index 09337937002..09337937002 100644 --- a/features/steps/snippets/discover_snippets.rb +++ b/features/steps/snippets/discover.rb diff --git a/features/steps/snippets/user_snippets.rb b/features/steps/snippets/user.rb index 2d7ffc866e7..2d7ffc866e7 100644 --- a/features/steps/snippets/user_snippets.rb +++ b/features/steps/snippets/user.rb diff --git a/lib/api/api.rb b/lib/api/api.rb index 6bec8368b12..7c4cdad7f0d 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -38,6 +38,7 @@ module API mount Internal mount SystemHooks mount ProjectSnippets + mount ProjectMembers mount DeployKeys mount ProjectHooks mount Services diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 8557fa074d4..9fa8506926c 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -15,7 +15,7 @@ module API end class UserSafe < Grape::Entity - expose :name + expose :name, :username end class UserBasic < Grape::Entity @@ -44,7 +44,7 @@ module API expose :id, :description, :default_branch expose :public?, as: :public expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url - expose :owner, using: Entities::UserBasic + expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group } expose :name, :name_with_namespace expose :path, :path_with_namespace expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at @@ -58,18 +58,6 @@ module API end end - class TeamMember < UserBasic - expose :permission, as: :access_level do |user, options| - options[:user_team].user_team_user_relationships.find_by(user_id: user.id).permission - end - end - - class TeamProject < Project - expose :greatest_access, as: :greatest_access_level do |project, options| - options[:user_team].user_team_project_relationships.find_by(project_id: project.id).greatest_access - end - end - class Group < Grape::Entity expose :id, :name, :path, :owner_id end @@ -144,7 +132,7 @@ module API end class MergeRequest < ProjectEntity - expose :target_branch, :source_branch, :title, :state, :upvotes, :downvotes + expose :target_branch, :source_branch, :title, :state, :upvotes, :downvotes, :description expose :author, :assignee, using: Entities::UserBasic expose :source_project_id, :target_project_id end @@ -175,5 +163,33 @@ module API class Namespace < Grape::Entity expose :id, :path, :kind end + + class ProjectAccess < Grape::Entity + expose :project_access, as: :access_level + expose :notification_level + end + + class GroupAccess < Grape::Entity + expose :group_access, as: :access_level + expose :notification_level + end + + class ProjectWithAccess < Project + expose :permissions do + expose :project_access, using: Entities::ProjectAccess do |project, options| + project.users_projects.find_by(user_id: options[:user].id) + end + + expose :group_access, using: Entities::GroupAccess do |project, options| + if project.group + project.group.users_groups.find_by(user_id: options[:user].id) + end + end + end + end + + class Label < Grape::Entity + expose :name + end end end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index f8c48e2f3b2..fc309f65a56 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -47,7 +47,7 @@ module API end def find_project(id) - project = Project.find_by(id: id) || Project.find_with_namespace(id) + project = Project.find_with_namespace(id) || Project.find_by(id: id) if project && can?(current_user, :read_project, project) project diff --git a/lib/api/internal.rb b/lib/api/internal.rb index ebc9fef07b4..bcf97574673 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,37 +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 - return false if user.ldap_user? && Gitlab::LDAP::User.blocked?(user.extern_uid) - 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 58d2f79faff..e2458198411 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -64,6 +64,7 @@ module API # target_project - The target project of the merge request defaults to the :id of the project # assignee_id - Assignee user ID # title (required) - Title of MR + # description - Description of MR # # Example: # POST /projects/:id/merge_requests @@ -72,7 +73,7 @@ module API set_current_user_for_thread do authorize! :write_merge_request, user_project required_attributes! [:source_branch, :target_branch, :title] - attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id] + attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description] merge_request = user_project.merge_requests.new(attrs) merge_request.author = current_user merge_request.source_project = user_project @@ -105,12 +106,13 @@ module API # assignee_id - Assignee user ID # title - Title of MR # state_event - Status of MR. (close|reopen|merge) + # description - Description of MR # Example: # PUT /projects/:id/merge_request/:merge_request_id # put ":id/merge_request/:merge_request_id" do set_current_user_for_thread do - attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event] + attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event, :description] merge_request = user_project.merge_requests.find(params[:merge_request_id]) authorize! :modify_merge_request, merge_request diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb index c271dd8b61b..79c3d122d32 100644 --- a/lib/api/project_hooks.rb +++ b/lib/api/project_hooks.rb @@ -5,15 +5,6 @@ module API before { authorize_admin_project } resource :projects do - helpers do - def handle_project_member_errors(errors) - if errors[:project_access].any? - error!(errors[:project_access], 422) - end - not_found! - end - end - # Get project hooks # # Parameters: diff --git a/lib/api/project_members.rb b/lib/api/project_members.rb new file mode 100644 index 00000000000..47c4ddce163 --- /dev/null +++ b/lib/api/project_members.rb @@ -0,0 +1,114 @@ +module API + # Projects members API + class ProjectMembers < Grape::API + before { authenticate! } + + resource :projects do + helpers do + def handle_project_member_errors(errors) + if errors[:project_access].any? + error!(errors[:project_access], 422) + end + not_found! + end + end + + # Get a project team members + # + # Parameters: + # id (required) - The ID of a project + # query - Query string + # Example Request: + # GET /projects/:id/members + get ":id/members" do + if params[:query].present? + @members = paginate user_project.users.where("username LIKE ?", "%#{params[:query]}%") + else + @members = paginate user_project.users + end + present @members, with: Entities::ProjectMember, project: user_project + end + + # Get a project team members + # + # Parameters: + # id (required) - The ID of a project + # user_id (required) - The ID of a user + # Example Request: + # GET /projects/:id/members/:user_id + get ":id/members/:user_id" do + @member = user_project.users.find params[:user_id] + present @member, with: Entities::ProjectMember, project: user_project + end + + # Add a new project team member + # + # Parameters: + # id (required) - The ID of a project + # user_id (required) - The ID of a user + # access_level (required) - Project access level + # Example Request: + # POST /projects/:id/members + post ":id/members" do + authorize! :admin_project, user_project + required_attributes! [:user_id, :access_level] + + # either the user is already a team member or a new one + team_member = user_project.team_member_by_id(params[:user_id]) + if team_member.nil? + team_member = user_project.users_projects.new( + user_id: params[:user_id], + project_access: params[:access_level] + ) + end + + if team_member.save + @member = team_member.user + present @member, with: Entities::ProjectMember, project: user_project + else + handle_project_member_errors team_member.errors + end + end + + # Update project team member + # + # Parameters: + # id (required) - The ID of a project + # user_id (required) - The ID of a team member + # access_level (required) - Project access level + # Example Request: + # PUT /projects/:id/members/:user_id + put ":id/members/:user_id" do + authorize! :admin_project, user_project + required_attributes! [:access_level] + + team_member = user_project.users_projects.find_by(user_id: params[:user_id]) + not_found!("User can not be found") if team_member.nil? + + if team_member.update_attributes(project_access: params[:access_level]) + @member = team_member.user + present @member, with: Entities::ProjectMember, project: user_project + else + handle_project_member_errors team_member.errors + end + end + + # Remove a team member from project + # + # Parameters: + # id (required) - The ID of a project + # user_id (required) - The ID of a team member + # Example Request: + # DELETE /projects/:id/members/:user_id + delete ":id/members/:user_id" do + authorize! :admin_project, user_project + team_member = user_project.users_projects.find_by(user_id: params[:user_id]) + unless team_member.nil? + team_member.destroy + else + {message: "Access revoked", id: params[:user_id].to_i} + end + end + end + end +end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index bcca69ff49a..9d290c75ba9 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -5,13 +5,6 @@ module API resource :projects do helpers do - def handle_project_member_errors(errors) - if errors[:project_access].any? - error!(errors[:project_access], 422) - end - not_found! - end - def map_public_to_visibility_level(attrs) publik = attrs.delete(:public) publik = [ true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON' ].include?(publik) @@ -55,7 +48,7 @@ module API # Example Request: # GET /projects/:id get ":id" do - present user_project, with: Entities::Project + present user_project, with: Entities::ProjectWithAccess, user: current_user end # Get a single project events @@ -196,104 +189,6 @@ module API user_project.forked_project_link.destroy end end - - # Get a project team members - # - # Parameters: - # id (required) - The ID of a project - # query - Query string - # Example Request: - # GET /projects/:id/members - get ":id/members" do - if params[:query].present? - @members = paginate user_project.users.where("username LIKE ?", "%#{params[:query]}%") - else - @members = paginate user_project.users - end - present @members, with: Entities::ProjectMember, project: user_project - end - - # Get a project team members - # - # Parameters: - # id (required) - The ID of a project - # user_id (required) - The ID of a user - # Example Request: - # GET /projects/:id/members/:user_id - get ":id/members/:user_id" do - @member = user_project.users.find params[:user_id] - present @member, with: Entities::ProjectMember, project: user_project - end - - # Add a new project team member - # - # Parameters: - # id (required) - The ID of a project - # user_id (required) - The ID of a user - # access_level (required) - Project access level - # Example Request: - # POST /projects/:id/members - post ":id/members" do - authorize! :admin_project, user_project - required_attributes! [:user_id, :access_level] - - # either the user is already a team member or a new one - team_member = user_project.team_member_by_id(params[:user_id]) - if team_member.nil? - team_member = user_project.users_projects.new( - user_id: params[:user_id], - project_access: params[:access_level] - ) - end - - if team_member.save - @member = team_member.user - present @member, with: Entities::ProjectMember, project: user_project - else - handle_project_member_errors team_member.errors - end - end - - # Update project team member - # - # Parameters: - # id (required) - The ID of a project - # user_id (required) - The ID of a team member - # access_level (required) - Project access level - # Example Request: - # PUT /projects/:id/members/:user_id - put ":id/members/:user_id" do - authorize! :admin_project, user_project - required_attributes! [:access_level] - - team_member = user_project.users_projects.find_by(user_id: params[:user_id]) - not_found!("User can not be found") if team_member.nil? - - if team_member.update_attributes(project_access: params[:access_level]) - @member = team_member.user - present @member, with: Entities::ProjectMember, project: user_project - else - handle_project_member_errors team_member.errors - end - end - - # Remove a team member from project - # - # Parameters: - # id (required) - The ID of a project - # user_id (required) - The ID of a team member - # Example Request: - # DELETE /projects/:id/members/:user_id - delete ":id/members/:user_id" do - authorize! :admin_project, user_project - team_member = user_project.users_projects.find_by(user_id: params[:user_id]) - unless team_member.nil? - team_member.destroy - else - {message: "Access revoked", id: params[:user_id].to_i} - end - end - # search for projects current_user has access to # # Parameters: @@ -320,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 60c03ce1c04..c2f3b851c07 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 cb747fe0137..00000000000 --- 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/backend/shell.rb b/lib/gitlab/backend/shell.rb index 7121c8e40d2..b93800e235f 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -2,6 +2,12 @@ module Gitlab class Shell class AccessDenied < StandardError; end + class KeyAdder < Struct.new(:io) + def add_key(id, key) + io.puts("#{id}\t#{key.strip}") + end + end + # Init new repository # # name - project path with namespace @@ -130,6 +136,16 @@ module Gitlab system "#{gitlab_shell_path}/bin/gitlab-keys", "add-key", key_id, key_content end + # Batch-add keys to authorized_keys + # + # Ex. + # batch_add_keys { |adder| adder.add_key("key-42", "sha-rsa ...") } + def batch_add_keys(&block) + IO.popen(%W(#{gitlab_shell_path}/bin/gitlab-keys batch-add-keys), 'w') do |io| + block.call(KeyAdder.new(io)) + end + end + # Remove ssh key from gitlab shell # # Ex. diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb new file mode 100644 index 00000000000..1ab8f9213a3 --- /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/ldap/access.rb b/lib/gitlab/ldap/access.rb new file mode 100644 index 00000000000..8f492e5c012 --- /dev/null +++ b/lib/gitlab/ldap/access.rb @@ -0,0 +1,23 @@ +module Gitlab + module LDAP + class Access + attr_reader :adapter + + def self.open(&block) + Gitlab::LDAP::Adapter.open do |adapter| + block.call(self.new(adapter)) + end + end + + def initialize(adapter=nil) + @adapter = adapter + end + + def allowed?(user) + !!Gitlab::LDAP::Person.find_by_dn(user.extern_uid, adapter) + rescue + false + end + end + end +end diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb new file mode 100644 index 00000000000..983a2956a35 --- /dev/null +++ b/lib/gitlab/ldap/adapter.rb @@ -0,0 +1,86 @@ +module Gitlab + module LDAP + class Adapter + attr_reader :ldap + + def self.open(&block) + Net::LDAP.open(adapter_options) do |ldap| + block.call(self.new(ldap)) + end + end + + def self.config + Gitlab.config.ldap + end + + def self.adapter_options + encryption = config['method'].to_s == 'ssl' ? :simple_tls : nil + + options = { + host: config['host'], + port: config['port'], + encryption: encryption + } + + auth_options = { + auth: { + method: :simple, + username: config['bind_dn'], + password: config['password'] + } + } + + if config['password'] || config['bind_dn'] + options.merge!(auth_options) + end + options + end + + + def initialize(ldap=nil) + @ldap = ldap || Net::LDAP.new(self.class.adapter_options) + end + + def users(field, value) + if field.to_sym == :dn + options = { + base: value + } + else + options = { + base: config['base'], + filter: Net::LDAP::Filter.eq(field, value) + } + end + + if config['user_filter'].present? + user_filter = Net::LDAP::Filter.construct(config['user_filter']) + + options[:filter] = if options[:filter] + Net::LDAP::Filter.join(options[:filter], user_filter) + else + user_filter + end + end + + entries = ldap.search(options).select do |entry| + entry.respond_to? config.uid + end + + entries.map do |entry| + Gitlab::LDAP::Person.new(entry) + end + end + + def user(*args) + users(*args).first + end + + private + + def config + @config ||= self.class.config + end + end + end +end diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb new file mode 100644 index 00000000000..06b17c58f8c --- /dev/null +++ b/lib/gitlab/ldap/person.rb @@ -0,0 +1,50 @@ +module Gitlab + module LDAP + class Person + def self.find_by_uid(uid, adapter=nil) + adapter ||= Gitlab::LDAP::Adapter.new + adapter.user(config.uid, uid) + end + + def self.find_by_dn(dn, adapter=nil) + adapter ||= Gitlab::LDAP::Adapter.new + adapter.user('dn', dn) + end + + def initialize(entry) + Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" } + @entry = entry + end + + def name + entry.cn.first + end + + def uid + entry.send(config.uid).first + end + + def username + uid + end + + def dn + entry.dn + end + + private + + def entry + @entry + end + + def adapter + @adapter ||= Gitlab::LDAP::Adapter.new + end + + def config + @config ||= Gitlab.config.ldap + end + end + end +end diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb index fd36dda7d22..456a61b9e43 100644 --- a/lib/gitlab/ldap/user.rb +++ b/lib/gitlab/ldap/user.rb @@ -13,8 +13,8 @@ module Gitlab def find_or_create(auth) @auth = auth - if uid.blank? || email.blank? - raise_error("Account must provide an uid and email address") + if uid.blank? || email.blank? || username.blank? + raise_error("Account must provide a dn, uid and email address") end user = find(auth) @@ -62,8 +62,16 @@ module Gitlab return nil unless ldap_conf.enabled && login.present? && password.present? ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf) + filter = Net::LDAP::Filter.eq(ldap.uid, login) + + # Apply LDAP user filter if present + if ldap_conf['user_filter'].present? + user_filter = Net::LDAP::Filter.construct(ldap_conf['user_filter']) + filter = Net::LDAP::Filter.join(filter, user_filter) + end + ldap_user = ldap.bind_as( - filter: Net::LDAP::Filter.eq(ldap.uid, login), + filter: filter, size: 1, password: password ) @@ -71,22 +79,20 @@ module Gitlab find_by_uid(ldap_user.dn) if ldap_user end - # Check LDAP user existance by dn. User in git over ssh check - # - # It covers 2 cases: - # * when ldap account was removed - # * when ldap account was deactivated by change of OU membership in 'dn' - def blocked?(dn) - ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf) - ldap.connection.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, size: 1).blank? - end - private def find_by_uid(uid) model.where(provider: provider, extern_uid: uid).last end + def username + (auth.info.nickname || samaccountname).to_s.force_encoding("utf-8") + end + + def samaccountname + (auth.extra[:raw_info][:samaccountname] || []).first + end + def provider 'ldap' end diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index dc9c0e0ab2c..80bb00821f7 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -98,7 +98,7 @@ module Gitlab (?<prefix>\W)? # Prefix ( # Reference @(?<user>[a-zA-Z][a-zA-Z0-9_\-\.]*) # User name - |\#(?<issue>([a-zA-Z]+-)?\d+) # Issue ID + |\#(?<issue>([a-zA-Z\-]+-)?\d+) # Issue ID |!(?<merge_request>\d+) # MR ID |\$(?<snippet>\d+) # Snippet ID |(?<commit>[\h]{6,40}) # Commit ID @@ -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 diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index d18fc8bf2ce..e932b64f4f0 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -7,7 +7,7 @@ module Gitlab end def project_name_regex - /\A[a-zA-Z0-9][a-zA-Z0-9_\-\. ]*\z/ + /\A[a-zA-Z0-9_][a-zA-Z0-9_\-\. ]*\z/ end def name_regex @@ -49,7 +49,7 @@ module Gitlab protected def default_regex - /\A[.?]?[a-zA-Z0-9][a-zA-Z0-9_\-\.]*(?<!\.git)\z/ + /\A[.?]?[a-zA-Z0-9_][a-zA-Z0-9_\-\.]*(?<!\.git)\z/ end end end diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb index bcf3012bd92..bdfcf254e9e 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/seeder.rb b/lib/gitlab/seeder.rb index 3aa3b2ba1e9..39de1223b18 100644 --- a/lib/gitlab/seeder.rb +++ b/lib/gitlab/seeder.rb @@ -1,10 +1,29 @@ module Gitlab class Seeder def self.quiet + mute_mailer SeedFu.quiet = true yield SeedFu.quiet = false puts "\nOK".green end + + def self.by_user(user) + begin + Thread.current[:current_user] = user + yield + ensure + Thread.current[:current_user] = nil + end + end + + def self.mute_mailer + code = <<-eos +def Notify.delay + self +end + eos + eval(code) + end end end diff --git a/lib/gitlab/upgrader.rb b/lib/gitlab/upgrader.rb index 0fe4888665d..0846359f9b1 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/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb index 2e18b0592b5..86d8b69b0ef 100644 --- a/lib/redcarpet/render/gitlab_html.rb +++ b/lib/redcarpet/render/gitlab_html.rb @@ -46,8 +46,10 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML end def preprocess(full_document) - if @project - h.create_relative_links(full_document, @project, @ref, @request_path, is_wiki?) + if is_wiki? + full_document + elsif @project + h.create_relative_links(full_document, @project, @ref, @request_path) else full_document end diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index c6e570784e0..ff584e69058 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. diff --git a/lib/support/logrotate/gitlab b/lib/support/logrotate/gitlab index df9398d0795..d9b07b61ec3 100644 --- a/lib/support/logrotate/gitlab +++ b/lib/support/logrotate/gitlab @@ -2,21 +2,19 @@ # based on: http://stackoverflow.com/a/4883967 /home/git/gitlab/log/*.log { - weekly + daily missingok - rotate 52 + rotate 90 compress - delaycompress notifempty copytruncate } /home/git/gitlab-shell/gitlab-shell.log { - weekly + daily missingok - rotate 52 + rotate 90 compress - delaycompress notifempty copytruncate } diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 7a0f3efbb53..5bff362da0e 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/dev.rake b/lib/tasks/dev.rake index 7d3602211c1..058c7417040 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -1,10 +1,10 @@ +task dev: ["dev:setup"] + namespace :dev do desc "GITLAB | Setup developer environment (db, fixtures)" task :setup => :environment do ENV['force'] = 'yes' - Rake::Task["db:setup"].invoke - Rake::Task["db:seed_fu"].invoke + Rake::Task["gitlab:setup"].invoke Rake::Task["gitlab:shell:setup"].invoke end end - diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 767674e1e84..071760c0c36 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -17,6 +17,7 @@ namespace :gitlab do check_database_config_exists check_database_is_not_sqlite check_migrations_are_up + check_orphaned_users_groups check_gitlab_config_exists check_gitlab_config_not_outdated check_log_writable @@ -65,6 +66,7 @@ namespace :gitlab do puts "no".green else puts "yes".red + puts "Please fix this by removing the SQLite entry from the database.yml".blue for_more_information( "https://github.com/gitlabhq/gitlabhq/wiki/Migrate-from-SQLite-to-MySQL", see_database_guide @@ -181,6 +183,19 @@ namespace :gitlab do end end + def check_orphaned_users_groups + print "Database contains orphaned UsersGroups? ... " + if UsersGroup.where("user_id not in (select id from users)").count > 0 + puts "yes".red + try_fixing_it( + "You can delete the orphaned records using something along the lines of:", + sudo_gitlab("bundle exec rails runner -e production 'UsersGroup.where(\"user_id NOT IN (SELECT id FROM users)\").delete_all'") + ) + else + puts "no".green + end + end + def check_satellites_exist print "Projects have satellites? ... " @@ -727,7 +742,7 @@ namespace :gitlab do end def check_gitlab_shell - required_version = Gitlab::VersionInfo.new(1, 7, 9) + 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/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake index 2b730774e06..853994dd67d 100644 --- a/lib/tasks/gitlab/setup.rake +++ b/lib/tasks/gitlab/setup.rake @@ -15,6 +15,14 @@ namespace :gitlab do end Rake::Task["db:setup"].invoke + + config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env] + success = case config["adapter"] + when /^mysql/ then + Rake::Task["add_limits_mysql"].invoke + when "postgresql" then + end + Rake::Task["db:seed_fu"].invoke rescue Gitlab::TaskAbortedByUserError puts "Quitting...".red diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake index 0d7a390bc92..08de0f2dd5d 100644 --- a/lib/tasks/gitlab/shell.rake +++ b/lib/tasks/gitlab/shell.rake @@ -34,14 +34,18 @@ namespace :gitlab do Gitlab::Shell.new.remove_all_keys - Key.find_each(batch_size: 1000) do |key| - if Gitlab::Shell.new.add_key(key.shell_id, key.key) + Gitlab::Shell.new.batch_add_keys do |adder| + Key.find_each(batch_size: 1000) do |key| + adder.add_key(key.shell_id, key.key) print '.' - else - print 'F' end end + unless $?.success? + puts "Failed to add keys...".red + exit 1 + end + rescue Gitlab::TaskAbortedByUserError puts "Quitting...".red exit 1 diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake index d36b9682850..da61c6e007f 100644 --- a/lib/tasks/gitlab/task_helpers.rake +++ b/lib/tasks/gitlab/task_helpers.rake @@ -82,6 +82,8 @@ namespace :gitlab do def run(command) output, _ = Gitlab::Popen.popen(command) output + rescue Errno::ENOENT + '' # if the command does not exist, return an empty string end def uid_for(user_name) diff --git a/lib/tasks/gitlab/test.rake b/lib/tasks/gitlab/test.rake index f52af0c3ded..2c9b9978933 100644 --- a/lib/tasks/gitlab/test.rake +++ b/lib/tasks/gitlab/test.rake @@ -2,15 +2,13 @@ namespace :gitlab do desc "GITLAB | Run all tests" task :test do cmds = [ - %W(rake db:setup), - %W(rake db:seed_fu), %W(rake spinach), %W(rake spec), %W(rake jasmine:ci) ] cmds.each do |cmd| - system({'RAILS_ENV' => 'test'}, *cmd) + system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) raise "#{cmd} failed!" unless $?.exitstatus.zero? end diff --git a/lib/tasks/migrate/add_limits_mysql.rake b/lib/tasks/migrate/add_limits_mysql.rake new file mode 100644 index 00000000000..46b6451752b --- /dev/null +++ b/lib/tasks/migrate/add_limits_mysql.rake @@ -0,0 +1,14 @@ +desc "GITLAB | Add limits to strings in mysql database" +task add_limits_mysql: :environment do + puts "Adding limits to schema.rb for mysql" + LimitsToMysql.new.up +end + +class LimitsToMysql < ActiveRecord::Migration + def up + change_column :merge_request_diffs, :st_commits, :text, limit: 2147483647 + change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647 + change_column :snippets, :content, :text, limit: 2147483647 + change_column :notes, :st_diff, :text, limit: 2147483647 + end +end diff --git a/lib/tasks/spec.rake b/lib/tasks/spec.rake new file mode 100644 index 00000000000..90a1809914b --- /dev/null +++ b/lib/tasks/spec.rake @@ -0,0 +1,14 @@ +Rake::Task["spec"].clear if Rake::Task.task_defined?('spec') + +desc "GITLAB | Run specs" +task :spec do + cmds = [ + %W(rake gitlab:setup), + %W(rspec spec), + ] + + cmds.each do |cmd| + system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) + raise "#{cmd} failed!" unless $?.exitstatus.zero? + end +end diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake new file mode 100644 index 00000000000..c23d0e0e188 --- /dev/null +++ b/lib/tasks/spinach.rake @@ -0,0 +1,14 @@ +Rake::Task["spinach"].clear if Rake::Task.task_defined?('spinach') + +desc "GITLAB | Run spinach" +task :spinach do + cmds = [ + %W(rake gitlab:setup), + %W(spinach), + ] + + cmds.each do |cmd| + system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) + raise "#{cmd} failed!" unless $?.exitstatus.zero? + end +end diff --git a/lib/tasks/test.rake b/lib/tasks/test.rake new file mode 100644 index 00000000000..f19da1bb437 --- /dev/null +++ b/lib/tasks/test.rake @@ -0,0 +1,6 @@ +Rake::Task["test"].clear + +desc "GITLAB | Run all tests" +task :test do + Rake::Task["gitlab:test"].invoke +end diff --git a/script/background_jobs b/script/background_jobs index 06125c11ffe..52732f5532b 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 121012d5d49..593d3e9eb56 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 7fc2b7c5e97..148477d6389 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 a3d8c462bf6..25a86b11fa9 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 0c354f02456..79a6aee41b5 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 82e816e388a..028cd32d2bb 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 a9c0afbb60f..f0ed7649eb5 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 8bb1e259efa..152cf66dcfd 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 76f9e753dd2..0bd2ccafcc1 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 cc6ee82ab75..6e3ae4d615b 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/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index 7e98b7944f1..5bd16d1c16c 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -440,12 +440,6 @@ describe GitlabMarkdownHelper do markdown(actual).should match(expected) end - it "should handle wiki urls" do - actual = "[Link](test/link)\n" - expected = "<p><a href=\"/#{project.path_with_namespace}/wikis/test/link\">Link</a></p>\n" - markdown(actual).should match(expected) - end - it "should handle relative urls in reference links for a file in master" do actual = "[GitLab API doc][GitLab readme]\n [GitLab readme]: doc/api/README.md\n" expected = "<p><a href=\"/#{project.path_with_namespace}/blob/master/doc/api/README.md\">GitLab API doc</a></p>\n" diff --git a/spec/lib/gitlab/ldap/ldap_user_auth_spec.rb b/spec/lib/gitlab/ldap/ldap_user_auth_spec.rb index a0e74c49631..501642dca79 100644 --- a/spec/lib/gitlab/ldap/ldap_user_auth_spec.rb +++ b/spec/lib/gitlab/ldap/ldap_user_auth_spec.rb @@ -9,7 +9,8 @@ describe Gitlab::LDAP do @info = double( uid: '12djsak321', name: 'John', - email: 'john@mail.com' + email: 'john@mail.com', + nickname: 'john' ) end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 6ba4d97ad4a..22d60429ccd 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -146,7 +146,8 @@ describe Notify do end context 'for issues' do - let(:issue) { create(:issue, author: current_user, assignee: assignee, project: project ) } + let(:issue) { create(:issue, author: current_user, assignee: assignee, project: project) } + let(:issue_with_description) { create(:issue, author: current_user, assignee: assignee, project: project, description: Faker::Lorem.sentence) } describe 'that are new' do subject { Notify.new_issue_email(issue.assignee_id, issue.id) } @@ -162,6 +163,14 @@ describe Notify do end end + describe 'that are new with a description' do + subject { Notify.new_issue_email(issue_with_description.assignee_id, issue_with_description.id) } + + it 'contains the description' do + should have_body_text /#{issue_with_description.description}/ + end + end + describe 'that have been reassigned' do subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user) } @@ -220,7 +229,9 @@ 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) } describe 'that are new' do subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) } @@ -244,6 +255,14 @@ describe Notify do end end + describe 'that are new with a description' do + subject { Notify.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) } + + it 'contains the description' do + should have_body_text /#{merge_request_with_description.description}/ + end + end + describe 'that are reassigned' do subject { Notify.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id) } @@ -270,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 @@ -335,10 +377,6 @@ describe Notify do should deliver_to recipient.email end - it 'contains the name of the note\'s author' do - should have_body_text /#{note_author.name}/ - end - it 'contains the message from the note' do should have_body_text /#{note.note}/ end @@ -468,6 +506,8 @@ describe Notify do let(:example_site_path) { root_path } let(:user) { create(:user) } let(:compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, 'cd5c4bac', 'b1e6a9db') } + let(:commits) { Commit.decorate(compare.commits) } + let(:diff_path) { project_compare_path(project, from: commits.first, to: commits.last) } subject { Notify.repository_push_email(project.id, 'devs@company.name', user.id, 'master', compare) } @@ -492,5 +532,9 @@ describe Notify do it 'includes diffs' do should have_body_text /Checkout wiki pages for installation information/ end + + it 'contains a link to the diff' do + should have_body_text /#{diff_path}/ + end end end diff --git a/spec/models/project_hook_spec.rb b/spec/models/project_hook_spec.rb new file mode 100644 index 00000000000..7bd7c431bcd --- /dev/null +++ b/spec/models/project_hook_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe ProjectHook do + describe '.push_hooks' do + it 'should return hooks for push events only' do + hook = create(:project_hook, push_events: true) + hook2 = create(:project_hook, push_events: false) + expect(ProjectHook.push_hooks).to eq([hook]) + end + end + + describe '.tag_push_hooks' do + it 'should return hooks for tag push events only' do + hook = create(:project_hook, tag_push_events: true) + hook2 = create(:project_hook, tag_push_events: false) + expect(ProjectHook.tag_push_hooks).to eq([hook]) + end + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 6bae5951b7b..839350bafbf 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 00000000000..b39cd4edf82 --- /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 00000000000..387455cb25e --- /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/observers/merge_request_observer_spec.rb b/spec/observers/merge_request_observer_spec.rb index 6ad7c4d81da..18df8b78513 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/observers/users_project_observer_spec.rb b/spec/observers/users_project_observer_spec.rb index dea90b2bfa7..be277b4dbd2 100644 --- a/spec/observers/users_project_observer_spec.rb +++ b/spec/observers/users_project_observer_spec.rb @@ -47,30 +47,6 @@ describe UsersProjectObserver do end describe "#after_create" do - context 'wiki_enabled creates repository directory' do - context 'wiki_enabled true creates wiki repository directory' do - before do - @project = create(:project, wiki_enabled: true) - @path = GollumWiki.new(@project, user).send(:path_to_repo) - end - - after do - FileUtils.rm_rf(@path) - end - - it { File.exists?(@path).should be_true } - end - - context 'wiki_enabled false does not create wiki repository directory' do - before do - @project = create(:project, wiki_enabled: false) - @path = GollumWiki.new(@project, user).send(:path_to_repo) - end - - it { File.exists?(@path).should be_false } - end - end - it "should send email to user" do subject.should_receive(:notification) Event.stub(create: true) diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 412b6c95ffa..1a9e4809e7d 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -92,9 +92,10 @@ describe API::API do it "should return merge_request" do post api("/projects/#{fork_project.id}/merge_requests", user2), - title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user2, target_project_id: project.id + title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user2, target_project_id: project.id, description: 'Test description for Test merge_request' response.status.should == 201 json_response['title'].should == 'Test merge_request' + json_response['description'].should == 'Test description for Test merge_request' end it "should not return 422 when source_branch equals target_branch" do @@ -168,6 +169,12 @@ describe API::API do json_response['title'].should == 'New title' end + it "should return merge_request" do + put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), description: "New description" + response.status.should == 200 + json_response['description'].should == 'New description' + end + it "should return 422 when source_branch and target_branch are renamed the same" do put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), source_branch: "master", target_branch: "master" diff --git a/spec/requests/api/project_members_spec.rb b/spec/requests/api/project_members_spec.rb new file mode 100644 index 00000000000..f3c0529da92 --- /dev/null +++ b/spec/requests/api/project_members_spec.rb @@ -0,0 +1,156 @@ +require 'spec_helper' + +describe API::API do + include ApiHelpers + before(:each) { enable_observers } + after(:each) { disable_observers } + + let(:user) { create(:user) } + let(:user2) { create(:user) } + let(:user3) { create(:user) } + let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } + 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) } + + describe "GET /projects/:id/members" do + before { users_project } + before { users_project2 } + + it "should return project team members" do + get api("/projects/#{project.id}/members", user) + response.status.should == 200 + json_response.should be_an Array + json_response.count.should == 2 + json_response.map { |u| u['email'] }.should include user.email + end + + it "finds team members with query string" do + get api("/projects/#{project.id}/members", user), query: user.username + response.status.should == 200 + json_response.should be_an Array + json_response.count.should == 1 + json_response.first['email'].should == user.email + end + + it "should return a 404 error if id not found" do + get api("/projects/9999/members", user) + response.status.should == 404 + end + end + + describe "GET /projects/:id/members/:user_id" do + before { users_project } + + it "should return project team member" do + get api("/projects/#{project.id}/members/#{user.id}", user) + response.status.should == 200 + json_response['email'].should == user.email + json_response['access_level'].should == UsersProject::MASTER + end + + it "should return a 404 error if user id not found" do + get api("/projects/#{project.id}/members/1234", user) + response.status.should == 404 + end + end + + describe "POST /projects/:id/members" do + it "should add user to project team" do + expect { + post api("/projects/#{project.id}/members", user), user_id: user2.id, + access_level: UsersProject::DEVELOPER + }.to change { UsersProject.count }.by(1) + + response.status.should == 201 + json_response['email'].should == user2.email + json_response['access_level'].should == UsersProject::DEVELOPER + end + + it "should return a 201 status if user is already project member" do + post api("/projects/#{project.id}/members", user), user_id: user2.id, + access_level: UsersProject::DEVELOPER + expect { + post api("/projects/#{project.id}/members", user), user_id: user2.id, + access_level: UsersProject::DEVELOPER + }.not_to change { UsersProject.count }.by(1) + + response.status.should == 201 + json_response['email'].should == user2.email + json_response['access_level'].should == UsersProject::DEVELOPER + end + + it "should return a 400 error when user id is not given" do + post api("/projects/#{project.id}/members", user), access_level: UsersProject::MASTER + response.status.should == 400 + end + + it "should return a 400 error when access level is not given" do + post api("/projects/#{project.id}/members", user), user_id: user2.id + response.status.should == 400 + end + + it "should return a 422 error when access level is not known" do + post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: 1234 + response.status.should == 422 + end + end + + describe "PUT /projects/:id/members/:user_id" do + before { users_project2 } + + it "should update project team member" do + put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: UsersProject::MASTER + response.status.should == 200 + json_response['email'].should == user3.email + json_response['access_level'].should == UsersProject::MASTER + end + + it "should return a 404 error if user_id is not found" do + put api("/projects/#{project.id}/members/1234", user), access_level: UsersProject::MASTER + response.status.should == 404 + end + + it "should return a 400 error when access level is not given" do + put api("/projects/#{project.id}/members/#{user3.id}", user) + response.status.should == 400 + end + + it "should return a 422 error when access level is not known" do + put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: 123 + response.status.should == 422 + end + end + + describe "DELETE /projects/:id/members/:user_id" do + before { users_project } + before { users_project2 } + + it "should remove user from project team" do + expect { + delete api("/projects/#{project.id}/members/#{user3.id}", user) + }.to change { UsersProject.count }.by(-1) + end + + it "should return 200 if team member is not part of a project" do + delete api("/projects/#{project.id}/members/#{user3.id}", user) + expect { + delete api("/projects/#{project.id}/members/#{user3.id}", user) + }.to_not change { UsersProject.count }.by(1) + end + + it "should return 200 if team member already removed" do + delete api("/projects/#{project.id}/members/#{user3.id}", user) + delete api("/projects/#{project.id}/members/#{user3.id}", user) + response.status.should == 200 + end + + it "should return 200 OK when the user was not member" do + expect { + delete api("/projects/#{project.id}/members/1000000", user) + }.to change { UsersProject.count }.by(0) + response.status.should == 200 + json_response['message'].should == "Access revoked" + json_response['id'].should == 1000000 + end + end +end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 342587ba5d6..7fe65639657 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 @@ -259,6 +260,7 @@ describe API::API do describe "GET /projects/:id" do before { project } + before { users_project } it "should return a project by id" do get api("/projects/#{project.id}", user) @@ -284,6 +286,28 @@ describe API::API do get api("/projects/#{project.id}", other_user) response.status.should == 404 end + + describe 'permissions' do + context 'personal project' do + before { get api("/projects/#{project.id}", user) } + + it { response.status.should == 200 } + it { json_response['permissions']["project_access"]["access_level"].should == Gitlab::Access::MASTER } + it { json_response['permissions']["group_access"].should be_nil } + end + + context 'group project' do + before do + project2 = create(:project, group: create(:group)) + project2.group.add_owner(user) + get api("/projects/#{project2.id}", user) + end + + it { response.status.should == 200 } + it { json_response['permissions']["project_access"].should be_nil } + it { json_response['permissions']["group_access"]["access_level"].should == Gitlab::Access::OWNER } + end + end end describe "GET /projects/:id/events" do @@ -311,148 +335,6 @@ describe API::API do end end - describe "GET /projects/:id/members" do - before { users_project } - before { users_project2 } - - it "should return project team members" do - get api("/projects/#{project.id}/members", user) - response.status.should == 200 - json_response.should be_an Array - json_response.count.should == 2 - json_response.map { |u| u['email'] }.should include user.email - end - - it "finds team members with query string" do - get api("/projects/#{project.id}/members", user), query: user.username - response.status.should == 200 - json_response.should be_an Array - json_response.count.should == 1 - json_response.first['email'].should == user.email - end - - it "should return a 404 error if id not found" do - get api("/projects/9999/members", user) - response.status.should == 404 - end - end - - describe "GET /projects/:id/members/:user_id" do - before { users_project } - - it "should return project team member" do - get api("/projects/#{project.id}/members/#{user.id}", user) - response.status.should == 200 - json_response['email'].should == user.email - json_response['access_level'].should == UsersProject::MASTER - end - - it "should return a 404 error if user id not found" do - get api("/projects/#{project.id}/members/1234", user) - response.status.should == 404 - end - end - - describe "POST /projects/:id/members" do - it "should add user to project team" do - expect { - post api("/projects/#{project.id}/members", user), user_id: user2.id, - access_level: UsersProject::DEVELOPER - }.to change { UsersProject.count }.by(1) - - response.status.should == 201 - json_response['email'].should == user2.email - json_response['access_level'].should == UsersProject::DEVELOPER - end - - it "should return a 201 status if user is already project member" do - post api("/projects/#{project.id}/members", user), user_id: user2.id, - access_level: UsersProject::DEVELOPER - expect { - post api("/projects/#{project.id}/members", user), user_id: user2.id, - access_level: UsersProject::DEVELOPER - }.not_to change { UsersProject.count }.by(1) - - response.status.should == 201 - json_response['email'].should == user2.email - json_response['access_level'].should == UsersProject::DEVELOPER - end - - it "should return a 400 error when user id is not given" do - post api("/projects/#{project.id}/members", user), access_level: UsersProject::MASTER - response.status.should == 400 - end - - it "should return a 400 error when access level is not given" do - post api("/projects/#{project.id}/members", user), user_id: user2.id - response.status.should == 400 - end - - it "should return a 422 error when access level is not known" do - post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: 1234 - response.status.should == 422 - end - end - - describe "PUT /projects/:id/members/:user_id" do - before { users_project2 } - - it "should update project team member" do - put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: UsersProject::MASTER - response.status.should == 200 - json_response['email'].should == user3.email - json_response['access_level'].should == UsersProject::MASTER - end - - it "should return a 404 error if user_id is not found" do - put api("/projects/#{project.id}/members/1234", user), access_level: UsersProject::MASTER - response.status.should == 404 - end - - it "should return a 400 error when access level is not given" do - put api("/projects/#{project.id}/members/#{user3.id}", user) - response.status.should == 400 - end - - it "should return a 422 error when access level is not known" do - put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: 123 - response.status.should == 422 - end - end - - describe "DELETE /projects/:id/members/:user_id" do - before { users_project } - before { users_project2 } - - it "should remove user from project team" do - expect { - delete api("/projects/#{project.id}/members/#{user3.id}", user) - }.to change { UsersProject.count }.by(-1) - end - - it "should return 200 if team member is not part of a project" do - delete api("/projects/#{project.id}/members/#{user3.id}", user) - expect { - delete api("/projects/#{project.id}/members/#{user3.id}", user) - }.to_not change { UsersProject.count }.by(1) - end - - it "should return 200 if team member already removed" do - delete api("/projects/#{project.id}/members/#{user3.id}", user) - delete api("/projects/#{project.id}/members/#{user3.id}", user) - response.status.should == 200 - end - - it "should return 200 OK when the user was not member" do - expect { - delete api("/projects/#{project.id}/members/1000000", user) - }.to change { UsersProject.count }.by(0) - response.status.should == 200 - json_response['message'].should == "Access revoked" - json_response['id'].should == 1000000 - end - end - describe "GET /projects/:id/snippets" do before { snippet } @@ -609,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) @@ -681,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 @@ -751,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 Binary files differindex 92b9587e3f7..8d32a927da8 100644 --- a/spec/seed_project.tar.gz +++ b/spec/seed_project.tar.gz diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb new file mode 100644 index 00000000000..e65a8204c54 --- /dev/null +++ b/spec/services/git_tag_push_service_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe GitTagPushService do + let (:user) { create :user } + let (:project) { create :project } + let (:service) { GitTagPushService.new } + + before do + @ref = 'refs/tags/super-tag' + @oldrev = 'b98a310def241a6fd9c9a9a3e7934c48e498fe81' + @newrev = 'b19a04f53caeebf4fe5ec2327cb83e9253dc91bb' + end + + describe 'Git Tag Push Data' do + before do + service.execute(project, user, @oldrev, @newrev, @ref) + @push_data = service.push_data + end + + subject { @push_data } + + it { should include(ref: @ref) } + it { should include(before: @oldrev) } + it { should include(after: @newrev) } + it { should include(user_id: user.id) } + it { should include(user_name: user.name) } + it { should include(project_id: project.id) } + + context 'With repository data' do + subject { @push_data[:repository] } + + it { should include(name: project.name) } + it { should include(url: project.url_to_repo) } + it { should include(description: project.description) } + it { should include(homepage: project.web_url) } + end + end + + describe "Web Hooks" do + context "execute web hooks" do + it "when pushing tags" do + project.should_receive(:execute_hooks) + service.execute(project, user, 'oldrev', 'newrev', 'refs/tags/v1.0.0') + end + end + end +end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 077ad8b6e12..fbd73a7086f 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 index 0a41832a211..f2a784df103 100644 --- a/spec/services/projects_create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -38,6 +38,27 @@ describe Projects::CreateService do it { @project.namespace.should == @group } end + context 'wiki_enabled creates repository directory' do + context 'wiki_enabled true creates wiki repository directory' do + before do + @project = create_project(@user, @opts) + @path = GollumWiki.new(@project, @user).send(:path_to_repo) + end + + it { File.exists?(@path).should be_true } + end + + context 'wiki_enabled false does not create wiki repository directory' do + before do + @opts.merge!(wiki_enabled: false) + @project = create_project(@user, @opts) + @path = GollumWiki.new(@project, @user).send(:path_to_repo) + end + + it { File.exists?(@path).should be_false } + end + end + context 'respect configured visibility setting' do before(:each) do @settings = double("settings") diff --git a/spec/services/project_transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 109b429967e..109b429967e 100644 --- a/spec/services/project_transfer_service_spec.rb +++ b/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 index 1854c0d8233..1854c0d8233 100644 --- a/spec/services/projects_update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index 457cb3c0ca3..b467282a5d6 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 dd008ed02ad..e6b1f816df0 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 77a669c0cc1..d00decf6121 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 @@ -106,7 +105,7 @@ module TestEnv setup_stubs [ %W(git reset --hard --quiet), - %W(git clean -fx), + %W(git clean -fx --quiet), %W(git checkout --quiet origin/master) ].each do |git_cmd| system(*git_cmd, chdir: seed_satellite_path) @@ -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 |