diff --git a/.rspec b/.rspec index 5902dd3a..53607ea5 100644 --- a/.rspec +++ b/.rspec @@ -1 +1 @@ ---colour --drb +--colour diff --git a/.travis.yml b/.travis.yml index 609f2967..befa0c32 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ branches: only: - 'master' rvm: - - 1.9.3-p392 + - 1.9.3-p327 services: - mysql - postgresql diff --git a/CHANGELOG b/CHANGELOG index 872661d4..62792fcf 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,10 +1,3 @@ -v 5.1.0 - - You can login with email or username now - - Corrected project transfer rollback when repository cannot be moved - - Move both repo and wiki when project transfer requrested - - Admin area: project editing was removed from admin namespace - - Access: admin user has now access to any project. - v 5.0.0 - Replaced gitolite with gitlab-shell - Removed gitolite-related libraries @@ -21,7 +14,7 @@ v 5.0.0 - Add validations for Group and Team names - Restyle team page for project - Update capybara, rspec-rails, poltergeist to recent versions - - Wiki on git using Gollum + - Wiki on git using Gollum - Added Solarized Dark theme for code review - Dont show user emails in autocomplete lists, profile pages - Added settings tab for group, team, project @@ -31,7 +24,7 @@ v 5.0.0 - Fixed search field on projects page - Added teams to search autocomplete - Move groups and teams on dashboard sidebar to sub-tabs - - API: improved return codes and docs. (Felix Gilcher, Sebastian Ziebell) + - API: improved return codes and docs. - Redesign wall to be more like chat - Snippets, Wall features are disabled by default for new projects @@ -63,7 +56,7 @@ v 4.1.0 - cleanup rake tasks - fix backup/restore - scss cleanup - - show preview for note images + - show preview for note images - improved network-graph - get rid of app/roles/ - added new classes Team, Repository @@ -77,7 +70,7 @@ v 4.1.0 v 4.0.0 - Remove project code and path from API. Use id instead - Return valid clonable url to repo for web hook - - Fixed backup issue + - Fixed backup issue - Reorganized settings - Fixed commits compare - Refactored scss diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f322a81c..d3ccbee9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,18 +1,35 @@ # Contribute to GitLab -This guide details how to use pull requests and the issues to improve GitLab. +If you have a question or want to contribute to GitLab this guide show you the appropriate channel to use. -## Closing policy for pull requests and issues +## Ruling out common errors -Pull requests and issues not in line with the guidelines listed in this document will be closed with just a link to this paragraph. GitLab is a popular open source project and the capacity to deal with issues and pull requests is limited. To get support for your problems please use other channels as detailed in [the getting help section of the readme](https://github.com/gitlabhq/gitlabhq#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/). +Some errors are common and it may so happen, that you are not the only one who stumbled over a particular issue. We have [collected several of those and documented quick solutions](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) for them. + +## Support forum + +Please visit our [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) for any kind of question regarding the usage or adiministration/configuration of GitLab. + +### Use the support forum if ... + +* You get permission denied errors +* You can't see your repos +* You have issues cloning, pulling or pushing +* You have issues with web_hooks not firing + +**Search** for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and had it resolved. + +## Paid support + +Community support in the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) is done by volunteers. A support subscription is available from [GitLab.com](http://blog.gitlab.com/subscription/) + +## Feature suggestions + +Feature suggestions don't belong in issues but can go to [Feedback forum](http://gitlab.uservoice.com/forums/176466-general) where they can be voted on. ## Pull requests -We welcome pull request with improvements to GitLab code and/or documentation. The issues we would really like a pull request for are listed with the [status 'accepting merge/pull requests' on our feedback forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome. - -### Pull request guidelines - - If you can please submit a pull request with the fix including tests. The workflow to make a pull request is as follows: +Code speaks louder than words. If you can please submit a pull request with the fix including tests. The workflow to make a pull request is as follows: 1. Fork the project on GitHub 1. Create a feature branch @@ -34,20 +51,26 @@ We will accept pull requests if: For examples of feedback on pull requests please look at already [closed pull requests](https://github.com/gitlabhq/gitlabhq/pulls?direction=desc&page=1&sort=created&state=closed). -## Issue tracker +## Submitting via GitHub's issue tracker -The [issue tracker](https://github.com/gitlabhq/gitlabhq/issues) is only for obvious bugs or misbehavior in the master branch of GitLab. When submitting an issue please conform to the issue submission guidelines listed below. +* For obvious bugs or misbehavior in GitLab in the master branch. Please include the revision id and a reproducible test case. +* For problematic or insufficient documentation. Please give a suggestion on how to improve it. -Please send a pull request with a tested solution or a pull request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) 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. +If you're unsure where to post, post it to the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) 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 to the [issue tracker on GitHub](https://github.com/gitlabhq/gitlabhq/issues). -### Issue tracker guidelines +### When submitting an issue **Search** for similar entries before submitting your own, there's a good chance somebody else had the same issue or idea. Show your support with `:+1:` and/or join the discussion. -* Summarize your issue in one sentence (what goes wrong, what did you expect to happen) -* Describe your issue in detail -* How can we reproduce the issue on the [GitLab Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) (start with: vagrant destroy && vagrant up && vagrant ssh) -* Add the last commit sha1 of the GitLab version you used to replicate the issue +Please consider the following points when submitting an **issue**: + +* Summarize your issue in one sentence (what happened wrong, when you did/expected something else) +* Describe your issue in detail (including steps to reproduce) * Add logs or screen shots when possible -* Link to the line of code that might be responsible for the problem * Describe your setup (use relevant parts from `sudo -u gitlab -H bundle exec rake gitlab:env:info`) + +## Thank you! + +By taking the time to use the right channel, you help the development team to organize and prioritize issues and suggestions in order to make GitLab a better product for us all. diff --git a/Gemfile b/Gemfile index 01e3b0f2..e1e5bba2 100644 --- a/Gemfile +++ b/Gemfile @@ -23,8 +23,8 @@ gem 'omniauth-github' # Extracting information from a git repository # Since gollum requires grit we cannot use gitlab-grit gem name any more. Use grit instead -gem "grit", '~> 2.5.0', git: 'https://github.com/gitlabhq/grit.git', ref: '42297cdcee16284d2e4eff23d41377f52fc28b9d' -gem 'grit_ext', '~> 0.8.1' +gem "grit", '~> 2.5.0', git: 'https://github.com/gitlabhq/grit.git', ref: 'c40a32432616a07fa7fc3c32c24ab73ad6a9718f' +gem 'grit_ext', '~> 0.6.2' # Ruby/Rack Git Smart-HTTP Server Handler gem 'gitlab-grack', '~> 1.0.0', require: 'grack' @@ -105,7 +105,7 @@ gem 'settingslogic' # github-linquist needs pygments 0.4.2 but Gollum 2.4.11 # requires pygments 0.3.2. The latest master Gollum has been updated # to use pygments 0.4.2. Change this after next Gollum release. -gem "gollum", "~> 2.4.0", git: "https://github.com/gollum/gollum.git", ref: "5dcd3c8c8f" +gem "gollum", "~> 2.4.0", git: "git://github.com/gollum/gollum.git", ref: "5dcd3c8c8f" # Misc gem "foreman" @@ -154,9 +154,9 @@ end group :development, :test do gem 'coveralls', require: false gem 'rails-dev-tweaks' - gem 'spinach-rails' - gem "rspec-rails" - gem "capybara" + gem 'spinach-rails', '0.2.0' + gem "rspec-rails", '2.12.2' + gem "capybara", '2.0.2' gem "pry" gem "awesome_print" gem "database_cleaner" @@ -174,8 +174,6 @@ group :development, :test do # PhantomJS driver for Capybara gem 'poltergeist', '1.1.0' - - gem 'spork', '~> 1.0rc' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 586b7bda..9614a07c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,29 +1,5 @@ GIT - remote: https://github.com/ctran/annotate_models.git - revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e - specs: - annotate (2.6.0.beta1) - activerecord (>= 2.3.0) - rake (>= 0.8.7) - -GIT - remote: https://github.com/gitlabhq/grit.git - revision: 42297cdcee16284d2e4eff23d41377f52fc28b9d - ref: 42297cdcee16284d2e4eff23d41377f52fc28b9d - specs: - grit (2.5.0) - diff-lcs (~> 1.1) - mime-types (~> 1.15) - posix-spawn (~> 0.3.6) - -GIT - remote: https://github.com/gitlabhq/raphael-rails.git - revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58 - specs: - raphael-rails (2.1.0) - -GIT - remote: https://github.com/gollum/gollum.git + remote: git://github.com/gollum/gollum.git revision: 5dcd3c8c8f68158e43ff79861279088ee56d0ebe ref: 5dcd3c8c8f specs: @@ -39,6 +15,30 @@ GIT stringex (~> 1.5.1) useragent (~> 0.4.16) +GIT + remote: https://github.com/ctran/annotate_models.git + revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e + specs: + annotate (2.6.0.beta1) + activerecord (>= 2.3.0) + rake (>= 0.8.7) + +GIT + remote: https://github.com/gitlabhq/grit.git + revision: c40a32432616a07fa7fc3c32c24ab73ad6a9718f + ref: c40a32432616a07fa7fc3c32c24ab73ad6a9718f + specs: + grit (2.5.0) + diff-lcs (~> 1.1) + mime-types (~> 1.15) + posix-spawn (~> 0.3.6) + +GIT + remote: https://github.com/gitlabhq/raphael-rails.git + revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58 + specs: + raphael-rails (2.1.0) + GEM remote: https://rubygems.org/ specs: @@ -105,7 +105,7 @@ GEM thor (~> 0.14) code_analyzer (0.3.1) sexp_processor - coderay (1.0.9) + coderay (1.0.8) coffee-rails (3.2.2) coffee-script (>= 2.2.0) railties (~> 3.2.0) @@ -122,7 +122,7 @@ GEM rest-client simplecov (>= 0.7) thor - crack (0.3.2) + crack (0.3.1) daemons (1.1.9) database_cleaner (0.9.1) debug_inspector (0.0.2) @@ -132,7 +132,7 @@ GEM orm_adapter (~> 0.1) railties (~> 3.1) warden (~> 1.2.1) - diff-lcs (1.2.1) + diff-lcs (1.1.3) draper (1.1.0) actionpack (>= 3.0) activesupport (>= 3.0) @@ -147,7 +147,7 @@ GEM eventmachine (1.0.0) execjs (1.4.0) multi_json (~> 1.0) - facter (1.6.18) + facter (1.6.17) factory_girl (4.1.0) activesupport (>= 3.0.0) factory_girl_rails (4.1.0) @@ -200,16 +200,15 @@ GEM grape-entity (0.2.0) activesupport multi_json (>= 1.3.2) - grit_ext (0.8.1) + grit_ext (0.6.2) charlock_holmes (~> 0.6.9) growl (1.0.3) - guard (1.6.2) - listen (>= 0.6.0) + guard (1.5.4) + listen (>= 0.4.2) lumberjack (>= 1.0.2) pry (>= 0.9.10) - terminal-table (>= 1.4.3) thor (>= 0.14.6) - guard-rspec (2.5.1) + guard-rspec (2.1.2) guard (>= 1.1) rspec (~> 2.11) guard-spinach (0.0.2) @@ -249,9 +248,9 @@ GEM addressable (~> 2.3) letter_opener (1.0.0) launchy (>= 2.0.4) - libv8 (3.11.8.17) - listen (0.7.3) - lumberjack (1.0.3) + libv8 (3.3.10.4) + listen (0.5.3) + lumberjack (1.0.2) mail (2.5.3) i18n (>= 0.4.0) mime-types (~> 1.16) @@ -260,7 +259,7 @@ GEM mime-types (1.21) modernizr (2.6.2) sprockets (~> 2.0) - multi_json (1.7.2) + multi_json (1.7.1) multi_xml (0.5.3) multipart-post (1.1.5) mustache (0.99.4) @@ -300,10 +299,11 @@ GEM http_parser.rb (~> 0.5.3) polyglot (0.3.3) posix-spawn (0.3.6) - pry (0.9.12) + progressbar (0.12.0) + pry (0.9.10) coderay (~> 1.0.5) method_source (~> 0.8) - slop (~> 3.4) + slop (~> 3.3.1) pygments.rb (0.4.2) posix-spawn (~> 0.3.6) yajl-ruby (~> 1.1.0) @@ -336,14 +336,14 @@ GEM rails-dev-tweaks (0.6.1) actionpack (~> 3.1) railties (~> 3.1) - rails_best_practices (1.13.4) + rails_best_practices (1.13.2) activesupport awesome_print code_analyzer colored erubis i18n - ruby-progressbar + progressbar railties (3.2.13) actionpack (= 3.2.13) activesupport (= 3.2.13) @@ -352,14 +352,14 @@ GEM rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) raindrops (0.10.0) - rake (10.0.4) + rake (10.0.3) rb-fsevent (0.9.2) rb-inotify (0.8.8) ffi (>= 0.5.0) rdoc (3.12.2) json (~> 1.4) redcarpet (2.2.2) - redis (3.0.3) + redis (3.0.2) redis-actionpack (3.2.3) actionpack (~> 3.2.3) redis-rack (~> 1.4.0) @@ -378,32 +378,30 @@ GEM redis-store (~> 1.1.0) redis-store (1.1.3) redis (>= 2.2.0) - ref (1.0.4) request_store (1.0.5) rest-client (1.6.7) mime-types (>= 1.16) - rspec (2.13.0) - rspec-core (~> 2.13.0) - rspec-expectations (~> 2.13.0) - rspec-mocks (~> 2.13.0) - rspec-core (2.13.1) - rspec-expectations (2.13.0) - diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.13.0) - rspec-rails (2.13.0) + rspec (2.12.0) + rspec-core (~> 2.12.0) + rspec-expectations (~> 2.12.0) + rspec-mocks (~> 2.12.0) + rspec-core (2.12.0) + rspec-expectations (2.12.0) + diff-lcs (~> 1.1.3) + rspec-mocks (2.12.0) + rspec-rails (2.12.2) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 2.13.0) - rspec-expectations (~> 2.13.0) - rspec-mocks (~> 2.13.0) - ruby-progressbar (1.0.2) + rspec-core (~> 2.12.0) + rspec-expectations (~> 2.12.0) + rspec-mocks (~> 2.12.0) rubyntlm (0.1.1) rubyzip (0.9.9) sanitize (2.0.3) nokogiri (>= 1.4.4, < 1.6) - sass (3.2.7) - sass-rails (3.2.6) + sass (3.2.5) + sass-rails (3.2.5) railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) @@ -422,10 +420,10 @@ GEM rubyzip websocket (~> 1.0.4) settingslogic (2.0.9) - sexp_processor (4.2.0) + sexp_processor (4.1.3) shoulda-matchers (1.3.0) activesupport (>= 3.0.0) - sidekiq (2.8.0) + sidekiq (2.7.5) celluloid (~> 0.12.0) connection_pool (~> 1.0) multi_json (~> 1) @@ -443,7 +441,7 @@ GEM slim (1.3.6) temple (~> 0.5.5) tilt (~> 1.3.3) - slop (3.4.4) + slop (3.3.3) spinach (0.7.0) colorize gherkin-ruby (~> 0.2.0) @@ -451,7 +449,6 @@ GEM capybara (~> 2.0.0) railties (>= 3) spinach (>= 0.4) - spork (1.0.0rc3) sprockets (2.2.2) hike (~> 1.2) multi_json (~> 1.0) @@ -461,16 +458,14 @@ GEM state_machine (1.1.2) stringex (1.5.1) temple (0.5.5) - terminal-table (1.4.5) test_after_commit (0.0.1) - therubyracer (0.11.4) - libv8 (~> 3.11.8.12) - ref + therubyracer (0.10.2) + libv8 (~> 3.3.10) thin (1.5.0) daemons (>= 1.0.9) eventmachine (>= 0.12.6) rack (>= 1.0.0) - thor (0.18.0) + thor (0.17.0) tilt (1.3.6) timers (1.1.0) treetop (1.4.12) @@ -508,7 +503,7 @@ DEPENDENCIES better_errors binding_of_caller bootstrap-sass (= 2.2.1.1) - capybara + capybara (= 2.0.2) carrierwave chosen-rails (= 0.9.8) coffee-rails (~> 3.2.2) @@ -537,7 +532,7 @@ DEPENDENCIES grape (~> 0.3.1) grape-entity (~> 0.2.0) grit (~> 2.5.0)! - grit_ext (~> 0.8.1) + grit_ext (~> 0.6.2) growl guard-rspec guard-spinach @@ -568,7 +563,7 @@ DEPENDENCIES rb-inotify redcarpet (~> 2.2.2) redis-rails - rspec-rails + rspec-rails (= 2.12.2) sass-rails (~> 3.2.5) sdoc seed-fu @@ -580,8 +575,7 @@ DEPENDENCIES sinatra six slim - spinach-rails - spork (~> 1.0rc) + spinach-rails (= 0.2.0) stamp state_machine test_after_commit diff --git a/README.md b/README.md index 3ab4c051..b04d7eb9 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ * powered by Ruby on Rails * completely free and open source (MIT license) -* used by more than 10.000 organizations to keep their code secure +* used by 10.000 organizations to keep their code secure ### Code status @@ -30,7 +30,7 @@ * 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 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.com commercial services: [Homepage](http://blog.gitlab.com/) [GitLab Cloud](http://blog.gitlab.com/cloud/) [Subscription](http://blog.gitlab.com/subscription/) [Consultancy](http://blog.gitlab.com/consultancy/) [Blog](http://blog.gitlab.com/blog/) * GitLab CI: [Readme](https://github.com/gitlabhq/gitlab-ci/blob/master/README.md) of the GitLab open-source continuous integration server @@ -47,45 +47,22 @@ ### Installation -#### Official production installation +#### For production Follow the installation guide for production server. -* [Installation guide for latest stable release (5.0)](https://github.com/gitlabhq/gitlabhq/blob/5-0-stable/doc/install/installation.md) - **Recommended** +* [Installation guide for latest stable release (4.2)](https://github.com/gitlabhq/gitlabhq/blob/4-2-stable/doc/install/installation.md) - **Recommended** -* [Installation guide for the current master branch (5.1)](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md) +* [Installation guide for the current master branch (5.0)](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md) -#### Official development installation +#### For development -If you want to contribute, please first read our [Contributing Guidelines](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) and then we suggest you to use the Vagrant virtual machine project to get an environment working with all dependencies. +If you want to contribute, please first read our [Contributing Guidelines](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) and then we suggest you to use the Vagrant virtual machine project to get an environment working sandboxed and with all dependencies. * [Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) - -#### Unsupported production installation - -* [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes) for setup on different platforms - -* [Unofficial installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) - -* [BitNami one-click installers](http://bitnami.com/stack/gitlab) - -* [TurnKey Linux virtual appliance](http://www.turnkeylinux.org/gitlab) - - -### New versions and upgrading - -Each month on the 22th a new version is released together with an upgrade guide. - -* [Upgrade guides](https://github.com/gitlabhq/gitlabhq/wiki) - -* [Changelog](https://github.com/gitlabhq/gitlabhq/blob/master/CHANGELOG) - -* [Roadmap](https://github.com/gitlabhq/gitlabhq/blob/master/ROADMAP.md) - - -### Getting started +### Starting 1. The Installation guide contains instructions to download an init script and run that on boot. With the init script you can also start GitLab @@ -123,35 +100,38 @@ Each month on the 22th a new version is released together with an upgrade guide. bundle exec rake spinach +### Getting help -### GitLab interfaces +* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) + +* [Support forum](https://groups.google.com/forum/#!forum/gitlabhq) + +* [Feedback and suggestions forum](http://gitlab.uservoice.com/forums/176466-general) + +* [Support subscription](http://blog.gitlab.com/subscription/) + +* [Consultancy](http://blog.gitlab.com/consultancy/) + +### New versions and the API + +Each month on the 22th a new version is released together with an upgrade guide. + +* [Upgrade guides](https://github.com/gitlabhq/gitlabhq/wiki) + +* [Roadmap](https://github.com/gitlabhq/gitlabhq/blob/master/ROADMAP.md) + +### Other documentation * [GitLab API](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/README.md) * [Rake tasks](https://github.com/gitlabhq/gitlabhq/tree/master/doc/raketasks) -* [Directory structure](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/structure.md) - -* [Databases](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/databases.md) - - -### Getting help - -* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) contains solutions to common problems. - -* [Support forum](https://groups.google.com/forum/#!forum/gitlabhq) is the best place 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 had it resolved. 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://gitlab.uservoice.com/forums/176466-general) is the place to propose and discuss new features for GitLab. - -* [Support subscription](http://www.gitlab.com/subscription/) connect you to the knowledge of GitLab experts that will resolve your issues and answer your questions. - -* [Consultancy](http://www.gitlab.com/consultancy/) allows you hire GitLab exports for installations, upgrades and customizations. - -* [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) describes how to submit pull requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed without comment. - +* [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes) ### Getting in touch +* [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) + * [Core team](https://github.com/gitlabhq?tab=members) * [Contributors](https://github.com/gitlabhq/gitlabhq/graphs/contributors) diff --git a/ROADMAP.md b/ROADMAP.md index 9c4bd2db..bf4fe695 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,5 +1,7 @@ ## GitLab Roadmap -### v5.1 April 22 +### v5.0 March 22 -* Not decided yet. +* Replace gitolite with gitlab-shell +* Usability improvements +* Notification improvements \ No newline at end of file diff --git a/VERSION b/VERSION index 52df2a23..0062ac97 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.0pre +5.0.0 diff --git a/app/assets/javascripts/behaviors/toggler_behavior.coffee b/app/assets/javascripts/behaviors/toggler_behavior.coffee index d2181e7b..3fefbf8e 100644 --- a/app/assets/javascripts/behaviors/toggler_behavior.coffee +++ b/app/assets/javascripts/behaviors/toggler_behavior.coffee @@ -3,11 +3,3 @@ $ -> container = $(@).closest(".js-toggler-container") container.toggleClass("on") - - $("body").on "click", ".js-toggle-visibility-link", (e) -> - $(@).find('i'). - toggleClass('icon-chevron-down'). - toggleClass('icon-chevron-up') - container = $(".js-toggle-visibility-container") - container.toggleClass("hide") - e.preventDefault() diff --git a/app/assets/javascripts/branch-graph.js.coffee b/app/assets/javascripts/branch-graph.js.coffee index 2a668de2..6e4d6931 100644 --- a/app/assets/javascripts/branch-graph.js.coffee +++ b/app/assets/javascripts/branch-graph.js.coffee @@ -5,10 +5,6 @@ class BranchGraph @mspace = 0 @parents = {} @colors = ["#000"] - @offsetX = 120 - @offsetY = 20 - @unitTime = 30 - @unitSpace = 10 @load() load: -> @@ -24,6 +20,8 @@ class BranchGraph prepareData: (@days, @commits) -> @collectParents() + @mtime += 4 + @mspace += 10 for c in @commits c.isParent = true if c.id of @parents @@ -37,7 +35,6 @@ class BranchGraph @mspace = Math.max(@mspace, c.space) for p in c.parents @parents[p[0]] = true - @mspace = Math.max(@mspace, p[1]) collectColors: -> k = 0 @@ -49,23 +46,25 @@ class BranchGraph k++ buildGraph: -> - graphHeight = $(@element).height() graphWidth = $(@element).width() - ch = Math.max(graphHeight, @offsetY + @unitTime * @mtime + 150) - cw = Math.max(graphWidth, @offsetX + @unitSpace * @mspace + 300) - @r = r = Raphael(@element.get(0), cw, ch) + ch = @mspace * 20 + 100 + cw = Math.max(graphWidth, @mtime * 20 + 260) + r = Raphael(@element.get(0), cw, ch) top = r.set() cuday = 0 cumonth = "" - barHeight = Math.max(graphHeight, @unitTime * @days.length + 320) - - r.rect(0, 0, 26, barHeight).attr fill: "#222" - r.rect(26, 0, 20, barHeight).attr fill: "#444" + offsetX = 20 + offsetY = 60 + barWidth = Math.max(graphWidth, @days.length * 20 + 320) + scrollLeft = cw + @raphael = r + r.rect(0, 0, barWidth, 20).attr fill: "#222" + r.rect(0, 20, barWidth, 20).attr fill: "#444" for day, mm in @days if cuday isnt day[0] # Dates - r.text(36, @offsetY + @unitTime * mm, day[0]) + r.text(offsetX + mm * 20, 31, day[0]) .attr( font: "12px Monaco, monospace" fill: "#DDD" @@ -74,7 +73,7 @@ class BranchGraph if cumonth isnt day[1] # Months - r.text(13, @offsetY + @unitTime * mm, day[1]) + r.text(offsetX + mm * 20, 11, day[1]) .attr( font: "12px Monaco, monospace" fill: "#EEE" @@ -82,20 +81,61 @@ class BranchGraph cumonth = day[1] for commit in @commits - x = @offsetX + @unitSpace * (@mspace - commit.space) - y = @offsetY + @unitTime * commit.time + x = offsetX + 20 * commit.time + y = offsetY + 10 * commit.space + # Draw dot + r.circle(x, y, 3).attr( + fill: @colors[commit.space] + stroke: "none" + ) - @drawDot(x, y, commit) + # Draw lines + for parent in commit.parents + parentCommit = @preparedCommits[parent[0]] + parentX = offsetX + 20 * parentCommit.time + parentY1 = offsetY + 10 * parentCommit.space + parentY2 = offsetY + 10 * parent[1] + if parentCommit.space is commit.space and parentCommit.space is parent[1] + r.path(["M", x, y, "L", parentX, parentY1]).attr( + stroke: @colors[parentCommit.space] + "stroke-width": 2 + ) - @drawLines(x, y, commit) + else if parentCommit.space < commit.space + if y is parentY2 + r.path(["M", x - 5, y, "l-5,-2,0,4,5,-2", "L", x - 10, y, "L", x - 15, parentY2, "L", parentX + 5, parentY2, "L", parentX, parentY1]).attr( + stroke: @colors[commit.space] + "stroke-width": 2 + ) - @appendLabel(x, y, commit.refs) if commit.refs + else + r.path(["M", x - 3, y - 6, "l-4,-3,4,-2,0,5", "L", x - 5, y - 10, "L", x - 10, parentY2, "L", parentX + 5, parentY2, "L", parentX, parentY1]).attr( + stroke: @colors[commit.space] + "stroke-width": 2 + ) - @appendAnchor(top, commit, x, y) + else + r.path(["M", x - 3, y + 6, "l-4,3,4,2,0,-5", "L", x - 5, y + 10, "L", x - 10, parentY2, "L", parentX + 5, parentY2, "L", parentX, parentY1]).attr( + stroke: @colors[parentCommit.space] + "stroke-width": 2 + ) - @markCommit(x, y, commit, graphHeight) + @appendLabel x, y, commit.refs if commit.refs + + # Mark commit and displayed in the center + if commit.id is @options.commit_id + r.path(["M", x, y - 5, "L", x + 4, y - 15, "L", x - 4, y - 15, "Z"]).attr( + fill: "#000" + "fill-opacity": .7 + stroke: "none" + ) + + scrollLeft = x - graphWidth / 2 + + @appendAnchor top, commit, x, y top.toFront() + @element.scrollLeft scrollLeft @bindEvents() bindEvents: -> @@ -127,37 +167,35 @@ class BranchGraph element.scrollTop element.scrollTop() + 50 if event.keyCode is 40 appendLabel: (x, y, refs) -> - r = @r + r = @raphael shortrefs = refs # Truncate if longer than 15 chars shortrefs = shortrefs.substr(0, 15) + "…" if shortrefs.length > 17 - text = r.text(x + 4, y, shortrefs).attr( - "text-anchor": "start" + text = r.text(x + 5, y + 8 + 10, shortrefs).attr( font: "10px Monaco, monospace" fill: "#FFF" title: refs ) textbox = text.getBBox() + text.transform ["t", textbox.height / -4, textbox.width / 2 + 5, "r90"] # Create rectangle based on the size of the textbox - rect = r.rect(x, y - 7, textbox.width + 5, textbox.height + 5, 4).attr( + rect = r.rect(x, y, textbox.width + 15, textbox.height + 5, 4).attr( fill: "#000" - "fill-opacity": .5 + "fill-opacity": .7 stroke: "none" ) - triangle = r.path(["M", x - 5, y, "L", x - 15, y - 4, "L", x - 15, y + 4, "Z"]).attr( + triangle = r.path(["M", x, y + 5, "L", x + 4, y + 15, "L", x - 4, y + 15, "Z"]).attr( fill: "#000" - "fill-opacity": .5 + "fill-opacity": .7 stroke: "none" ) - - label = r.set(rect, text) - label.transform(["t", -rect.getBBox().width - 15, 0]) - + # Rotate and reposition rectangle over text + rect.transform ["r", 90, x, y, "t", 15, -9] # Set text to front text.toFront() appendAnchor: (top, commit, x, y) -> - r = @r + r = @raphael options = @options anchor = r.circle(x, y, 10).attr( fill: "#000" @@ -166,92 +204,18 @@ class BranchGraph ).click(-> window.open options.commit_url.replace("%s", commit.id), "_blank" ).hover(-> - @tooltip = r.commitTooltip(x + 5, y, commit) + @tooltip = r.commitTooltip(x, y + 5, commit) top.push @tooltip.insertBefore(this) , -> @tooltip and @tooltip.remove() and delete @tooltip ) top.push anchor - drawDot: (x, y, commit) -> - r = @r - r.circle(x, y, 3).attr( - fill: @colors[commit.space] - stroke: "none" - ) - r.rect(@offsetX + @unitSpace * @mspace + 10, y - 10, 20, 20).attr( - fill: "url(#{commit.author.icon})" - stroke: @colors[commit.space] - "stroke-width": 2 - ) - r.text(@offsetX + @unitSpace * @mspace + 35, y, commit.message.split("\n")[0]).attr( - "text-anchor": "start" - font: "14px Monaco, monospace" - ) - - drawLines: (x, y, commit) -> - r = @r - for parent, i in commit.parents - parentCommit = @preparedCommits[parent[0]] - parentY = @offsetY + @unitTime * parentCommit.time - parentX1 = @offsetX + @unitSpace * (@mspace - parentCommit.space) - parentX2 = @offsetX + @unitSpace * (@mspace - parent[1]) - - # Set line color - if parentCommit.space <= commit.space - color = @colors[commit.space] - - else - color = @colors[parentCommit.space] - - # Build line shape - if parent[1] is commit.space - offset = [0, 5] - arrow = "l-2,5,4,0,-2,-5,0,5" - - else if parent[1] < commit.space - offset = [3, 3] - arrow = "l5,0,-2,4,-3,-4,4,2" - - else - offset = [-3, 3] - arrow = "l-5,0,2,4,3,-4,-4,2" - - # Start point - route = ["M", x + offset[0], y + offset[1]] - - # Add arrow if not first parent - if i > 0 - route.push(arrow) - - # Circumvent if overlap - if commit.space isnt parentCommit.space or commit.space isnt parent[1] - route.push( - "L", parentX2, y + 10, - "L", parentX2, parentY - 5, - ) - - # End point - route.push("L", parentX1, parentY) - - r - .path(route) - .attr( - stroke: color - "stroke-width": 2) - - markCommit: (x, y, commit, graphHeight) -> - if commit.id is @options.commit_id - r = @r - r.path(["M", x + 5, y, "L", x + 15, y + 4, "L", x + 15, y - 4, "Z"]).attr( - fill: "#000" - "fill-opacity": .5 - stroke: "none" - ) - # Displayed in the center - @element.scrollTop(y - graphHeight / 2) - Raphael::commitTooltip = (x, y, commit) -> + icon = undefined + nameText = undefined + idText = undefined + messageText = undefined boxWidth = 300 boxHeight = 200 icon = @image(commit.author.icon, x, y, 20, 20) diff --git a/app/assets/javascripts/extensions/jquery.js.coffee b/app/assets/javascripts/extensions/jquery.js.coffee deleted file mode 100644 index 8a997fe3..00000000 --- a/app/assets/javascripts/extensions/jquery.js.coffee +++ /dev/null @@ -1,9 +0,0 @@ -$.fn.showAndHide = -> - $(@).show(). - delay(3000). - fadeOut() - -$.fn.enableButton = -> - $(@).removeAttr('disabled'). - removeClass('disabled') - diff --git a/app/assets/javascripts/main.js.coffee b/app/assets/javascripts/main.js.coffee index 39ec86e6..b61df846 100644 --- a/app/assets/javascripts/main.js.coffee +++ b/app/assets/javascripts/main.js.coffee @@ -7,8 +7,6 @@ window.slugify = (text) -> window.ajaxGet = (url) -> $.ajax({type: "GET", url: url, dataType: "script"}) -window.showAndHide = (selector) -> - window.errorMessage = (message) -> ehtml = $("

") ehtml.addClass("error_message") diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee index 213133bc..42207a39 100644 --- a/app/assets/javascripts/profile.js.coffee +++ b/app/assets/javascripts/profile.js.coffee @@ -15,8 +15,6 @@ $ -> $(this).find('.update-failed').hide() $('.update-username form').on 'ajax:complete', -> - $(this).find('.btn-save').enableButton() + $(this).find('.save-btn').removeAttr('disabled') + $(this).find('.save-btn').removeClass('disabled') $(this).find('.loading-gif').hide() - - $('.update-notifications').on 'ajax:complete', -> - $(this).find('.btn-save').enableButton() diff --git a/app/assets/javascripts/tree.js.coffee b/app/assets/javascripts/tree.js.coffee index 10d0df70..2603b9a9 100644 --- a/app/assets/javascripts/tree.js.coffee +++ b/app/assets/javascripts/tree.js.coffee @@ -6,7 +6,7 @@ $ -> $('span.log_loading:first').removeClass('hide') $('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live "click", -> - $("#tree-content-holder").hide("slide", { direction: "left" }, 400) + $("#tree-content-holder").hide("slide", { direction: "left" }, 150) # Make the entire tree-item row clickable, but not if clicking another link (like a commit message) $("#tree-slider .tree-item").live 'click', (e) -> diff --git a/app/assets/javascripts/wall.js.coffee b/app/assets/javascripts/wall.js.coffee index e2fca3dd..a35c8c60 100644 --- a/app/assets/javascripts/wall.js.coffee +++ b/app/assets/javascripts/wall.js.coffee @@ -58,26 +58,14 @@ form.show() renderNote: (note) -> - template = Wall.noteTemplate() - template = template.replace('{{author_name}}', note.author.name) - template = template.replace('{{created_at}}', note.created_at) - template = template.replace('{{text}}', linkify(sanitize(note.body))) + author = '' + note.author.name + '' + body = '' + linkify(sanitize(note.body)) + '' + file = '' + time = '' + note.created_at + '' if note.attachment - file = '' + note.attachment + '' - else - file = '' - template = template.replace('{{file}}', file) + file = '' + note.attachment + '' + + html = '

  • ' + author + body + file + time + '
  • ' - - $('ul.notes').append(template) - - noteTemplate: -> - return '
  • - {{author_name}} - - {{text}} - {{file}} - - {{created_at}} -
  • ' + $('ul.notes').append(html) diff --git a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss index 842e7a08..cb055a1c 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss @@ -100,9 +100,8 @@ margin-top: 0; } - .btn { - position: relative; - top: -2px; + .btn-tiny { + @include box-shadow(0 0px 0px 1px #f1f1f1); } .nav-pills { diff --git a/app/assets/stylesheets/sections/graph.scss b/app/assets/stylesheets/sections/graph.scss index 7da00719..5800098a 100644 --- a/app/assets/stylesheets/sections/graph.scss +++ b/app/assets/stylesheets/sections/graph.scss @@ -12,7 +12,7 @@ .graph { background: #f1f1f1; cursor: move; - height: 500px; + height: 70%; overflow: hidden; } } diff --git a/app/assets/stylesheets/sections/nav.scss b/app/assets/stylesheets/sections/nav.scss index 514b0b7c..50091cd7 100644 --- a/app/assets/stylesheets/sections/nav.scss +++ b/app/assets/stylesheets/sections/nav.scss @@ -1,64 +1,68 @@ -.main-nav { +/* + * Main Menu of Application + * + */ +ul.main_menu { + margin: auto; margin: 30px 0; margin-top: 10px; - border-bottom: 1px solid #E1E1E1; - - ul { - margin: auto; - height: 39px; + height: 38px; + position: relative; + overflow: hidden; + .count { position: relative; - top: 3px; - overflow: hidden; - .count { - position: relative; - top: -1px; - display: inline-block; - height: 15px; - margin: 0 0 0 5px; - padding: 0 8px 1px 8px; - height: auto; - font-size: 0.82em; - line-height: 14px; - text-align: center; - color: #777; - } - .label { - background: $hover; - text-shadow: none; - color: $style_color; - } - li { - list-style-type: none; - margin: 0; - display: table-cell; - width: 1%; - &.active { - border-bottom: 3px solid #777; - a { - color: $style_color; - font-weight: bolder; - } + top: -1px; + display: inline-block; + height: 15px; + margin: 0 0 0 5px; + padding: 0 8px 1px 8px; + height: auto; + font-size: 0.82em; + line-height: 14px; + text-align: center; + color: #777; + } + .label { + background: $hover; + text-shadow: none; + color: $style_color; + } + li { + list-style-type: none; + margin: 0; + display: table-cell; + width: 1%; + border-bottom: 2px solid #EEE; + &.active { + border-bottom: 2px solid #474D57; + a { + color: $style_color; } + } - &.home { - a { - i { - font-size: 20px; - position: relative; - top: 4px; - } + &.home { + a { + i { + font-size: 20px; + position: relative; + top: 4px; } } } - a { - display: block; - text-align: center; - font-weight: normal; - height: 36px; - line-height: 34px; - color: #777; - text-shadow: 0 1px 1px white; - padding: 0 10px; - } + } + a { + display: block; + text-align: center; + font-weight: normal; + height: 36px; + line-height: 36px; + color: #777; + text-shadow: 0 1px 1px white; + padding: 0 10px; } } +/* + * End of Main Menu + * + */ + diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index a8628fc5..ae2e1b25 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -274,15 +274,6 @@ ul.notes { } -.common-note-form { - margin: 0; - height: 140px; - background: #F9F9F9; - padding: 3px; - padding-bottom: 25px; - border: 1px solid #DDD; -} - .note-form-actions { background: #F9F9F9; @@ -290,8 +281,8 @@ ul.notes { padding: 0 5px; .note-form-option { - margin-top: 10px; - margin-left: 30px; + margin-top: 8px; + margin-left: 15px; @extend .pull-left; } diff --git a/app/assets/stylesheets/sections/wall.scss b/app/assets/stylesheets/sections/wall.scss index d6ac08fc..598d9df8 100644 --- a/app/assets/stylesheets/sections/wall.scss +++ b/app/assets/stylesheets/sections/wall.scss @@ -14,31 +14,12 @@ .notes { margin-bottom: 160px; - background: #FFE; - border: 1px solid #EED; - - > li { - @extend .clearfix; - border-bottom: 1px solid #EED; - padding: 10px; - } .wall-author { color: #666; - float: left; - font-size: 12px; - width: 120px; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - } - - .wall-text { - border-left: 1px solid #CCC; - margin-left: 10px; - padding-left: 10px; - float: left; - width: 75%; + margin-right: 10px; + border-right: 1px solid #CCC; + padding-right: 5px } .wall-file { diff --git a/app/contexts/commit_load_context.rb b/app/contexts/commit_load_context.rb index 2cf5420d..1f23f633 100644 --- a/app/contexts/commit_load_context.rb +++ b/app/contexts/commit_load_context.rb @@ -12,6 +12,7 @@ class CommitLoadContext < BaseContext commit = project.repository.commit(params[:id]) if commit + commit = CommitDecorator.decorate(commit) line_notes = project.notes.for_commit_id(commit.id).inline result[:commit] = commit diff --git a/app/contexts/notes/create_context.rb b/app/contexts/notes/create_context.rb index 36ea76ff..1367dff4 100644 --- a/app/contexts/notes/create_context.rb +++ b/app/contexts/notes/create_context.rb @@ -3,6 +3,8 @@ module Notes def execute note = project.notes.new(params[:note]) note.author = current_user + note.notify = params[:notify].present? + note.notify_author = params[:notify_author].present? note.save note end diff --git a/app/contexts/projects/transfer_context.rb b/app/contexts/projects/transfer_context.rb deleted file mode 100644 index aed396a5..00000000 --- a/app/contexts/projects/transfer_context.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Projects - class TransferContext < BaseContext - def execute(role = :default) - namespace_id = params[:project].delete(:namespace_id) - allowed_transfer = can?(current_user, :change_namespace, project) || role == :admin - - if allowed_transfer && namespace_id.present? - if namespace_id == Namespace.global_id - if project.namespace.present? - # Transfer to global namespace from anyone - project.transfer(nil) - end - elsif namespace_id.to_i != project.namespace_id - # Transfer to someone namespace - namespace = Namespace.find(namespace_id) - project.transfer(namespace) - end - end - - rescue ProjectTransferService::TransferError => ex - project.reload - project.errors.add(:namespace_id, ex.message) - false - end - end -end - diff --git a/app/contexts/projects/update_context.rb b/app/contexts/projects/update_context.rb index 40385fa6..e5d09b7d 100644 --- a/app/contexts/projects/update_context.rb +++ b/app/contexts/projects/update_context.rb @@ -1,8 +1,24 @@ module Projects class UpdateContext < BaseContext def execute(role = :default) - params[:project].delete(:namespace_id) + namespace_id = params[:project].delete(:namespace_id) params[:project].delete(:public) unless can?(current_user, :change_public_mode, project) + + allowed_transfer = can?(current_user, :change_namespace, project) || role == :admin + + if allowed_transfer && namespace_id.present? + if namespace_id == Namespace.global_id + if project.namespace.present? + # Transfer to global namespace from anyone + project.transfer(nil) + end + elsif namespace_id.to_i != project.namespace_id + # Transfer to someone namespace + namespace = Namespace.find(namespace_id) + project.transfer(namespace) + end + end + project.update_attributes(params[:project], as: role) end end diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index bbb80cbb..8ae0bba9 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -19,6 +19,34 @@ class Admin::ProjectsController < Admin::ApplicationController @users = @users.all end + def edit + end + + def team_update + @project.team.add_users_ids(params[:user_ids], params[:project_access]) + + redirect_to [:admin, @project], notice: 'Project was successfully updated.' + end + + def update + project.creator = current_user unless project.creator + + status = ::Projects::UpdateContext.new(project, current_user, params).execute(:admin) + + if status + redirect_to [:admin, @project], notice: 'Project was successfully updated.' + else + render action: "edit" + end + end + + def destroy + @project.team.truncate + @project.destroy + + redirect_to admin_projects_path, notice: 'Project was successfully deleted.' + end + protected def project diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 20cb13e7..43e6f099 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -84,8 +84,6 @@ class Admin::UsersController < Admin::ApplicationController format.html { redirect_to [:admin, admin_user], notice: 'User was successfully updated.' } format.json { head :ok } else - # restore username to keep form action url. - admin_user.username = params[:id] format.html { render action: "edit" } format.json { render json: admin_user.errors, status: :unprocessable_entity } end diff --git a/app/controllers/blame_controller.rb b/app/controllers/blame_controller.rb index 310b567c..37d7245c 100644 --- a/app/controllers/blame_controller.rb +++ b/app/controllers/blame_controller.rb @@ -7,7 +7,10 @@ class BlameController < ProjectResourceController before_filter :authorize_code_access! before_filter :require_non_empty_project + before_filter :assign_ref_vars + def show - @blame = Gitlab::Git::Blame.new(project.repository, @commit.id, @path) + @repo = @project.repo + @blame = Grit::Blob.blame(@repo, @commit.id, @path) end end diff --git a/app/controllers/blob_controller.rb b/app/controllers/blob_controller.rb index 530b72fe..d4a45d95 100644 --- a/app/controllers/blob_controller.rb +++ b/app/controllers/blob_controller.rb @@ -7,6 +7,8 @@ class BlobController < ProjectResourceController before_filter :authorize_code_access! before_filter :require_non_empty_project + before_filter :assign_ref_vars + def show if @tree.is_blob? send_data( diff --git a/app/controllers/commits_controller.rb b/app/controllers/commits_controller.rb index cde1f459..9dc0d968 100644 --- a/app/controllers/commits_controller.rb +++ b/app/controllers/commits_controller.rb @@ -13,6 +13,7 @@ class CommitsController < ProjectResourceController @limit, @offset = (params[:limit] || 40), (params[:offset] || 0) @commits = @repo.commits(@ref, @path, @limit, @offset) + @commits = CommitDecorator.decorate_collection(@commits) respond_to do |format| format.html # index.html.erb diff --git a/app/controllers/compare_controller.rb b/app/controllers/compare_controller.rb index 750e9c23..bd3f1115 100644 --- a/app/controllers/compare_controller.rb +++ b/app/controllers/compare_controller.rb @@ -8,13 +8,15 @@ class CompareController < ProjectResourceController end def show - compare = Gitlab::Git::Compare.new(project.repository, params[:from], params[:to]) + result = Commit.compare(project, params[:from], params[:to]) - @commits = compare.commits - @commit = compare.commit - @diffs = compare.diffs - @refs_are_same = compare.same + @commits = result[:commits] + @commit = result[:commit] + @diffs = result[:diffs] + @refs_are_same = result[:same] @line_notes = [] + + @commits = CommitDecorator.decorate_collection(@commits) end def create diff --git a/app/controllers/edit_tree_controller.rb b/app/controllers/edit_tree_controller.rb deleted file mode 100644 index aae983aa..00000000 --- a/app/controllers/edit_tree_controller.rb +++ /dev/null @@ -1,47 +0,0 @@ -# Controller for edit a repository's file -class EditTreeController < ProjectResourceController - include ExtractsPath - - # Authorize - before_filter :authorize_read_project! - before_filter :authorize_code_access! - before_filter :require_non_empty_project - - before_filter :edit_requirements, only: [:edit, :update] - - def show - @last_commit = @project.repository.last_commit_for(@ref, @path).sha - end - - def update - edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path) - updated_successfully = edit_file_action.commit!( - params[:content], - params[:commit_message], - params[:last_commit] - ) - - if updated_successfully - redirect_to project_tree_path(@project, @id), notice: "Your changes have been successfully commited" - else - flash[:notice] = "Your changes could not be commited, because the file has been changed" - render :edit - end - end - - private - - def edit_requirements - unless @tree.is_blob? && @tree.text? - redirect_to project_tree_path(@project, @id), notice: "You can only edit text files" - end - - allowed = if project.protected_branch? @ref - can?(current_user, :push_code_to_protected_branches, project) - else - can?(current_user, :push_code, project) - end - - return access_denied! unless allowed - end -end diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb index 1950ebb2..88e0df16 100644 --- a/app/controllers/merge_requests_controller.rb +++ b/app/controllers/merge_requests_controller.rb @@ -94,10 +94,12 @@ class MergeRequestsController < ProjectResourceController def branch_from @commit = @repository.commit(params[:ref]) + @commit = CommitDecorator.decorate(@commit) end def branch_to @commit = @repository.commit(params[:ref]) + @commit = CommitDecorator.decorate(@commit) end def ci_status @@ -127,11 +129,11 @@ class MergeRequestsController < ProjectResourceController def validates_merge_request # Show git not found page if target branch doesn't exist - return invalid_mr unless @project.repository.branch_names.include?(@merge_request.target_branch) + return invalid_mr unless @project.repo.heads.map(&:name).include?(@merge_request.target_branch) # Show git not found page if source branch doesn't exist # and there is no saved commits between source & target branch - return invalid_mr if !@project.repository.branch_names.include?(@merge_request.source_branch) && @merge_request.commits.blank? + return invalid_mr if !@project.repo.heads.map(&:name).include?(@merge_request.source_branch) && @merge_request.commits.blank? end def define_show_vars @@ -141,6 +143,7 @@ class MergeRequestsController < ProjectResourceController # Get commits from repository # or from cache if already merged @commits = @merge_request.commits + @commits = CommitDecorator.decorate_collection(@commits) @allowed_to_merge = allowed_to_merge? @show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb deleted file mode 100644 index e44e0aa8..00000000 --- a/app/controllers/notifications_controller.rb +++ /dev/null @@ -1,13 +0,0 @@ -class NotificationsController < ApplicationController - layout 'profile' - - def show - @notification = current_user.notification - @projects = current_user.authorized_projects - end - - def update - current_user.notification_level = params[:notification_level] - @saved = current_user.save - end -end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 8e55aa01..f2718344 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -4,7 +4,7 @@ class ProjectsController < ProjectResourceController # Authorize before_filter :authorize_read_project!, except: [:index, :new, :create] - before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer] + before_filter :authorize_admin_project!, only: [:edit, :update, :destroy] before_filter :require_non_empty_project, only: [:blob, :tree, :graph] layout 'application', only: [:new, :create] @@ -45,10 +45,10 @@ class ProjectsController < ProjectResourceController format.js end end - end - def transfer - ::Projects::TransferContext.new(project, current_user, params).execute + rescue Project::TransferError => ex + @error = ex + render :update_failed end def show @@ -57,11 +57,11 @@ class ProjectsController < ProjectResourceController respond_to do |format| format.html do - if @project.empty_repo? - render "projects/empty" - else + if @project.repository && !@project.repository.empty? @last_push = current_user.recent_push(@project.id) render :show + else + render "projects/empty" end end format.js diff --git a/app/controllers/refs_controller.rb b/app/controllers/refs_controller.rb index eb8d1e19..0e4dba3d 100644 --- a/app/controllers/refs_controller.rb +++ b/app/controllers/refs_controller.rb @@ -34,6 +34,7 @@ class RefsController < ProjectResourceController @logs = contents.map do |content| file = params[:path] ? File.join(params[:path], content.name) : content.name last_commit = @repo.commits(@commit.id, file, 1).last + last_commit = CommitDecorator.decorate(last_commit) { file_name: content.name, commit: last_commit @@ -48,7 +49,9 @@ class RefsController < ProjectResourceController @repo = project.repository @commit = @repo.commit(@ref) + @commit = CommitDecorator.decorate(@commit) @tree = Tree.new(@commit.tree, @ref, params[:path]) + @tree = TreeDecorator.new(@tree) @hex_path = Digest::SHA1.hexdigest(params[:path] || "") if params[:path] diff --git a/app/controllers/tree_controller.rb b/app/controllers/tree_controller.rb index a03ea3ff..2151bd7c 100644 --- a/app/controllers/tree_controller.rb +++ b/app/controllers/tree_controller.rb @@ -7,6 +7,9 @@ class TreeController < ProjectResourceController before_filter :authorize_code_access! before_filter :require_non_empty_project + before_filter :assign_ref_vars + before_filter :edit_requirements, only: [:edit, :update] + def show @hex_path = Digest::SHA1.hexdigest(@path) @logs_path = logs_file_project_ref_path(@project, @ref, @path) @@ -17,4 +20,40 @@ class TreeController < ProjectResourceController format.js { no_cache_headers } end end + + def edit + @last_commit = @project.repository.last_commit_for(@ref, @path).sha + end + + def update + edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path) + updated_successfully = edit_file_action.commit!( + params[:content], + params[:commit_message], + params[:last_commit] + ) + + if updated_successfully + redirect_to project_tree_path(@project, @id), notice: "Your changes have been successfully commited" + else + flash[:notice] = "Your changes could not be commited, because the file has been changed" + render :edit + end + end + + private + + def edit_requirements + unless @tree.is_blob? && @tree.text? + redirect_to project_tree_path(@project, @id), notice: "You can only edit text files" + end + + allowed = if project.protected_branch? @ref + can?(current_user, :push_code_to_protected_branches, project) + else + can?(current_user, :push_code, project) + end + + return access_denied! unless allowed + end end diff --git a/app/decorators/commit_decorator.rb b/app/decorators/commit_decorator.rb new file mode 100644 index 00000000..0337d8d4 --- /dev/null +++ b/app/decorators/commit_decorator.rb @@ -0,0 +1,93 @@ +class CommitDecorator < ApplicationDecorator + decorates :commit + + # Returns a string describing the commit for use in a link title + # + # Example + # + # "Commit: Alex Denisov - Project git clone panel" + def link_title + "Commit: #{author_name} - #{title}" + end + + # Returns the commits title. + # + # Usually, the commit title is the first line of the commit message. + # In case this first line is longer than 80 characters, it is cut off + # after 70 characters and ellipses (`&hellp;`) are appended. + def title + title = safe_message + + return no_commit_message if title.blank? + + title_end = title.index(/\n/) + if (!title_end && title.length > 80) || (title_end && title_end > 80) + title[0..69] << "…".html_safe + else + title.split(/\n/, 2).first + end + end + + # Returns the commits description + # + # cut off, ellipses (`&hellp;`) are prepended to the commit message. + def description + description = safe_message + + title_end = description.index(/\n/) + if (!title_end && description.length > 80) || (title_end && title_end > 80) + "…".html_safe << description[70..-1] + else + description.split(/\n/, 2)[1].try(:chomp) + end + end + + # Returns a link to the commit author. If the author has a matching user and + # is a member of the current @project it will link to the team member page. + # Otherwise it will link to the author email as specified in the commit. + # + # options: + # avatar: true will prepend the avatar image + # size: size of the avatar image in px + def author_link(options = {}) + person_link(options.merge source: :author) + end + + # Just like #author_link but for the committer. + def committer_link(options = {}) + person_link(options.merge source: :committer) + end + + protected + + def no_commit_message + "--no commit message" + end + + # Private: Returns a link to a person. If the person has a matching user and + # is a member of the current @project it will link to the team member page. + # Otherwise it will link to the person email as specified in the commit. + # + # options: + # source: one of :author or :committer + # avatar: true will prepend the avatar image + # size: size of the avatar image in px + def person_link(options = {}) + source_name = send "#{options[:source]}_name".to_sym + source_email = send "#{options[:source]}_email".to_sym + text = if options[:avatar] + avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "" + %Q{#{avatar} #{source_name}} + else + source_name + end + + user = User.where('name like ? or email like ?', source_name, source_email).first + + if user.nil? + h.mail_to(source_email, text.html_safe, class: "commit-#{options[:source]}-link") + else + h.link_to(text.html_safe, h.user_path(user), class: "commit-#{options[:source]}-link") + end + end +end diff --git a/app/decorators/tree_decorator.rb b/app/decorators/tree_decorator.rb new file mode 100644 index 00000000..0e760f97 --- /dev/null +++ b/app/decorators/tree_decorator.rb @@ -0,0 +1,33 @@ +class TreeDecorator < ApplicationDecorator + decorates :tree + + def breadcrumbs(max_links = 2) + if path + part_path = "" + parts = path.split("\/") + + yield('..', nil) if parts.count > max_links + + parts.each do |part| + part_path = File.join(part_path, part) unless part_path.empty? + part_path = part if part_path.empty? + + next unless parts.last(2).include?(part) if parts.count > max_links + yield(part, h.tree_join(ref, part_path)) + end + end + end + + def up_dir? + path.present? + end + + def up_dir_path + file = File.join(path, "..") + h.tree_join(ref, file) + end + + def readme + @readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i } + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index cb9cb1a3..f03039e4 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -96,7 +96,7 @@ module ApplicationHelper ] project_nav = [] - if @project && @project.repository.exists? && @project.repository.root_ref + if @project && @project.repository && @project.repository.root_ref project_nav = [ { label: "#{simple_sanitize(@project.name_with_namespace)} - Issues", url: project_issues_path(@project) }, { label: "#{simple_sanitize(@project.name_with_namespace)} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) }, diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 95ca294c..acdd48e0 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -1,20 +1,4 @@ module CommitsHelper - # Returns a link to the commit author. If the author has a matching user and - # is a member of the current @project it will link to the team member page. - # Otherwise it will link to the author email as specified in the commit. - # - # options: - # avatar: true will prepend the avatar image - # size: size of the avatar image in px - def commit_author_link(commit, options = {}) - commit_person_link(commit, options.merge(source: :author)) - end - - # Just like #author_link but for the committer. - def commit_committer_link(commit, options = {}) - commit_person_link(commit, options.merge(source: :committer)) - end - def identification_type(line) if line[0] == "+" "new" @@ -109,7 +93,9 @@ module CommitsHelper end def commit_to_html commit - escape_javascript(render 'commits/commit', commit: commit) + if commit.model + escape_javascript(render 'commits/commit', commit: commit) + end end def diff_line_content(line) @@ -119,58 +105,4 @@ module CommitsHelper line end end - - # Breadcrumb links for a Project and, if applicable, a tree path - def commits_breadcrumbs - return unless @project && @ref - - # Add the root project link and the arrow icon - crumbs = content_tag(:li) do - content_tag(:span, nil, class: 'arrow') + - link_to(@project.name, project_commits_path(@project, @ref)) - end - - if @path - parts = @path.split('/') - - parts.each_with_index do |part, i| - crumbs += content_tag(:span, '/', class: 'divider') - crumbs += content_tag(:li) do - # The text is just the individual part, but the link needs all the parts before it - link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/'))) - end - end - end - - crumbs.html_safe - end - - protected - - # Private: Returns a link to a person. If the person has a matching user and - # is a member of the current @project it will link to the team member page. - # Otherwise it will link to the person email as specified in the commit. - # - # options: - # source: one of :author or :committer - # avatar: true will prepend the avatar image - # size: size of the avatar image in px - def commit_person_link(commit, options = {}) - source_name = commit.send "#{options[:source]}_name".to_sym - source_email = commit.send "#{options[:source]}_email".to_sym - text = if options[:avatar] - avatar = image_tag(gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "") - %Q{#{avatar} #{source_name}} - else - source_name - end - - user = User.where('name like ? or email like ?', source_name, source_email).first - - if user.nil? - mail_to(source_email, text.html_safe, class: "commit-#{options[:source]}-link") - else - link_to(text.html_safe, user_path(user), class: "commit-#{options[:source]}-link") - end - end end diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb index ca7d823a..36933015 100644 --- a/app/helpers/graph_helper.rb +++ b/app/helpers/graph_helper.rb @@ -1,13 +1,6 @@ module GraphHelper - def get_refs(commit) - refs = "" - refs += commit.refs.collect{|r|r.name}.join(" ") if commit.refs - - # append note count - notes = @project.notes.for_commit_id(commit.id) - refs += "[#{notes.count}]" if notes.any? - - refs + def join_with_space(ary) + ary.collect{|r|r.name}.join(" ") unless ary.nil? end def parents_zip_spaces(parents, parent_spaces) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 70ebbdd3..54385117 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -48,31 +48,19 @@ module IssuesHelper if @project.used_default_issues_tracker? project_issues_filter_path(@project) else - url = Gitlab.config.issues_tracker[@project.issues_tracker]["project_url"] + url = Settings[:issues_tracker][@project.issues_tracker]["project_url"] url.gsub(':project_id', @project.id.to_s) .gsub(':issues_tracker_id', @project.issues_tracker_id.to_s) end end - def url_for_new_issue - return "" if @project.nil? - - if @project.used_default_issues_tracker? - url = new_project_issue_path project_id: @project - else - url = Gitlab.config.issues_tracker[@project.issues_tracker]["new_issue_url"] - url.gsub(':project_id', @project.id.to_s) - .gsub(':issues_tracker_id', @project.issues_tracker_id.to_s) - end - end - def url_for_issue(issue_id) return "" if @project.nil? if @project.used_default_issues_tracker? url = project_issue_url project_id: @project, id: issue_id else - url = Gitlab.config.issues_tracker[@project.issues_tracker]["issues_url"] + url = Settings[:issues_tracker][@project.issues_tracker]["issues_url"] url.gsub(':id', issue_id.to_s) .gsub(':project_id', @project.id.to_s) .gsub(':issues_tracker_id', @project.issues_tracker_id.to_s) diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb deleted file mode 100644 index 7342393a..00000000 --- a/app/helpers/notifications_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module NotificationsHelper -end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 1cba9476..fab0085b 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -70,26 +70,28 @@ module TreeHelper end end - def tree_breadcrumbs(tree, max_links = 2) - if tree.path - part_path = "" - parts = tree.path.split("\/") + # Breadcrumb links for a Project and, if applicable, a tree path + def breadcrumbs + return unless @project && @ref - yield('..', nil) if parts.count > max_links + # Add the root project link and the arrow icon + crumbs = content_tag(:li) do + content_tag(:span, nil, class: 'arrow') + + link_to(@project.name, project_commits_path(@project, @ref)) + end - parts.each do |part| - part_path = File.join(part_path, part) unless part_path.empty? - part_path = part if part_path.empty? + if @path + parts = @path.split('/') - next unless parts.last(2).include?(part) if parts.count > max_links - yield(part, tree_join(tree.ref, part_path)) + parts.each_with_index do |part, i| + crumbs += content_tag(:span, '/', class: 'divider') + crumbs += content_tag(:li) do + # The text is just the individual part, but the link needs all the parts before it + link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/'))) + end end end - end - def up_dir_path tree - file = File.join(tree.path, "..") - tree_join(tree.ref, file) + crumbs.html_safe end - end diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb index 79731b60..5b69886f 100644 --- a/app/mailers/emails/issues.rb +++ b/app/mailers/emails/issues.rb @@ -1,9 +1,9 @@ module Emails module Issues - def new_issue_email(recipient_id, issue_id) + def new_issue_email(issue_id) @issue = Issue.find(issue_id) @project = @issue.project - mail(to: recipient(recipient_id), subject: subject("new issue ##{@issue.id}", @issue.title)) + mail(to: @issue.assignee_email, subject: subject("new issue ##{@issue.id}", @issue.title)) end def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) @@ -13,14 +13,6 @@ module Emails mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title)) end - def closed_issue_email(recipient_id, issue_id, updated_by_user_id) - @issue = Issue.find issue_id - @project = @issue.project - @updated_by = User.find updated_by_user_id - mail(to: recipient(recipient_id), - subject: subject("Closed issue ##{@issue.id}", @issue.title)) - end - def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id) @issue = Issue.find issue_id @issue_status = status diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb index 806f1b01..35890460 100644 --- a/app/mailers/emails/merge_requests.rb +++ b/app/mailers/emails/merge_requests.rb @@ -1,9 +1,9 @@ module Emails module MergeRequests - def new_merge_request_email(recipient_id, merge_request_id) + def new_merge_request_email(merge_request_id) @merge_request = MergeRequest.find(merge_request_id) @project = @merge_request.project - mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.id}", @merge_request.title)) + mail(to: @merge_request.assignee_email, subject: subject("new merge request !#{@merge_request.id}", @merge_request.title)) end def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) @@ -12,18 +12,5 @@ module Emails @project = @merge_request.project mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title)) end - - def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) - @merge_request = MergeRequest.find(merge_request_id) - @project = @merge_request.project - @updated_by = User.find updated_by_user_id - mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.id}", @merge_request.title)) - end - - def merged_merge_request_email(recipient_id, merge_request_id) - @merge_request = MergeRequest.find(merge_request_id) - @project = @merge_request.project - mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.id}", @merge_request.title)) - end end end diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb index 769b6e0b..de51debf 100644 --- a/app/mailers/emails/notes.rb +++ b/app/mailers/emails/notes.rb @@ -3,6 +3,7 @@ module Emails def note_commit_email(recipient_id, note_id) @note = Note.find(note_id) @commit = @note.noteable + @commit = CommitDecorator.decorate(@commit) @project = @note.project mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title)) end diff --git a/app/models/ability.rb b/app/models/ability.rb index 5b49104d..41f71274 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -41,7 +41,7 @@ class Ability rules << project_guest_rules end - if project.owner == user || user.admin? + if project.owner == user rules << project_admin_rules end diff --git a/app/models/commit.rb b/app/models/commit.rb index e3363350..daba5414 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -8,70 +8,168 @@ class Commit # DIFF_SAFE_SIZE = 100 - def self.decorate(commits) - commits.map { |c| self.new(c) } + attr_accessor :commit, :head, :refs + + delegate :message, :authored_date, :committed_date, :parents, :sha, + :date, :committer, :author, :diffs, :tree, :id, :stats, + :to_patch, to: :commit + + class << self + def find_or_first(repo, commit_id = nil, root_ref) + commit = if commit_id + repo.commit(commit_id) + else + repo.commits(root_ref).first + end + + Commit.new(commit) if commit + end + + def fresh_commits(repo, n = 10) + commits = repo.heads.map do |h| + repo.commits(h.name, n).map { |c| Commit.new(c, h) } + end.flatten.uniq { |c| c.id } + + commits.sort! do |x, y| + y.committed_date <=> x.committed_date + end + + commits[0...n] + end + + def commits_with_refs(repo, n = 20) + commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) } + + commits.sort! do |x, y| + y.committed_date <=> x.committed_date + end + + commits[0..n] + end + + def commits_since(repo, date) + commits = repo.heads.map do |h| + repo.log(h.name, nil, since: date).each { |c| Commit.new(c, h) } + end.flatten.uniq { |c| c.id } + + commits.sort! do |x, y| + y.committed_date <=> x.committed_date + end + + commits + end + + def commits(repo, ref, path = nil, limit = nil, offset = nil) + if path + repo.log(ref, path, max_count: limit, skip: offset) + elsif limit && offset + repo.commits(ref, limit, offset) + else + repo.commits(ref) + end.map{ |c| Commit.new(c) } + end + + def commits_between(repo, from, to) + repo.commits_between(from, to).map { |c| Commit.new(c) } + end + + def compare(project, from, to) + result = { + commits: [], + diffs: [], + commit: nil, + same: false + } + + return result unless from && to + + first = project.repository.commit(to.try(:strip)) + last = project.repository.commit(from.try(:strip)) + + if first && last + result[:same] = (first.id == last.id) + result[:commits] = project.repo.commits_between(last.id, first.id).map {|c| Commit.new(c)} + + # Dont load diff for 100+ commits + result[:diffs] = if result[:commits].size > 100 + [] + else + project.repo.diff(last.id, first.id) rescue [] + end + + result[:commit] = Commit.new(first) + end + + result + end end - attr_accessor :raw - - def initialize(raw_commit) + def initialize(raw_commit, head = nil) raise "Nil as raw commit passed" unless raw_commit - @raw = raw_commit + @commit = raw_commit + @head = head end - def id - @raw.id + def short_id(length = 10) + id.to_s[0..length] end - # Returns a string describing the commit for use in a link title - # - # Example - # - # "Commit: Alex Denisov - Project git clone panel" - def link_title - "Commit: #{author_name} - #{title}" + def safe_message + @safe_message ||= message end - # Returns the commits title. + def created_at + committed_date + end + + def author_email + author.email + end + + def author_name + author.name + end + + # Was this commit committed by a different person than the original author? + def different_committer? + author_name != committer_name || author_email != committer_email + end + + def committer_name + committer.name + end + + def committer_email + committer.email + end + + def prev_commit + @prev_commit ||= if parents.present? + Commit.new(parents.first) + else + nil + end + end + + def prev_commit_id + prev_commit.try :id + end + + # Shows the diff between the commit's parent and the commit. # - # Usually, the commit title is the first line of the commit message. - # In case this first line is longer than 80 characters, it is cut off - # after 70 characters and ellipses (`&hellp;`) are appended. - def title - title = safe_message + # Cuts out the header and stats from #to_patch and returns only the diff. + def to_diff + # see Grit::Commit#show + patch = to_patch - return no_commit_message if title.blank? - - title_end = title.index(/\n/) - if (!title_end && title.length > 80) || (title_end && title_end > 80) - title[0..69] << "…".html_safe - else - title.split(/\n/, 2).first + # discard lines before the diff + lines = patch.split("\n") + while !lines.first.start_with?("diff --git") do + lines.shift end - end - - # Returns the commits description - # - # cut off, ellipses (`&hellp;`) are prepended to the commit message. - def description - description = safe_message - - title_end = description.index(/\n/) - if (!title_end && description.length > 80) || (title_end && title_end > 80) - "…".html_safe << description[70..-1] - else - description.split(/\n/, 2)[1].try(:chomp) - end - end - - def method_missing(m, *args, &block) - @raw.send(m, *args, &block) - end - - def respond_to?(method) - return true if @raw.respond_to?(method) - - super + lines.pop if lines.last =~ /^[\d.]+$/ # Git version + lines.pop if lines.last == "-- " # end of diff + lines.join("\n") end end diff --git a/app/models/gollum_wiki.rb b/app/models/gollum_wiki.rb index 16e801c1..a1ee3a08 100644 --- a/app/models/gollum_wiki.rb +++ b/app/models/gollum_wiki.rb @@ -50,7 +50,7 @@ class GollumWiki # Returns the last 30 Commit objects across the entire # repository. def recent_history - Gitlab::Git::Commit.fresh_commits(wiki.repo, 30) + Commit.fresh_commits(wiki.repo, 30) end # Finds a page within the repository based on a tile @@ -90,17 +90,13 @@ class GollumWiki private def create_repo! - if init_repo(path_with_namespace) + if gitlab_shell.add_repository(path_with_namespace) Gollum::Wiki.new(path_to_repo) else raise CouldNotCreateWikiError end end - def init_repo(path_with_namespace) - gitlab_shell.add_repository(path_with_namespace) - end - def commit_details(action, message = nil, title = nil) commit_message = message || default_message(action, title) @@ -118,4 +114,5 @@ class GollumWiki def path_to_repo @path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git") end + end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 6ce94417..9d42b1e1 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -152,17 +152,7 @@ class MergeRequest < ActiveRecord::Base end def commits - if st_commits.present? - # check if merge request commits are valid - if st_commits.first.respond_to?(:short_id) - st_commits - else - # if commits are invalid - simply reload it from repo - reloaded_commits - end - else - [] - end + st_commits || [] end def probably_merged? @@ -172,12 +162,6 @@ class MergeRequest < ActiveRecord::Base def reloaded_commits if opened? && unmerged_commits.any? - # we need to reset st_commits field first - # in order to prevent internal rails comparison - self.st_commits = [] - save - - # Then we can safely write unmerged commits self.st_commits = unmerged_commits save end @@ -185,8 +169,9 @@ class MergeRequest < ActiveRecord::Base end def unmerged_commits - self.project.repository. + self.project.repo. commits_between(self.target_branch, self.source_branch). + map {|c| Commit.new(c)}. sort_by(&:created_at). reverse end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index cb7164ea..e8b7d0c3 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -13,8 +13,6 @@ # class Namespace < ActiveRecord::Base - include Gitlab::ShellAdapter - attr_accessible :name, :description, :path has_many :projects, dependent: :destroy @@ -33,7 +31,7 @@ class Namespace < ActiveRecord::Base delegate :name, to: :owner, allow_nil: true, prefix: true after_create :ensure_dir_exist - after_update :move_dir, if: :path_changed? + after_update :move_dir after_destroy :rm_dir scope :root, -> { where('type IS NULL') } @@ -55,34 +53,48 @@ class Namespace < ActiveRecord::Base end def ensure_dir_exist - gitlab_shell.add_namespace(path) + unless dir_exists? + FileUtils.mkdir( namespace_full_path, mode: 0770 ) + end end - def rm_dir - gitlab_shell.rm_namespace(path) + def dir_exists? + File.exists?(namespace_full_path) + end + + def namespace_full_path + @namespace_full_path ||= File.join(Gitlab.config.gitlab_shell.repos_path, path) end def move_dir - if gitlab_shell.mv_namespace(path_was, path) - # If repositories moved successfully we need to remove old satellites - # and send update instructions to users. - # However we cannot allow rollback since we moved namespace dir - # So we basically we mute exceptions in next actions - begin - gitlab_shell.rm_satellites(path_was) - send_update_instructions - rescue - # Returning false does not rolback after_* transaction but gives - # us information about failing some of tasks - false + if path_changed? + old_path = File.join(Gitlab.config.gitlab_shell.repos_path, path_was) + new_path = File.join(Gitlab.config.gitlab_shell.repos_path, path) + if File.exists?(new_path) + raise "Already exists" + end + + + begin + # Remove satellite when moving repo + if path_was.present? + satellites_path = File.join(Gitlab.config.satellites.path, path_was) + FileUtils.rm_r( satellites_path, force: true ) + end + + FileUtils.mv( old_path, new_path ) + send_update_instructions + rescue Exception => e + raise "Namespace move error #{old_path} #{new_path}" end - else - # if we cannot move namespace directory we should rollback - # db changes in order to prevent out of sync between db and fs - raise Exception.new('namespace directory cannot be moved') end end + def rm_dir + dir_path = File.join(Gitlab.config.gitlab_shell.repos_path, path) + FileUtils.rm_r( dir_path, force: true ) + end + def send_update_instructions projects.each(&:send_move_instructions) end diff --git a/app/models/network/commit.rb b/app/models/network/commit.rb index 3cd0c015..d0bc61c3 100644 --- a/app/models/network/commit.rb +++ b/app/models/network/commit.rb @@ -8,7 +8,7 @@ module Network attr_accessor :time, :spaces, :parent_spaces def initialize(raw_commit, refs) - @commit = Gitlab::Git::Commit.new(raw_commit) + @commit = ::Commit.new(raw_commit) @time = -1 @spaces = [] @parent_spaces = [] diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb index 2957adbf..4b1abf52 100644 --- a/app/models/network/graph.rb +++ b/app/models/network/graph.rb @@ -40,12 +40,15 @@ module Network def index_commits days = [] @map = {} - @reserved = {} - @commits.each_with_index do |c,i| + @commits.reverse.each_with_index do |c,i| c.time = i days[i] = c.committed_date @map[c.id] = c + end + + @reserved = {} + days.each_index do |i| @reserved[i] = [] end @@ -132,7 +135,11 @@ module Network spaces = [] commit.parents(@map).each do |parent| - range = commit.time..parent.time + range = if commit.time < parent.time then + commit.time..parent.time + else + parent.time..commit.time + end space = if commit.space >= parent.space then find_free_parent_space(range, parent.space, -1, commit.space) @@ -159,7 +166,7 @@ module Network range.each do |i| if i != range.first && i != range.last && - @commits[i].spaces.include?(overlap_space) then + @commits[reversed_index(i)].spaces.include?(overlap_space) then return true; end @@ -177,7 +184,7 @@ module Network return end - time_range = leaves.first.time..leaves.last.time + time_range = leaves.last.time..leaves.first.time space_base = get_space_base(leaves) space = find_free_space(time_range, 2, space_base) leaves.each do |l| @@ -191,17 +198,17 @@ module Network end # and mark it as reserved - if parent_time.nil? - min_time = leaves.first.time - else - min_time = parent_time + 1 + min_time = leaves.last.time + leaves.last.parents(@map).each do |parent| + if parent.time < min_time + min_time = parent.time + end end - max_time = leaves.last.time - leaves.last.parents(@map).each do |parent| - if max_time < parent.time - max_time = parent.time - end + if parent_time.nil? + max_time = leaves.first.time + else + max_time = parent_time - 1 end mark_reserved(min_time..max_time, space) @@ -282,5 +289,9 @@ module Network end refs_cache end + + def reversed_index(index) + -index - 1 + end end end diff --git a/app/models/note.rb b/app/models/note.rb index f26420ca..f56f999f 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -22,6 +22,9 @@ class Note < ActiveRecord::Base attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id, :attachment, :line_code, :commit_id + attr_accessor :notify + attr_accessor :notify_author + belongs_to :project belongs_to :noteable, polymorphic: true belongs_to :author, class_name: "User" @@ -140,6 +143,14 @@ class Note < ActiveRecord::Base nil end + def notify + @notify ||= false + end + + def notify_author + @notify_author ||= false + end + # Returns true if this is an upvote note, # otherwise false is returned def upvote? diff --git a/app/models/notification.rb b/app/models/notification.rb deleted file mode 100644 index bfd1e2cf..00000000 --- a/app/models/notification.rb +++ /dev/null @@ -1,30 +0,0 @@ -class Notification - # - # Notification levels - # - N_DISABLED = 0 - N_PARTICIPATING = 1 - N_WATCH = 2 - - attr_accessor :user - - def self.notification_levels - [N_DISABLED, N_PARTICIPATING, N_WATCH] - end - - def initialize(user) - @user = user - end - - def disabled? - user.notification_level == N_DISABLED - end - - def participating? - user.notification_level == N_PARTICIPATING - end - - def watch? - user.notification_level == N_WATCH - end -end diff --git a/app/models/project.rb b/app/models/project.rb index 0263ffef..b13b2918 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -18,15 +18,16 @@ # public :boolean default(FALSE), not null # issues_tracker :string(255) default("gitlab"), not null # issues_tracker_id :string(255) -# snippets_enabled :boolean default(TRUE), not null # require "grit" class Project < ActiveRecord::Base - include Gitlab::ShellAdapter + include Gitolited extend Enumerize + class TransferError < StandardError; end + attr_accessible :name, :path, :description, :default_branch, :issues_tracker, :issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :wiki_enabled, :public, :import_url, as: [:default, :admin] @@ -141,7 +142,13 @@ class Project < ActiveRecord::Base end def repository - @repository ||= Repository.new(path_with_namespace, default_branch) + if path + @repository ||= Repository.new(path_with_namespace, default_branch) + else + nil + end + rescue Grit::NoSuchPathError + nil end def saved? @@ -326,14 +333,14 @@ class Project < ActiveRecord::Base end def valid_repo? - repository.exists? + repo rescue errors.add(:path, "Invalid repository path") false end def empty_repo? - !repository.exists? || repository.empty? + !repository || repository.empty? end def ensure_satellite_exists @@ -357,25 +364,18 @@ class Project < ActiveRecord::Base end def repo_exists? - @repo_exists ||= repository.exists? + @repo_exists ||= (repository && repository.branches.present?) rescue @repo_exists = false end def open_branches - all_branches = repository.branches - - if protected_branches.present? - all_branches.reject! do |branch| - protected_branches_names.include?(branch.name) - end - end - - all_branches - end - - def protected_branches_names - @protected_branches_names ||= protected_branches.map(&:name) + if protected_branches.empty? + self.repo.heads + else + pnames = protected_branches.map(&:name) + self.repo.heads.reject { |h| pnames.include?(h.name) } + end.sort_by(&:name) end def root_ref?(branch) @@ -397,6 +397,6 @@ class Project < ActiveRecord::Base # Check if current branch name is marked as protected in the system def protected_branch? branch_name - protected_branches_names.include?(branch_name) + protected_branches.map(&:name).include?(branch_name) end end diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb index 16379720..57229d50 100644 --- a/app/models/protected_branch.rb +++ b/app/models/protected_branch.rb @@ -10,7 +10,7 @@ # class ProtectedBranch < ActiveRecord::Base - include Gitlab::ShellAdapter + include Gitolited attr_accessible :name diff --git a/app/models/repository.rb b/app/models/repository.rb index ed600e29..934c1a6e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1,45 +1,169 @@ class Repository - attr_accessor :raw_repository + include Gitlab::Popen - def initialize(path_with_namespace, default_branch) - @raw_repository = Gitlab::Git::Repository.new(path_with_namespace, default_branch) - rescue Gitlab::Git::Repository::NoRepository - nil + # Repository directory name with namespace direcotry + # Examples: + # gitlab/gitolite + # diaspora + # + attr_accessor :path_with_namespace + + # Grit repo object + attr_accessor :repo + + # Default branch in the repository + attr_accessor :root_ref + + def initialize(path_with_namespace, root_ref = 'master') + @root_ref = root_ref || "master" + @path_with_namespace = path_with_namespace + + # Init grit repo object + repo end - def exists? - raw_repository + def raw + repo end - def empty? - raw_repository.empty? + def path_to_repo + @path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git") end - def commit(id = nil) - commit = raw_repository.commit(id) - commit = Commit.new(commit) if commit - commit + def repo + @repo ||= Grit::Repo.new(path_to_repo) + end + + def commit(commit_id = nil) + Commit.find_or_first(repo, commit_id, root_ref) + end + + def fresh_commits(n = 10) + Commit.fresh_commits(repo, n) + end + + def commits_with_refs(n = 20) + Commit.commits_with_refs(repo, n) + end + + def commits_since(date) + Commit.commits_since(repo, date) end def commits(ref, path = nil, limit = nil, offset = nil) - commits = raw_repository.commits(ref, path, limit, offset) - commits = Commit.decorate(commits) if commits.present? - commits + Commit.commits(repo, ref, path, limit, offset) end - def commits_between(target, source) - commits = raw_repository.commits_between(target, source) - commits = Commit.decorate(commits) if commits.present? - commits + def last_commit_for(ref, path = nil) + commits(ref, path, 1).first end - def method_missing(m, *args, &block) - raw_repository.send(m, *args, &block) + def commits_between(from, to) + Commit.commits_between(repo, from, to) end - def respond_to?(method) - return true if raw_repository.respond_to?(method) + # Returns an Array of branch names + def branch_names + repo.branches.collect(&:name).sort + end - super + # Returns an Array of Branches + def branches + repo.branches.sort_by(&:name) + end + + # Returns an Array of tag names + def tag_names + repo.tags.collect(&:name).sort.reverse + end + + # Returns an Array of Tags + def tags + repo.tags.sort_by(&:name).reverse + end + + # Returns an Array of branch and tag names + def ref_names + [branch_names + tag_names].flatten + end + + def heads + @heads ||= repo.heads + end + + def tree(fcommit, path = nil) + fcommit = commit if fcommit == :head + tree = fcommit.tree + path ? (tree / path) : tree + end + + def has_commits? + !!commit + rescue Grit::NoSuchPathError + false + end + + def empty? + !has_commits? + end + + # Discovers the default branch based on the repository's available branches + # + # - If no branches are present, returns nil + # - If one branch is present, returns its name + # - If two or more branches are present, returns the one that has a name + # matching root_ref (default_branch or 'master' if default_branch is nil) + def discover_default_branch + if branch_names.length == 0 + nil + elsif branch_names.length == 1 + branch_names.first + else + branch_names.select { |v| v == root_ref }.first + end + end + + # Archive Project to .tar.gz + # + # Already packed repo archives stored at + # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz + # + def archive_repo(ref) + ref = ref || self.root_ref + commit = self.commit(ref) + return nil unless commit + + # Build file path + file_name = self.path_with_namespace.gsub("/","_") + "-" + commit.id.to_s + ".tar.gz" + storage_path = Rails.root.join("tmp", "repositories") + file_path = File.join(storage_path, self.path_with_namespace, file_name) + + # Put files into a directory before archiving + prefix = File.basename(self.path_with_namespace) + "/" + + # Create file if not exists + unless File.exists?(file_path) + FileUtils.mkdir_p File.dirname(file_path) + file = self.repo.archive_to_file(ref, prefix, file_path) + end + + file_path + end + + # Return repo size in megabytes + # Cached in redis + def size + Rails.cache.fetch(cache_key(:size)) do + size = popen('du -s', path_to_repo).first.strip.to_i + (size.to_f / 1024).round(2) + end + end + + def expire_cache + Rails.cache.delete(cache_key(:size)) + end + + def cache_key(type) + "#{type}:#{path_with_namespace}" end end diff --git a/app/models/tree.rb b/app/models/tree.rb index 4b6c5b13..96395a42 100644 --- a/app/models/tree.rb +++ b/app/models/tree.rb @@ -26,12 +26,4 @@ class Tree def empty? data.blank? end - - def up_dir? - path.present? - end - - def readme - @readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i } - end end diff --git a/app/models/user.rb b/app/models/user.rb index dcbf5812..c73353bf 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -33,7 +33,6 @@ # can_create_team :boolean default(TRUE), not null # state :string(255) # color_scheme_id :integer default(1), not null -# notification_level :integer default(1), not null # class User < ActiveRecord::Base @@ -47,13 +46,6 @@ class User < ActiveRecord::Base attr_accessor :force_random_password - # Virtual attribute for authenticating by either username or email - attr_accessor :login - - # Add login to attr_accessible - attr_accessible :login - - # # Relations # @@ -109,9 +101,6 @@ class User < ActiveRecord::Base format: { with: Gitlab::Regex.username_regex, message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } - validates :notification_level, - inclusion: { in: Notification.notification_levels }, - presence: true validate :namespace_uniq, if: ->(user) { user.username_changed? } @@ -151,16 +140,6 @@ class User < ActiveRecord::Base # Class methods # class << self - # Devise method overriden to allow sing in with email or username - def find_for_database_authentication(warden_conditions) - conditions = warden_conditions.dup - if login = conditions.delete(:login) - where(conditions).where(["lower(username) = :value OR lower(email) = :value", { value: login.downcase }]).first - else - where(conditions).first - end - end - def filter filter_name case filter_name when "admins"; self.admins @@ -212,10 +191,6 @@ class User < ActiveRecord::Base username end - def notification - @notification ||= Notification.new(self) - end - def generate_password if self.force_random_password self.password = self.password_confirmation = Devise.friendly_token.first(8) diff --git a/app/models/users_project.rb b/app/models/users_project.rb index c32edf38..486aaa69 100644 --- a/app/models/users_project.rb +++ b/app/models/users_project.rb @@ -11,7 +11,7 @@ # class UsersProject < ActiveRecord::Base - include Gitlab::ShellAdapter + include Gitolited GUEST = 10 REPORTER = 20 @@ -38,7 +38,7 @@ class UsersProject < ActiveRecord::Base scope :masters, -> { where(project_access: MASTER) } scope :in_project, ->(project) { where(project_id: project.id) } - scope :in_projects, ->(projects) { where(project_id: projects.map { |p| p.id }) } + scope :in_projects, ->(projects) { where(project_id: project_ids) } scope :with_user, ->(user) { where(user_id: user.id) } class << self diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 497d69e8..adc77b22 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -79,14 +79,14 @@ class WikiPage def version return nil unless persisted? - @version ||= Commit.new(Gitlab::Git::Commit.new(@page.version)) + @version ||= Commit.new(@page.version) end # Returns an array of Gitlab Commit instances. def versions return [] unless persisted? - @page.versions.map { |v| Commit.new(Gitlab::Git::Commit.new(v)) } + @page.versions.map { |v| Commit.new(v) } end # Returns the Date that this latest version was diff --git a/app/observers/activity_observer.rb b/app/observers/activity_observer.rb index ee3e4629..c040c4c5 100644 --- a/app/observers/activity_observer.rb +++ b/app/observers/activity_observer.rb @@ -1,4 +1,4 @@ -class ActivityObserver < BaseObserver +class ActivityObserver < ActiveRecord::Observer observe :issue, :merge_request, :note, :milestone def after_create(record) diff --git a/app/observers/base_observer.rb b/app/observers/base_observer.rb deleted file mode 100644 index 182d3b7b..00000000 --- a/app/observers/base_observer.rb +++ /dev/null @@ -1,9 +0,0 @@ -class BaseObserver < ActiveRecord::Observer - def notification - NotificationService.new - end - - def log_info message - Gitlab::AppLogger.info message - end -end diff --git a/app/observers/issue_observer.rb b/app/observers/issue_observer.rb index 03ce4b95..29e24040 100644 --- a/app/observers/issue_observer.rb +++ b/app/observers/issue_observer.rb @@ -1,30 +1,42 @@ -class IssueObserver < BaseObserver +class IssueObserver < ActiveRecord::Observer cattr_accessor :current_user def after_create(issue) - notification.new_issue(issue, current_user) + if issue.assignee && issue.assignee != current_user + Notify.delay.new_issue_email(issue.id) + end end def after_close(issue, transition) - notification.close_issue(issue, current_user) + send_reassigned_email(issue) if issue.is_being_reassigned? create_note(issue) end def after_reopen(issue, transition) + send_reassigned_email(issue) if issue.is_being_reassigned? + create_note(issue) end def after_update(issue) - if issue.is_being_reassigned? - notification.reassigned_issue(issue, current_user) - end + send_reassigned_email(issue) if issue.is_being_reassigned? end protected - # Create issue note with service comment like 'Status changed to closed' def create_note(issue) Note.create_status_change_note(issue, current_user, issue.state) + [issue.author, issue.assignee].compact.uniq.each do |recipient| + Notify.delay.issue_status_changed_email(recipient.id, issue.id, issue.state, current_user.id) + end + end + + def send_reassigned_email(issue) + recipient_ids = [issue.assignee_id, issue.assignee_id_was].keep_if {|id| id && id != current_user.id } + + recipient_ids.each do |recipient_id| + Notify.delay.reassigned_issue_email(recipient_id, issue.id, issue.assignee_id_was) + end end end diff --git a/app/observers/key_observer.rb b/app/observers/key_observer.rb index 28fef55a..9d02cbc1 100644 --- a/app/observers/key_observer.rb +++ b/app/observers/key_observer.rb @@ -1,4 +1,6 @@ -class KeyObserver < BaseObserver +class KeyObserver < ActiveRecord::Observer + include Gitolited + def after_save(key) GitlabShellWorker.perform_async( :add_key, @@ -6,7 +8,8 @@ class KeyObserver < BaseObserver key.key ) - notification.new_key(key) + # Notify about ssh key being added + Notify.delay.new_ssh_key_email(key.id) if key.user end def after_destroy(key) diff --git a/app/observers/merge_request_observer.rb b/app/observers/merge_request_observer.rb index d0dfad88..d89e7734 100644 --- a/app/observers/merge_request_observer.rb +++ b/app/observers/merge_request_observer.rb @@ -1,25 +1,36 @@ -class MergeRequestObserver < BaseObserver +class MergeRequestObserver < ActiveRecord::Observer cattr_accessor :current_user def after_create(merge_request) - notification.new_merge_request(merge_request, current_user) + if merge_request.assignee && merge_request.assignee != current_user + Notify.delay.new_merge_request_email(merge_request.id) + end end def after_close(merge_request, transition) + send_reassigned_email(merge_request) if merge_request.is_being_reassigned? + Note.create_status_change_note(merge_request, current_user, merge_request.state) - - notification.close_mr(merge_request, current_user) - end - - def after_merge(merge_request, transition) - notification.merge_mr(merge_request) end def after_reopen(merge_request, transition) + send_reassigned_email(merge_request) if merge_request.is_being_reassigned? + Note.create_status_change_note(merge_request, current_user, merge_request.state) end def after_update(merge_request) - notification.reassigned_merge_request(merge_request, current_user) if merge_request.is_being_reassigned? + send_reassigned_email(merge_request) if merge_request.is_being_reassigned? + end + + protected + + def send_reassigned_email(merge_request) + recipients_ids = merge_request.assignee_id_was, merge_request.assignee_id + recipients_ids.delete current_user.id + + recipients_ids.each do |recipient_id| + Notify.delay.reassigned_merge_request_email(recipient_id, merge_request.id, merge_request.assignee_id_was) + end end end diff --git a/app/observers/note_observer.rb b/app/observers/note_observer.rb index 7b79161c..0f820a26 100644 --- a/app/observers/note_observer.rb +++ b/app/observers/note_observer.rb @@ -1,5 +1,38 @@ -class NoteObserver < BaseObserver +class NoteObserver < ActiveRecord::Observer def after_create(note) - notification.new_note(note) + send_notify_mails(note) + end + + protected + + def send_notify_mails(note) + if note.notify + notify_team(note) + elsif note.notify_author + # Notify only author of resource + if note.commit_author + Notify.delay.note_commit_email(note.commit_author.id, note.id) + end + else + # Otherwise ignore it + nil + end + end + + # Notifies the whole team except the author of note + def notify_team(note) + # Note: wall posts are not "attached" to anything, so fall back to "Wall" + noteable_type = note.noteable_type.presence || "Wall" + notify_method = "note_#{noteable_type.underscore}_email".to_sym + + if Notify.respond_to? notify_method + team_without_note_author(note).map do |u| + Notify.delay.send(notify_method, u.id, note.id) + end + end + end + + def team_without_note_author(note) + note.project.users.reject { |u| u.id == note.author.id } end end diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb index 7d7ecdd3..89dc97ac 100644 --- a/app/observers/project_observer.rb +++ b/app/observers/project_observer.rb @@ -1,4 +1,4 @@ -class ProjectObserver < BaseObserver +class ProjectObserver < ActiveRecord::Observer def after_create(project) GitlabShellWorker.perform_async( :add_repository, @@ -27,4 +27,10 @@ class ProjectObserver < BaseObserver log_info("Project \"#{project.name}\" was removed") end + + protected + + def log_info message + Gitlab::AppLogger.info message + end end diff --git a/app/observers/system_hook_observer.rb b/app/observers/system_hook_observer.rb index 3a649fd5..be2594b4 100644 --- a/app/observers/system_hook_observer.rb +++ b/app/observers/system_hook_observer.rb @@ -1,4 +1,4 @@ -class SystemHookObserver < BaseObserver +class SystemHookObserver < ActiveRecord::Observer observe :user, :project, :users_project def after_create(model) diff --git a/app/observers/user_observer.rb b/app/observers/user_observer.rb index 6bb3c471..6c461e07 100644 --- a/app/observers/user_observer.rb +++ b/app/observers/user_observer.rb @@ -1,8 +1,9 @@ -class UserObserver < BaseObserver +class UserObserver < ActiveRecord::Observer def after_create(user) log_info("User \"#{user.name}\" (#{user.email}) was created") - notification.new_user(user) + # Dont email omniauth created users + Notify.delay.new_user_email(user.id, user.password) unless user.extern_uid? end def after_destroy user @@ -18,4 +19,10 @@ class UserObserver < BaseObserver end end end + + protected + + def log_info message + Gitlab::AppLogger.info message + end end diff --git a/app/observers/users_project_observer.rb b/app/observers/users_project_observer.rb index ca9649c7..66b42175 100644 --- a/app/observers/users_project_observer.rb +++ b/app/observers/users_project_observer.rb @@ -1,6 +1,7 @@ -class UsersProjectObserver < BaseObserver +class UsersProjectObserver < ActiveRecord::Observer def after_commit(users_project) return if users_project.destroyed? + Notify.delay.project_access_granted_email(users_project.id) end def after_create(users_project) @@ -9,12 +10,6 @@ class UsersProjectObserver < BaseObserver action: Event::JOINED, author_id: users_project.user.id ) - - notification.new_team_member(users_project) - end - - def after_update(users_project) - notification.update_team_member(users_project) end def after_destroy(users_project) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb deleted file mode 100644 index f8779fd5..00000000 --- a/app/services/notification_service.rb +++ /dev/null @@ -1,197 +0,0 @@ -# NotificationService class -# -# Used for notifing users with emails about different events -# -# Ex. -# NotificationService.new.new_issue(issue, current_user) -# -class NotificationService - # Always notify user about ssh key added - # only if ssh key is not deploy key - # - # This is security email so it will be sent - # even if user disabled notifications - def new_key(key) - if key.user - Notify.delay.new_ssh_key_email(key.id) - end - end - - # When create an issue we should send next emails: - # - # * issue assignee if his notification level is not Disabled - # * project team members with notification level higher then Participating - # - def new_issue(issue, current_user) - new_resource_email(issue, 'new_issue_email') - end - - # When we close an issue we should send next emails: - # - # * issue author if his notification level is not Disabled - # * issue assignee if his notification level is not Disabled - # * project team members with notification level higher then Participating - # - def close_issue(issue, current_user) - close_resource_email(issue, current_user, 'closed_issue_email') - end - - # When we reassign an issue we should send next emails: - # - # * issue old assignee if his notification level is not Disabled - # * issue new assignee if his notification level is not Disabled - # - def reassigned_issue(issue, current_user) - reassign_resource_email(issue, current_user, 'reassigned_issue_email') - end - - - # When create a merge request we should send next emails: - # - # * mr assignee if his notification level is not Disabled - # - def new_merge_request(merge_request, current_user) - new_resource_email(merge_request, 'new_merge_request_email') - end - - # When we reassign a merge_request we should send next emails: - # - # * merge_request old assignee if his notification level is not Disabled - # * merge_request assignee if his notification level is not Disabled - # - def reassigned_merge_request(merge_request, current_user) - reassign_resource_email(merge_request, current_user, 'reassigned_merge_request_email') - end - - # When we close a merge request we should send next emails: - # - # * merge_request author if his notification level is not Disabled - # * merge_request assignee if his notification level is not Disabled - # * project team members with notification level higher then Participating - # - def close_mr(merge_request, current_user) - close_resource_email(merge_request, current_user, 'closed_merge_request_email') - end - - # When we merge a merge request we should send next emails: - # - # * merge_request author if his notification level is not Disabled - # * merge_request assignee if his notification level is not Disabled - # * project team members with notification level higher then Participating - # - def merge_mr(merge_request) - recipients = reject_muted_users([merge_request.author, merge_request.assignee]) - recipients = recipients.concat(project_watchers(merge_request.project)).uniq - - recipients.each do |recipient| - Notify.delay.merged_merge_request_email(recipient.id, merge_request.id) - end - end - - # Notify new user with email after creation - def new_user(user) - # Dont email omniauth created users - Notify.delay.new_user_email(user.id, user.password) unless user.extern_uid? - end - - # Notify users on new note in system - # - # TODO: split on methods and refactor - # - def new_note(note) - # ignore wall messages - return true unless note.noteable_type.present? - - opts = { noteable_type: note.noteable_type, project_id: note.project_id } - - if note.commit_id.present? - opts.merge!(commit_id: note.commit_id) - recipients = [note.commit_author] - else - opts.merge!(noteable_id: note.noteable_id) - target = note.noteable - recipients = [] - recipients << target.assignee if target.respond_to?(:assignee) - recipients << target.author if target.respond_to?(:author) - end - - # Get users who left comment in thread - recipients = recipients.concat(User.where(id: Note.where(opts).pluck(:author_id))) - - # Merge project watchers - recipients = recipients.concat(project_watchers(note.project)).compact.uniq - - # Reject mutes users - recipients = reject_muted_users(recipients) - - # Reject author - recipients.delete(note.author) - - # build notify method like 'note_commit_email' - notify_method = "note_#{note.noteable_type.underscore}_email".to_sym - - recipients.each do |recipient| - Notify.delay.send(notify_method, recipient.id, note.id) - end - end - - def new_team_member(users_project) - Notify.delay.project_access_granted_email(users_project.id) - end - - def update_team_member(users_project) - Notify.delay.project_access_granted_email(users_project.id) - end - - protected - - # Get project users with WATCH notification level - def project_watchers(project) - project.users.where(notification_level: Notification::N_WATCH) - end - - # Remove users with disabled notifications from array - # Also remove duplications and nil recipients - def reject_muted_users(users) - users.compact.uniq.reject do |user| - user.notification.disabled? - end - end - - def new_resource_email(target, method) - recipients = reject_muted_users([target.assignee]) - recipients = recipients.concat(project_watchers(target.project)).uniq - recipients.delete(target.author) - - recipients.each do |recipient| - Notify.delay.send(method, recipient.id, target.id) - end - end - - def close_resource_email(target, current_user, method) - recipients = reject_muted_users([target.author, target.assignee]) - recipients = recipients.concat(project_watchers(target.project)).uniq - recipients.delete(current_user) - - recipients.each do |recipient| - Notify.delay.send(method, recipient.id, target.id, current_user.id) - end - end - - def reassign_resource_email(target, current_user, method) - recipients = User.where(id: [target.assignee_id, target.assignee_id_was]) - - # Add watchers to email list - recipients = recipients.concat(project_watchers(target.project)) - - # reject users with disabled notifications - recipients = reject_muted_users(recipients) - - # Reject me from recipients if I reassign an item - recipients.delete(current_user) - - recipients.each do |recipient| - Notify.delay.send(method, recipient.id, target.id, target.assignee_id_was) - end - end -end diff --git a/app/services/project_transfer_service.rb b/app/services/project_transfer_service.rb index 3b8c4847..2ff1aa91 100644 --- a/app/services/project_transfer_service.rb +++ b/app/services/project_transfer_service.rb @@ -3,9 +3,7 @@ # Used for transfer project to another namespace # class ProjectTransferService - include Gitlab::ShellAdapter - - class TransferError < StandardError; end + include Gitolited attr_accessor :project @@ -21,16 +19,14 @@ class ProjectTransferService project.namespace = new_namespace project.save! - # Move main repository unless gitlab_shell.mv_repository(old_path, new_path) raise TransferError.new('Cannot move project') end - # Move wiki repo also if present - gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki") - true end + rescue => ex + raise Project::TransferError.new(ex.message) end end diff --git a/app/views/admin/projects/_form.html.haml b/app/views/admin/projects/_form.html.haml new file mode 100644 index 00000000..29b90bdd --- /dev/null +++ b/app/views/admin/projects/_form.html.haml @@ -0,0 +1,86 @@ += form_for [:admin, project] do |f| + -if project.errors.any? + .alert.alert-error + %ul + - project.errors.full_messages.each do |msg| + %li= msg + + .clearfix.project_name_holder + = f.label :name do + Project name is + .input + = f.text_field :name, placeholder: "Example Project", class: "xxlarge" + + - if project.repo_exists? + %fieldset.adv_settings + %legend Advanced settings: + .clearfix + = f.label :path do + Path + .input + = text_field_tag :ppath, @project.repository.path_to_repo, class: "xlarge", disabled: true + + .clearfix + = f.label :default_branch, "Default Branch" + .input= f.select(:default_branch, @project.repository.heads.map(&:name), {}, style: "width:210px;") + + %fieldset.adv_settings + %legend Features: + + .clearfix + = f.label :issues_enabled, "Issues" + .input= f.check_box :issues_enabled + + - if Project.issues_tracker.values.count > 1 + .clearfix + = f.label :issues_tracker, "Issues tracker", class: 'control-label' + .input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled }) + + .clearfix + = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label' + .input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id? + + .clearfix + = f.label :merge_requests_enabled, "Merge Requests" + .input= f.check_box :merge_requests_enabled + + .clearfix + = f.label :wall_enabled, "Wall" + .input= f.check_box :wall_enabled + + .clearfix + = f.label :wiki_enabled, "Wiki" + .input= f.check_box :wiki_enabled + + %fieldset.features + %legend Public mode: + .clearfix + = f.label :public do + %span Allow public http clone + .input= f.check_box :public + + %fieldset.features + %legend Transfer: + .control-group + = f.label :namespace_id do + %span Namespace + .controls + = f.select :namespace_id, namespaces_options(@project.namespace_id, :all), {}, {class: 'chosen'} + %br + %ul.prepend-top-10.cred + %li Be careful. Changing project namespace can have unintended side effects + %li You can transfer project only to namespaces you can manage + %li You will need to update your local repositories to point to the new location. + + + .actions + = f.submit 'Save Project', class: "btn btn-save" + = link_to 'Cancel', admin_projects_path, class: "btn btn-cancel" + + + +:javascript + $(function(){ + new Projects(); + }) + diff --git a/app/views/admin/projects/edit.html.haml b/app/views/admin/projects/edit.html.haml new file mode 100644 index 00000000..7b59a0cc --- /dev/null +++ b/app/views/admin/projects/edit.html.haml @@ -0,0 +1,3 @@ +%h3.page_title #{@project.name} → Edit project +%hr += render 'form', project: @project diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 82d5fdcd..15b27782 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -52,8 +52,8 @@ %i.icon-lock.cgreen = link_to project.name_with_namespace, [:admin, project] .pull-right - = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" - = link_to 'Destroy', [project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" + = link_to 'Edit', edit_admin_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" + = link_to 'Destroy', [:admin, project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" - if @projects.blank? %p.nothing_here_message 0 projects matches - else diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 92b89601..65b92117 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -1,90 +1,129 @@ %h3.page_title Project: #{@project.name_with_namespace} - = link_to edit_project_path(@project), class: "btn pull-right" do + = link_to edit_admin_project_path(@project), class: "btn pull-right" do %i.icon-edit Edit -%hr -.row - .span6 - .ui-box - %h5.title - Project info: - %ul.well-list - %li - %span.light Name: - %strong= @project.name - %li - %span.light Namespace: - %strong - - if @project.namespace - = link_to @project.namespace.human_name, [:admin, @project.group || @project.owner] - - else - Global - %li - %span.light Owned by: - %strong - - if @project.owner - = link_to @project.owner_name, admin_user_path(@project.owner) - - else - (deleted) - %li - %span.light Created by: - %strong - = @project.creator.try(:name) || '(deleted)' - %li - %span.light Created at: - %strong - = @project.created_at.stamp("March 1, 1999") +%br +%table.zebra-striped + %thead + %tr + %th Project + %th + %tr + %td + %b + Name: + %td + = @project.name + %tr + %td + %b + Namespace: + %td + - if @project.namespace + = @project.namespace.human_name + - else + Global + %tr + %td + %b + Owned by: + %td + - if @project.owner + = link_to @project.owner_name, admin_user_path(@project.owner) + - else + (deleted) + %tr + %td + %b + Created by: + %td + = @project.creator.try(:name) || '(deleted)' + %tr + %td + %b + Created at: + %td + = @project.created_at.stamp("March 1, 1999") + %tr + %td + %b + Smart HTTP: + %td + = link_to @project.http_url_to_repo + %tr + %td + %b + SSH: + %td + = link_to @project.ssh_url_to_repo + - if @project.public + %tr.bgred + %td + %b + Public Read-Only Code access: + %td + = check_box_tag 'public', nil, @project.public - %li - %span.light http: - %strong - = link_to @project.http_url_to_repo - %li - %span.light ssh: - %strong - = link_to @project.ssh_url_to_repo - - if @project.repository.exists? - %li - %span.light fs: - %strong - = @repository.path_to_repo +- if @repository + %table.zebra-striped + %thead + %tr + %th Repository + %th + %tr + %td + %b + FS Path: + %td + %code= @repository.path_to_repo + %tr + %td + %b + Last commit at: + %td + = last_commit(@project) - %li - %span.light last commit: - %strong - = last_commit(@project) - - else - %li - %span.light repository: - %strong.cred - does not exist +%br +%h5 + Team + %small + (#{@project.users.count}) +%br +%table.zebra-striped.team_members + %thead + %tr + %th Name + %th Project Access + %th Repository Access + %th - %li - %span.light access: - %strong - - if @project.public - %span.cblue - %i.icon-share - Public - - else - %span.cgreen - %i.icon-lock - Private - .span6 - .ui-box - %h5.title - Team - %small - (#{@project.users.count}) - = link_to project_team_index_path(@project), class: "btn btn-tiny" do - %i.icon-edit - Edit Team - %ul.well-list.team_members - - @project.users.each do |tm| - %li - %strong - = link_to tm.name, admin_user_path(tm) - %span.pull-right.light= @project.project_access_human(tm) + - @project.users.each do |tm| + %tr + %td + = link_to tm.name, admin_user_path(tm) + %td= @project.project_access_human(tm) + %td= link_to 'Edit Access', edit_admin_project_member_path(@project, tm), class: "btn btn-small" + %td= link_to 'Remove from team', admin_project_member_path(@project, tm), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove small" + +%br +%h5 Add new team member +%br += form_tag team_update_admin_project_path(@project), class: "bulk_import", method: :put do + %table.zebra-striped + %thead + %tr + %th Users + %th Project Access: + + %tr + %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5' + %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"} + + %tr + %td= submit_tag 'Add', class: "btn btn-primary" + %td + Read more about project permissions + %strong= link_to "here", help_permissions_path, class: "vlink" diff --git a/app/views/admin/projects/team.html.haml b/app/views/admin/projects/team.html.haml new file mode 100644 index 00000000..e69de29b diff --git a/app/views/blame/show.html.haml b/app/views/blame/show.html.haml index 96d153e6..b2a45ef5 100644 --- a/app/views/blame/show.html.haml +++ b/app/views/blame/show.html.haml @@ -6,7 +6,7 @@ %i.icon-angle-right = link_to project_tree_path(@project, @ref) do = @project.name - - tree_breadcrumbs(@tree, 6) do |link| + - @tree.breadcrumbs(6) do |link| \/ %li= link .clear @@ -22,13 +22,13 @@ %table - current_line = 1 - @blame.each do |commit, lines| - - commit = Commit.new(commit) + - commit = CommitDecorator.decorate(Commit.new(commit)) %tr %td.blame-commit %span.commit = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"   - = commit_author_link(commit, avatar: true, size: 16) + = commit.author_link avatar: true, size: 16   = link_to_gfm truncate(commit.title, length: 20), project_commit_path(@project, commit.id), class: "row_title" %td.lines.blame-numbers diff --git a/app/views/commit/show.html.haml b/app/views/commit/show.html.haml index 48fb44a9..485f2d1e 100644 --- a/app/views/commit/show.html.haml +++ b/app/views/commit/show.html.haml @@ -1,11 +1,10 @@ -= render "commit_box" += render "commits/commit_box" -- unless @commit.has_zero_stats? - %p.pull-right.cgray - This commit has - %span.cgreen #{@commit.stats.additions} additions - and - %span.cred #{@commit.stats.deletions} deletions +%p.pull-right.cgray + This commit has + %span.cgreen #{@commit.stats.additions} additions + and + %span.cred #{@commit.stats.deletions} deletions = render "commits/diffs", diffs: @commit.diffs = render "notes/notes_with_form" diff --git a/app/views/commits/_commit.html.haml b/app/views/commits/_commit.html.haml index 65d92030..2f5ff130 100644 --- a/app/views/commits/_commit.html.haml +++ b/app/views/commits/_commit.html.haml @@ -4,7 +4,7 @@ %strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right" %p = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id" - = commit_author_link(commit, avatar: true, size: 24) + = commit.author_link avatar: true, size: 24   = link_to_gfm truncate(commit.title, length: 70), project_commit_path(@project, commit.id), class: "row_title" diff --git a/app/views/commit/_commit_box.html.haml b/app/views/commits/_commit_box.html.haml similarity index 93% rename from app/views/commit/_commit_box.html.haml rename to app/views/commits/_commit_box.html.haml index 64679177..4c80c13c 100644 --- a/app/views/commit/_commit_box.html.haml +++ b/app/views/commits/_commit_box.html.haml @@ -24,14 +24,14 @@ .row .span5 .author - = commit_author_link(@commit, avatar: true, size: 32) + = @commit.author_link avatar: true, size: 32 authored %time{title: @commit.authored_date.stamp("Aug 21, 2011 9:23pm")} #{time_ago_in_words(@commit.authored_date)} ago - if @commit.different_committer? .committer → - = commit_committer_link(@commit) + = @commit.committer_link committed %time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")} #{time_ago_in_words(@commit.committed_date)} ago diff --git a/app/views/commits/show.html.haml b/app/views/commits/show.html.haml index 586b21df..d180b8ec 100644 --- a/app/views/commits/show.html.haml +++ b/app/views/commits/show.html.haml @@ -2,7 +2,7 @@ - if @path.present? %ul.breadcrumb - = commits_breadcrumbs + = breadcrumbs %div{id: dom_id(@project)} #commits-list= render "commits" diff --git a/app/views/compare/show.html.haml b/app/views/compare/show.html.haml index 56c4a113..476be255 100644 --- a/app/views/compare/show.html.haml +++ b/app/views/compare/show.html.haml @@ -16,7 +16,7 @@ %div.ui-box %h5.title Commits (#{@commits.count}) - %ul.well-list= render Commit.decorate(@commits) + %ul.well-list= render @commits - unless @diffs.empty? %h4 Diff diff --git a/app/views/dashboard/_groups.html.haml b/app/views/dashboard/_groups.html.haml index 2fedf87a..3124d76a 100644 --- a/app/views/dashboard/_groups.html.haml +++ b/app/views/dashboard/_groups.html.haml @@ -1,11 +1,11 @@ .ui-box %h5.title Groups - %span.light + %small (#{groups.count}) - if current_user.can_create_group? %span.pull-right - = link_to new_group_path, class: "btn btn-small" do + = link_to new_group_path, class: "btn btn-tiny info" do %i.icon-plus New Group %ul.well-list diff --git a/app/views/dashboard/_projects.html.haml b/app/views/dashboard/_projects.html.haml index a106e83e..105e23fe 100644 --- a/app/views/dashboard/_projects.html.haml +++ b/app/views/dashboard/_projects.html.haml @@ -1,11 +1,11 @@ .ui-box %h5.title Projects - %span.light + %small (#{@projects_count}) - if current_user.can_create_project? %span.pull-right - = link_to new_project_path, class: "btn btn-small" do + = link_to new_project_path, class: "btn btn-tiny info" do %i.icon-plus New Project diff --git a/app/views/dashboard/_teams.html.haml b/app/views/dashboard/_teams.html.haml index 95d87f50..5c28f964 100644 --- a/app/views/dashboard/_teams.html.haml +++ b/app/views/dashboard/_teams.html.haml @@ -1,10 +1,10 @@ .ui-box.teams-box %h5.title Teams - %span.light + %small (#{teams.count}) %span.pull-right - = link_to new_team_path, class: "btn btn-small" do + = link_to new_team_path, class: "btn btn-tiny info" do %i.icon-plus New Team %ul.well-list diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index 5e93ab18..d904e701 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -1,19 +1,19 @@ - if ldap_enable? - = render partial: 'devise/sessions/new_ldap' + = render :partial => 'devise/sessions/new_ldap' - else - = form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: "login-box" }) do |f| - = image_tag "login-logo.png", width: "304", height: "66", class: "login-logo", alt: "Login Logo" - = f.text_field :login, class: "text top", placeholder: "Username or Email", autofocus: "autofocus" - = f.password_field :password, class: "text bottom", placeholder: "Password" + = form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f| + = image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo" + = f.email_field :email, :class => "text top", :placeholder => "Email", :autofocus => "autofocus" + = f.password_field :password, :class => "text bottom", :placeholder => "Password" - if devise_mapping.rememberable? .clearfix.inputs-list - %label.checkbox.remember_me{for: "user_remember_me"} + %label.checkbox.remember_me{:for => "user_remember_me"} = f.check_box :remember_me %span Remember me %br/ - = f.submit "Sign in", class: "btn-create btn" + = f.submit "Sign in", :class => "btn-create btn" .pull-right - = link_to "Forgot your password?", new_password_path(resource_name), class: "btn" + = link_to "Forgot your password?", new_password_path(resource_name), :class => "btn" %br/ - if Gitlab.config.gitlab.signup_enabled %hr/ diff --git a/app/views/events/_commit.html.haml b/app/views/events/_commit.html.haml index f2f2d47e..ea417aa9 100644 --- a/app/views/events/_commit.html.haml +++ b/app/views/events/_commit.html.haml @@ -1,3 +1,4 @@ +- commit = CommitDecorator.decorate(commit) %li.commit %p = link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id" diff --git a/app/views/graph/show.json.erb b/app/views/graph/show.json.erb index 529d5849..d0a0709a 100644 --- a/app/views/graph/show.json.erb +++ b/app/views/graph/show.json.erb @@ -13,7 +13,7 @@ }, time: c.time, space: c.spaces.first, - refs: get_refs(c), + refs: join_with_space(c.refs), id: c.sha, date: c.date, message: c.message, diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index abe3f2ea..00a08e61 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -4,8 +4,24 @@ %body{class: "#{app_theme} admin"} = render "layouts/head_panel", title: "Admin area" = render "layouts/flash" - %nav.main-nav - .container= render 'layouts/nav/admin' - .container + %ul.main_menu + = nav_link(controller: :dashboard, html_options: {class: 'home'}) do + = link_to admin_root_path, title: "Stats" do + %i.icon-home + = nav_link(controller: :projects) do + = link_to "Projects", admin_projects_path + = nav_link(controller: :teams) do + = link_to "Teams", admin_teams_path + = nav_link(controller: :groups) do + = link_to "Groups", admin_groups_path + = nav_link(controller: :users) do + = link_to "Users", admin_users_path + = nav_link(controller: :logs) do + = link_to "Logs", admin_logs_path + = nav_link(controller: :hooks) do + = link_to "Hooks", admin_hooks_path + = nav_link(controller: :resque) do + = link_to "Background Jobs", admin_resque_path + .content= yield diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 4e683140..90c26534 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -4,8 +4,25 @@ %body{class: "#{app_theme} application"} = render "layouts/head_panel", title: "Dashboard" = render "layouts/flash" - %nav.main-nav - .container= render 'layouts/nav/dashboard' - .container + %ul.main_menu + = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do + = link_to root_path, title: "Home" do + %i.icon-home + = nav_link(path: 'dashboard#projects') do + = link_to projects_dashboard_path do + Projects + = nav_link(path: 'dashboard#issues') do + = link_to issues_dashboard_path do + Issues + %span.count= current_user.assigned_issues.opened.count + = nav_link(path: 'dashboard#merge_requests') do + = link_to merge_requests_dashboard_path do + Merge Requests + %span.count= current_user.cared_merge_requests.opened.count + = nav_link(path: 'search#show') do + = link_to "Search", search_path + = nav_link(controller: :help) do + = link_to "Help", help_path + .content= yield diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml index 8296b8ae..45528281 100644 --- a/app/views/layouts/group.html.haml +++ b/app/views/layouts/group.html.haml @@ -4,8 +4,25 @@ %body{class: "#{app_theme} application"} = render "layouts/head_panel", title: "group: #{@group.name}" = render "layouts/flash" - %nav.main-nav - .container= render 'layouts/nav/group' - .container + %ul.main_menu + = nav_link(path: 'groups#show', html_options: {class: 'home'}) do + = link_to group_path(@group), title: "Home" do + %i.icon-home + = nav_link(path: 'groups#issues') do + = link_to issues_group_path(@group) do + Issues + %span.count= current_user.assigned_issues.opened.of_group(@group).count + = nav_link(path: 'groups#merge_requests') do + = link_to merge_requests_group_path(@group) do + Merge Requests + %span.count= current_user.cared_merge_requests.opened.of_group(@group).count + = nav_link(path: 'groups#people') do + = link_to "People", people_group_path(@group) + + - if can?(current_user, :manage_group, @group) + = nav_link(path: 'groups#edit') do + = link_to edit_group_path(@group), class: "tab " do + Settings + .content= yield diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml deleted file mode 100644 index ca77c26e..00000000 --- a/app/views/layouts/nav/_admin.html.haml +++ /dev/null @@ -1,19 +0,0 @@ -%ul - = nav_link(controller: :dashboard, html_options: {class: 'home'}) do - = link_to admin_root_path, title: "Stats" do - %i.icon-home - = nav_link(controller: :projects) do - = link_to "Projects", admin_projects_path - = nav_link(controller: :teams) do - = link_to "Teams", admin_teams_path - = nav_link(controller: :groups) do - = link_to "Groups", admin_groups_path - = nav_link(controller: :users) do - = link_to "Users", admin_users_path - = nav_link(controller: :logs) do - = link_to "Logs", admin_logs_path - = nav_link(controller: :hooks) do - = link_to "Hooks", admin_hooks_path - = nav_link(controller: :resque) do - = link_to "Background Jobs", admin_resque_path - diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml deleted file mode 100644 index 2ac35050..00000000 --- a/app/views/layouts/nav/_dashboard.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -%ul - = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do - = link_to root_path, title: "Home" do - %i.icon-home - = nav_link(path: 'dashboard#projects') do - = link_to projects_dashboard_path do - Projects - = nav_link(path: 'dashboard#issues') do - = link_to issues_dashboard_path do - Issues - %span.count= current_user.assigned_issues.opened.count - = nav_link(path: 'dashboard#merge_requests') do - = link_to merge_requests_dashboard_path do - Merge Requests - %span.count= current_user.cared_merge_requests.opened.count - = nav_link(path: 'search#show') do - = link_to "Search", search_path - = nav_link(controller: :help) do - = link_to "Help", help_path - diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml deleted file mode 100644 index f3cdb5ac..00000000 --- a/app/views/layouts/nav/_group.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -%ul - = nav_link(path: 'groups#show', html_options: {class: 'home'}) do - = link_to group_path(@group), title: "Home" do - %i.icon-home - = nav_link(path: 'groups#issues') do - = link_to issues_group_path(@group) do - Issues - %span.count= current_user.assigned_issues.opened.of_group(@group).count - = nav_link(path: 'groups#merge_requests') do - = link_to merge_requests_group_path(@group) do - Merge Requests - %span.count= current_user.cared_merge_requests.opened.of_group(@group).count - = nav_link(path: 'groups#people') do - = link_to "People", people_group_path(@group) - - - if can?(current_user, :manage_group, @group) - = nav_link(path: 'groups#edit') do - = link_to edit_group_path(@group), class: "tab " do - Settings - diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml deleted file mode 100644 index e5e4b27c..00000000 --- a/app/views/layouts/nav/_profile.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -%ul - = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do - = link_to profile_path, title: "Profile" do - %i.icon-home - = nav_link(path: 'profiles#account') do - = link_to "Account", account_profile_path - = nav_link(controller: :notifications) do - = link_to "Notifications", profile_notifications_path - = nav_link(controller: :keys) do - = link_to keys_path do - SSH Keys - %span.count= current_user.keys.count - = nav_link(path: 'profiles#design') do - = link_to "Design", design_profile_path - = nav_link(path: 'profiles#history') do - = link_to "History", history_profile_path - diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml deleted file mode 100644 index ec3da964..00000000 --- a/app/views/layouts/nav/_project.html.haml +++ /dev/null @@ -1,43 +0,0 @@ -%ul - = nav_link(path: 'projects#show', html_options: {class: "home"}) do - = link_to project_path(@project), title: "Project" do - %i.icon-home - - - unless @project.empty_repo? - - if can? current_user, :download_code, @project - = nav_link(controller: %w(tree blob blame)) do - = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref) - = nav_link(controller: %w(commit commits compare repositories protected_branches)) do - = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref) - = nav_link(controller: %w(graph)) do - = link_to "Network", project_graph_path(@project, @ref || @repository.root_ref) - - - if @project.issues_enabled - = nav_link(controller: %w(issues milestones labels)) do - = link_to url_for_project_issues do - Issues - - if @project.used_default_issues_tracker? - %span.count.issue_counter= @project.issues.opened.count - - - if @project.repo_exists? && @project.merge_requests_enabled - = nav_link(controller: :merge_requests) do - = link_to project_merge_requests_path(@project) do - Merge Requests - %span.count.merge_counter= @project.merge_requests.opened.count - - - if @project.wiki_enabled - = nav_link(controller: :wikis) do - = link_to 'Wiki', project_wiki_path(@project, :home) - - - if @project.wall_enabled - = nav_link(controller: :walls) do - = link_to 'Wall', project_wall_path(@project) - - - if @project.snippets_enabled - = nav_link(controller: :snippets) do - = link_to 'Snippets', project_snippets_path(@project) - - - if can? current_user, :admin_project, @project - = nav_link(html_options: {class: "#{project_tab_class}"}) do - = link_to edit_project_path(@project), class: "stat-tab tab " do - Settings diff --git a/app/views/layouts/nav/_team.html.haml b/app/views/layouts/nav/_team.html.haml deleted file mode 100644 index 415e4510..00000000 --- a/app/views/layouts/nav/_team.html.haml +++ /dev/null @@ -1,25 +0,0 @@ -%ul - = nav_link(path: 'teams#show', html_options: {class: 'home'}) do - = link_to team_path(@team), title: "Home" do - %i.icon-home - - = nav_link(path: 'teams#issues') do - = link_to issues_team_path(@team) do - Issues - %span.count= Issue.opened.of_user_team(@team).count - - = nav_link(path: 'teams#merge_requests') do - = link_to merge_requests_team_path(@team) do - Merge Requests - %span.count= MergeRequest.opened.of_user_team(@team).count - - = nav_link(controller: [:members]) do - = link_to team_members_path(@team), class: "team-tab tab" do - Members - %span.count= @team.members.count - - - if can? current_user, :admin_user_team, @team - = nav_link(path: 'teams#edit') do - = link_to edit_team_path(@team), class: "stat-tab tab " do - Settings - diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml index 535f94c4..611063e8 100644 --- a/app/views/layouts/profile.html.haml +++ b/app/views/layouts/profile.html.haml @@ -4,8 +4,20 @@ %body{class: "#{app_theme} profile"} = render "layouts/head_panel", title: "Profile" = render "layouts/flash" - %nav.main-nav - .container= render 'layouts/nav/profile' - .container + %ul.main_menu + = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do + = link_to profile_path, title: "Profile" do + %i.icon-home + = nav_link(path: 'profiles#account') do + = link_to "Account", account_profile_path + = nav_link(controller: :keys) do + = link_to keys_path do + SSH Keys + %span.count= current_user.keys.count + = nav_link(path: 'profiles#design') do + = link_to "Design", design_profile_path + = nav_link(path: 'profiles#history') do + = link_to "History", history_profile_path + .content= yield diff --git a/app/views/layouts/project_resource.html.haml b/app/views/layouts/project_resource.html.haml index 7b0d4789..ca2f6e9a 100644 --- a/app/views/layouts/project_resource.html.haml +++ b/app/views/layouts/project_resource.html.haml @@ -7,8 +7,49 @@ - if can?(current_user, :download_code, @project) = render 'shared/no_ssh' - %nav.main-nav - .container= render 'layouts/nav/project' - .container + %ul.main_menu + = nav_link(path: 'projects#show', html_options: {class: "home"}) do + = link_to project_path(@project), title: "Project" do + %i.icon-home + + - if @project.repo_exists? + - if can? current_user, :download_code, @project + = nav_link(controller: %w(tree blob blame)) do + = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref) + = nav_link(controller: %w(commit commits compare repositories protected_branches)) do + = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref) + = nav_link(controller: %w(graph)) do + = link_to "Network", project_graph_path(@project, @ref || @repository.root_ref) + + - if @project.issues_enabled + = nav_link(controller: %w(issues milestones labels)) do + = link_to url_for_project_issues do + Issues + - if @project.used_default_issues_tracker? + %span.count.issue_counter= @project.issues.opened.count + + - if @project.repo_exists? && @project.merge_requests_enabled + = nav_link(controller: :merge_requests) do + = link_to project_merge_requests_path(@project) do + Merge Requests + %span.count.merge_counter= @project.merge_requests.opened.count + + - if @project.wiki_enabled + = nav_link(controller: :wikis) do + = link_to 'Wiki', project_wiki_path(@project, :home) + + - if @project.wall_enabled + = nav_link(controller: :walls) do + = link_to 'Wall', project_wall_path(@project) + + - if @project.snippets_enabled + = nav_link(controller: :snippets) do + = link_to 'Snippets', project_snippets_path(@project) + + - if can? current_user, :admin_project, @project + = nav_link(html_options: {class: "#{project_tab_class}"}) do + = link_to edit_project_path(@project), class: "stat-tab tab " do + Settings + .content= yield diff --git a/app/views/layouts/user_team.html.haml b/app/views/layouts/user_team.html.haml index f2ead9d2..483bfad6 100644 --- a/app/views/layouts/user_team.html.haml +++ b/app/views/layouts/user_team.html.haml @@ -4,8 +4,30 @@ %body{class: "#{app_theme} application"} = render "layouts/head_panel", title: "team: #{@team.name}" = render "layouts/flash" - %nav.main-nav - .container= render 'layouts/nav/team' - .container + %ul.main_menu + = nav_link(path: 'teams#show', html_options: {class: 'home'}) do + = link_to team_path(@team), title: "Home" do + %i.icon-home + + = nav_link(path: 'teams#issues') do + = link_to issues_team_path(@team) do + Issues + %span.count= Issue.opened.of_user_team(@team).count + + = nav_link(path: 'teams#merge_requests') do + = link_to merge_requests_team_path(@team) do + Merge Requests + %span.count= MergeRequest.opened.of_user_team(@team).count + + = nav_link(controller: [:members]) do + = link_to team_members_path(@team), class: "team-tab tab" do + Members + %span.count= @team.members.count + + - if can? current_user, :admin_user_team, @team + = nav_link(path: 'teams#edit') do + = link_to edit_team_path(@team), class: "stat-tab tab " do + Settings + .content= yield diff --git a/app/views/merge_requests/_form.html.haml b/app/views/merge_requests/_form.html.haml index 6d64988c..816c852d 100644 --- a/app/views/merge_requests/_form.html.haml +++ b/app/views/merge_requests/_form.html.haml @@ -13,7 +13,7 @@ .mr_branch_box %h5.cgray From (Head Branch) .body - .padded= f.select(:source_branch, @repository.branch_names, { include_blank: "Select branch" }, {class: 'chosen span4'}) + .padded= f.select(:source_branch, @repository.heads.map(&:name), { include_blank: "Select branch" }, {class: 'chosen span4'}) .mr_source_commit .span2 @@ -22,7 +22,7 @@ .mr_branch_box %h5.cgray To (Base Branch) .body - .padded= f.select(:target_branch, @repository.branch_names, { include_blank: "Select branch" }, {class: 'chosen span4'}) + .padded= f.select(:target_branch, @repository.heads.map(&:name), { include_blank: "Select branch" }, {class: 'chosen span4'}) .mr_target_commit %fieldset diff --git a/app/views/notes/_form.html.haml b/app/views/notes/_form.html.haml index 7add2921..c2bdeafb 100644 --- a/app/views/notes/_form.html.haml +++ b/app/views/notes/_form.html.haml @@ -1,4 +1,4 @@ -= form_for [@project, @note], remote: true, html: { multipart: true, id: nil, class: "new_note js-new-note-form common-note-form" } do |f| += form_for [@project, @note], remote: true, html: { multipart: true, id: nil, class: "new_note js-new-note-form" } do |f| = note_target_fields = f.hidden_field :commit_id @@ -26,6 +26,15 @@ %a.btn.grouped.js-close-discussion-note-form Cancel + .note-form-option + = label_tag :notify do + = check_box_tag :notify, 1, false + %span.light Notify team via email + + .js-notify-commit-author + = label_tag :notify_author do + = check_box_tag :notify_author, 1 , false + %span.light Notify commit author .note-form-option %a.choose-btn.btn.btn-small.js-choose-note-attachment-button %i.icon-paper-clip diff --git a/app/views/notifications/show.html.haml b/app/views/notifications/show.html.haml deleted file mode 100644 index d8ab93b8..00000000 --- a/app/views/notifications/show.html.haml +++ /dev/null @@ -1,68 +0,0 @@ -%h3.page_title Setup your notification level - -%br - -%p.light - %strong Disabled - – You will not get any notifications via email -%p.light - %strong Participating - – You will receive only notifications from related resources(ex. from assigned issue or your commit) -%p.light - %strong Watch - – You will receive all notifications from projects in which you participate -%hr - -= form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications' do - %ul.well-list - %li - .row - .span4 - %h5 Global - .span7 - = label_tag do - = radio_button_tag :notification_level, Notification::N_DISABLED, @notification.disabled? - %span Disabled - - = label_tag do - = radio_button_tag :notification_level, Notification::N_PARTICIPATING, @notification.participating? - %span Participating - - = label_tag do - = radio_button_tag :notification_level, Notification::N_WATCH, @notification.watch? - %span Watch - - - = link_to '#', class: 'js-toggle-visibility-link' do - %h6.btn.btn-tiny - %i.icon-chevron-down - %span Per project notifications settings - %ul.well-list.js-toggle-visibility-container.hide - - @projects.each do |project| - %li - .row - .span4 - %span - = project.name_with_namespace - .span7 - = label_tag do - = radio_button_tag :"notification_level[#{project.id}]", Notification::N_DISABLED, @notification.disabled?, disabled: true - %span Disabled - - = label_tag do - = radio_button_tag :"notification_level[#{project.id}]", Notification::N_PARTICIPATING, @notification.participating?, disabled: true - %span Participating - - = label_tag do - = radio_button_tag :"notification_level[#{project.id}]", Notification::N_WATCH, @notification.watch?, disabled: true - %span Watch - - - .form-actions - = submit_tag 'Save', class: 'btn btn-save' - %span.update-success.cgreen.hide - %i.icon-ok - Saved - %span.update-failed.cred.hide - %i.icon-remove - Failed diff --git a/app/views/notifications/update.js.haml b/app/views/notifications/update.js.haml deleted file mode 100644 index 4468004a..00000000 --- a/app/views/notifications/update.js.haml +++ /dev/null @@ -1,7 +0,0 @@ -- if @saved - :plain - $('.update-notifications .update-success').showAndHide(); -- else - :plain - $('.update-notifications .update-failed').showAndHide(); - diff --git a/app/views/notify/closed_issue_email.html.haml b/app/views/notify/closed_issue_email.html.haml deleted file mode 100644 index 23ccc453..00000000 --- a/app/views/notify/closed_issue_email.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -%p - = "Issue was closed by #{@updated_by.name}" -%p - = "Issue ##{@issue.id}" - = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title diff --git a/app/views/notify/closed_issue_email.text.haml b/app/views/notify/closed_issue_email.text.haml deleted file mode 100644 index 0cca3215..00000000 --- a/app/views/notify/closed_issue_email.text.haml +++ /dev/null @@ -1,3 +0,0 @@ -= "Issue was closed by #{@updated_by.name}" - -Issue ##{@issue.id}: #{project_issue_url(@issue.project, @issue)} diff --git a/app/views/notify/closed_merge_request_email.html.haml b/app/views/notify/closed_merge_request_email.html.haml deleted file mode 100644 index 0c6c79e0..00000000 --- a/app/views/notify/closed_merge_request_email.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -%p - = "Merge Request #{@merge_request.id} was closed by #{@updated_by.name}" -%p - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.project, @merge_request) -%p - Branches: #{@merge_request.source_branch} → #{@merge_request.target_branch} -%p - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} - diff --git a/app/views/notify/closed_merge_request_email.text.haml b/app/views/notify/closed_merge_request_email.text.haml deleted file mode 100644 index ee4648e3..00000000 --- a/app/views/notify/closed_merge_request_email.text.haml +++ /dev/null @@ -1,8 +0,0 @@ -= "Merge Request #{@merge_request.id} was closed by #{@updated_by.name}" - -Merge Request url: #{project_merge_request_url(@merge_request.project, @merge_request)} - -Branches: #{@merge_request.source_branch} - #{@merge_request.target_branch} - -Author: #{@merge_request.author_name} -Assignee: #{@merge_request.assignee_name} diff --git a/app/views/notify/merged_merge_request_email.html.haml b/app/views/notify/merged_merge_request_email.html.haml deleted file mode 100644 index 2b8cc030..00000000 --- a/app/views/notify/merged_merge_request_email.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -%p - = "Merge Request #{@merge_request.id} was merged" -%p - = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.project, @merge_request) -%p - Branches: #{@merge_request.source_branch} → #{@merge_request.target_branch} -%p - Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} - diff --git a/app/views/notify/merged_merge_request_email.text.haml b/app/views/notify/merged_merge_request_email.text.haml deleted file mode 100644 index 91c23360..00000000 --- a/app/views/notify/merged_merge_request_email.text.haml +++ /dev/null @@ -1,8 +0,0 @@ -= "Merge Request #{@merge_request.id} was merged" - -Merge Request Url: #{project_merge_request_url(@merge_request.project, @merge_request)} - -Branches: #{@merge_request.source_branch} - #{@merge_request.target_branch} - -Author: #{@merge_request.author_name} -Assignee: #{@merge_request.assignee_name} diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml index f11c8506..0c891748 100644 --- a/app/views/notify/new_issue_email.html.haml +++ b/app/views/notify/new_issue_email.html.haml @@ -1,9 +1,5 @@ %p - New Issue was created. + New Issue was created and assigned to you. %p = "Issue ##{@issue.id}" = 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} diff --git a/app/views/notify/new_issue_email.text.erb b/app/views/notify/new_issue_email.text.erb index 9907ca83..5ed55c35 100644 --- a/app/views/notify/new_issue_email.text.erb +++ b/app/views/notify/new_issue_email.text.erb @@ -1,5 +1,4 @@ -New Issue was created. +New Issue was created and assigned to you. + Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %> -Author: <%= @issue.author_name %> -Asignee: <%= @issue.assignee_name %> diff --git a/app/views/projects/_clone_panel.html.haml b/app/views/projects/_clone_panel.html.haml index 9a2be429..e52df19b 100644 --- a/app/views/projects/_clone_panel.html.haml +++ b/app/views/projects/_clone_panel.html.haml @@ -13,5 +13,5 @@ = link_to new_project_merge_request_path(@project), title: "New Merge Request", class: "btn-small btn grouped" do Merge Request - if @project.issues_enabled && can?(current_user, :write_issue, @project) - = link_to url_for_new_issue, title: "New Issue", class: "btn-small btn grouped" do + = link_to new_project_issue_path(@project), title: "New Issue", class: "btn-small btn grouped" do Issue diff --git a/app/views/projects/_form.html.haml b/app/views/projects/_form.html.haml index 4d51e10d..6d547c94 100644 --- a/app/views/projects/_form.html.haml +++ b/app/views/projects/_form.html.haml @@ -36,10 +36,10 @@ .input = f.text_area :description, placeholder: "awesome project", class: "span5", rows: 3, maxlength: 250 - - unless @project.empty_repo? + - unless @repository.heads.empty? .clearfix = f.label :default_branch, "Default Branch" - .input= f.select(:default_branch, @repository.branch_names, {}) + .input= f.select(:default_branch, @repository.heads.map(&:name), {}, style: "width:210px;") - if can?(current_user, :change_public_mode, @project) @@ -54,7 +54,7 @@ %span.descr If checked, this project can be cloned %em without any - authentication. + authentification. It will also be listed on the #{link_to "public access directory", public_root_path}. %fieldset.features @@ -107,9 +107,8 @@ - if can?(current_user, :change_namespace, @project) .ui-box.ui-box-danger %h5.title Transfer project - .errors-holder .form-holder - = form_for(@project, url: transfer_project_path(@project), remote: true, html: { class: 'transfer-project' }) do |f| + = form_for(@project, remote: true, html: { class: 'transfer-project' }) do |f| .control-group = f.label :namespace_id do %span Namespace diff --git a/app/views/projects/transfer.js.haml b/app/views/projects/transfer.js.haml deleted file mode 100644 index 10b0de98..00000000 --- a/app/views/projects/transfer.js.haml +++ /dev/null @@ -1,7 +0,0 @@ -- if @project.errors[:namespace_id].present? - :plain - $("#tab-transfer .errors-holder").replaceWith(errorMessage('#{escape_javascript(@project.errors[:namespace_id].first)}')); - $("#tab-transfer .form-actions input").removeAttr('disabled').removeClass('disabled'); -- else - :plain - location.href = "#{edit_project_path(@project)}"; diff --git a/app/views/projects/update_failed.js.haml b/app/views/projects/update_failed.js.haml new file mode 100644 index 00000000..a3ac5f40 --- /dev/null +++ b/app/views/projects/update_failed.js.haml @@ -0,0 +1,2 @@ +:plain + $(".save-project-loader").replaceWith(errorMessage('#{escape_javascript(@error.message)}')); diff --git a/app/views/repositories/_branch.html.haml b/app/views/repositories/_branch.html.haml index dd91e14b..a6faa5fd 100644 --- a/app/views/repositories/_branch.html.haml +++ b/app/views/repositories/_branch.html.haml @@ -1,4 +1,5 @@ -- commit = Commit.new(Gitlab::Git::Commit.new(branch.commit)) +- commit = Commit.new(branch.commit) +- commit = CommitDecorator.decorate(commit) %tr %td = link_to project_commits_path(@project, branch.name) do diff --git a/app/views/repositories/_feed.html.haml b/app/views/repositories/_feed.html.haml index 6bb75265..eaf15ca7 100644 --- a/app/views/repositories/_feed.html.haml +++ b/app/views/repositories/_feed.html.haml @@ -1,4 +1,5 @@ - commit = update +- commit = CommitDecorator.new(commit) %tr %td = link_to project_commits_path(@project, commit.head.name) do diff --git a/app/views/repositories/tags.html.haml b/app/views/repositories/tags.html.haml index 2d311124..d4b8bbe1 100644 --- a/app/views/repositories/tags.html.haml +++ b/app/views/repositories/tags.html.haml @@ -7,7 +7,8 @@ %th Last commit %th - @tags.each do |tag| - - commit = Commit.new(Gitlab::Git::Commit.new(tag.commit)) + - commit = Commit.new(tag.commit) + - commit = CommitDecorator.decorate(commit) %tr %td %strong diff --git a/app/views/team_members/_assigned_team.html.haml b/app/views/team_members/_assigned_team.html.haml index 51a31a64..1d512c44 100644 --- a/app/views/team_members/_assigned_team.html.haml +++ b/app/views/team_members/_assigned_team.html.haml @@ -1,7 +1,7 @@ %li{id: dom_id(team), class: "user_team_row team_#{team.id}"} .pull-right - if can?(current_user, :admin_team_member, @project) - = link_to resign_project_team_path(@project, team), method: :delete, confirm: "Are you sure?", class: "btn btn-remove btn-tiny" do + = link_to resign_project_team_path(@project, team), method: :delete, confirm: "Are you shure?", class: "btn btn-remove btn-tiny" do %i.icon-minus.icon-white %strong= link_to team.name, team_path(team), title: team.name, class: "dark" diff --git a/app/views/tree/_blob_actions.html.haml b/app/views/tree/_blob_actions.html.haml index 1d55a4ff..0bde968d 100644 --- a/app/views/tree/_blob_actions.html.haml +++ b/app/views/tree/_blob_actions.html.haml @@ -1,7 +1,7 @@ .btn-group.tree-btn-group -# only show edit link for text files - if @tree.text? - = link_to "edit", project_edit_tree_path(@project, @id), class: "btn btn-tiny", disabled: !allowed_tree_edit? + = link_to "edit", edit_project_tree_path(@project, @id), class: "btn btn-tiny", disabled: !allowed_tree_edit? = link_to "raw", project_blob_path(@project, @id), class: "btn btn-tiny", target: "_blank" -# only show normal/blame view links for text files - if @tree.text? diff --git a/app/views/tree/_tree.html.haml b/app/views/tree/_tree.html.haml index caab6e45..24a57ae7 100644 --- a/app/views/tree/_tree.html.haml +++ b/app/views/tree/_tree.html.haml @@ -3,7 +3,7 @@ %i.icon-angle-right = link_to project_tree_path(@project, @ref) do = @project.path - - tree_breadcrumbs(tree, 6) do |title, path| + - tree.breadcrumbs(6) do |title, path| \/ %li - if path @@ -27,7 +27,7 @@ %tr.tree-item %td.tree-item-file-name = image_tag "file_empty.png", size: '16x16' - = link_to "..", project_tree_path(@project, up_dir_path(tree)) + = link_to "..", project_tree_path(@project, tree.up_dir_path) %td %td %td diff --git a/app/views/tree/_tree_commit_column.html.haml b/app/views/tree/_tree_commit_column.html.haml index 7ae2582c..9d02132b 100644 --- a/app/views/tree/_tree_commit_column.html.haml +++ b/app/views/tree/_tree_commit_column.html.haml @@ -1,2 +1,2 @@ -%span.tree_author= commit_author_link(commit, avatar: true) +%span.tree_author= commit.author_link avatar: true = link_to_gfm truncate(commit.title, length: 80), project_commit_path(@project, commit.id), class: "tree-commit-link" diff --git a/app/views/edit_tree/show.html.haml b/app/views/tree/edit.html.haml similarity index 93% rename from app/views/edit_tree/show.html.haml rename to app/views/tree/edit.html.haml index 211396ba..81918e50 100644 --- a/app/views/edit_tree/show.html.haml +++ b/app/views/tree/edit.html.haml @@ -1,5 +1,5 @@ .file-editor - = form_tag(project_edit_tree_path(@project, @id), method: :put, class: "form-horizontal") do + = form_tag(project_tree_path(@project, @id), method: :put, class: "form-horizontal") do .file_holder .file_title %i.icon-file diff --git a/app/views/tree/show.js.haml b/app/views/tree/show.js.haml index a01d4917..fadd5e22 100644 --- a/app/views/tree/show.js.haml +++ b/app/views/tree/show.js.haml @@ -1,7 +1,7 @@ :plain // Load Files list $("#tree-holder").html("#{escape_javascript(render(partial: "tree", locals: {tree: @tree}))}"); - $("#tree-content-holder").show("slide", { direction: "right" }, 400); + $("#tree-content-holder").show("slide", { direction: "right" }, 150); $('.project-refs-form #path').val("#{@path}"); // Load last commit log for each file in tree diff --git a/app/views/walls/show.html.haml b/app/views/walls/show.html.haml index 139e66f5..0cd29486 100644 --- a/app/views/walls/show.html.haml +++ b/app/views/walls/show.html.haml @@ -1,5 +1,5 @@ %div.wall-page - %ul.notes + %ul.well-list.notes - if can? current_user, :write_note, @project .note-form-holder @@ -11,6 +11,11 @@ .buttons = f.submit 'Add Comment', class: "btn comment-btn grouped js-comment-button" + .note-form-option + = label_tag :notify do + = check_box_tag :notify, 1, false + %span.light Notify team via email + .note-form-option %a.choose-btn.btn.btn-small.js-choose-note-attachment-button %i.icon-paper-clip diff --git a/app/views/wikis/history.html.haml b/app/views/wikis/history.html.haml index f4946ed0..599e9cf6 100644 --- a/app/views/wikis/history.html.haml +++ b/app/views/wikis/history.html.haml @@ -14,13 +14,12 @@ %th Format %tbody - @wiki.versions.each do |version| - - commit = version + - commit = CommitDecorator.new(version) %tr %td = link_to project_wiki_path(@project, @wiki, version_id: commit.id) do = commit.short_id - %td - = commit_author_link(commit, avatar: true, size: 24) + %td= commit.author_link avatar: true, size: 24 %td = commit.title %td diff --git a/app/views/wikis/pages.html.haml b/app/views/wikis/pages.html.haml index 95d5eef1..eb65599d 100644 --- a/app/views/wikis/pages.html.haml +++ b/app/views/wikis/pages.html.haml @@ -21,5 +21,5 @@ = wiki_page.created_at.to_s(:short) do (#{time_ago_in_words(wiki_page.created_at)} ago) - %td - = commit_author_link(wiki_page.version, avatar: true, size: 24) + - commit = CommitDecorator.decorate(wiki_page.version) + %td= commit.author_link avatar: true, size: 24 diff --git a/app/views/wikis/show.html.haml b/app/views/wikis/show.html.haml index b237bc52..b660a15e 100644 --- a/app/views/wikis/show.html.haml +++ b/app/views/wikis/show.html.haml @@ -13,4 +13,5 @@ = preserve do = render_wiki_content(@wiki) -%p.time Last edited by #{commit_author_link(@wiki.version, avatar: true, size: 16)} #{time_ago_in_words @wiki.created_at} ago +- commit = CommitDecorator.new(@wiki.version) +%p.time Last edited by #{commit.author_link(avatar: true, size: 16)} #{time_ago_in_words @wiki.created_at} ago diff --git a/app/workers/gitlab_shell_worker.rb b/app/workers/gitlab_shell_worker.rb index cfeda88b..0a921b1b 100644 --- a/app/workers/gitlab_shell_worker.rb +++ b/app/workers/gitlab_shell_worker.rb @@ -1,6 +1,6 @@ class GitlabShellWorker include Sidekiq::Worker - include Gitlab::ShellAdapter + include Gitolited sidekiq_options queue: :gitlab_shell diff --git a/config/database.yml.postgresql b/config/database.yml.postgresql index 4a4aa346..2bc0884f 100644 --- a/config/database.yml.postgresql +++ b/config/database.yml.postgresql @@ -6,7 +6,7 @@ production: encoding: unicode database: gitlabhq_production pool: 5 - username: git + username: gitlab password: # host: localhost # port: 5432 diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 90d04b39..a8704719 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -46,19 +46,12 @@ production: &base # ## :project_id - GitLab project identifier # ## :issues_tracker_id - Project Name or Id in external issue tracker # project_url: "http://redmine.sample/projects/:issues_tracker_id" - # # ## If not nil, links from /#\d/ entities from commit messages will replaced with this # ## Use placeholders: # ## :project_id - GitLab project identifier # ## :issues_tracker_id - Project Name or Id in external issue tracker # ## :id - Issue id (from commit messages) # issues_url: "http://redmine.sample/issues/:id" - # - # ## If not nil, linkis to creating new issues will be replaced with this - # ## Use placeholders: - # ## :project_id - GitLab project identifier - # ## :issues_tracker_id - Project Name or Id in external issue tracker - # new_issue_url: "http://redmine.sample/projects/:issues_tracker_id/issues/new" ## Gravatar gravatar: @@ -159,7 +152,6 @@ test: redmine: project_url: "http://redmine/projects/:issues_tracker_id" issues_url: "http://redmine/:project_id/:issues_tracker_id/:id" - new_issue_url: "http://redmine/projects/:issues_tracker_id/insues/new" staging: <<: *base diff --git a/config/initializers/5_backend.rb b/config/initializers/5_backend.rb index 7c2e7f39..73436608 100644 --- a/config/initializers/5_backend.rb +++ b/config/initializers/5_backend.rb @@ -3,6 +3,3 @@ require Rails.root.join("lib", "gitlab", "backend", "grack_auth") # GIT over SSH require Rails.root.join("lib", "gitlab", "backend", "shell") - -# GitLab shell adapter -require Rails.root.join("lib", "gitlab", "backend", "shell_adapter") diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 5714407f..9c397633 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -23,7 +23,7 @@ Devise.setup do |config| # session. If you need permissions, you should implement that in a before filter. # You can also supply a hash where the value is a boolean determining whether # or not authentication should be aborted when the value is not present. - config.authentication_keys = [ :login ] + # config.authentication_keys = [ :email ] # Configure parameters from the request object used for authentication. Each entry # given should be a request method and it will automatically be passed to the diff --git a/config/routes.rb b/config/routes.rb index 61a604b9..0028baf8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -85,7 +85,11 @@ Gitlab::Application.routes.draw do resource :logs, only: [:show] resource :resque, controller: 'resque', only: [:show] - resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] do + resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, except: [:new, :create] do + member do + get :team + put :team_update + end scope module: :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } do resources :members, only: [:edit, :update, :destroy] end @@ -110,8 +114,6 @@ Gitlab::Application.routes.draw do put :reset_private_token put :update_username end - - resource :notifications end resources :keys @@ -165,13 +167,8 @@ Gitlab::Application.routes.draw do # Project Area # resources :projects, constraints: { id: /(?:[a-zA-Z.0-9_\-]+\/)?[a-zA-Z.0-9_\-]+/ }, except: [:new, :create, :index], path: "/" do - member do - put :transfer - end - resources :blob, only: [:show], constraints: {id: /.+/} - resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ } - resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit' + resources :tree, only: [:show, :edit, :update], constraints: {id: /.+/} resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/} resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/} resources :compare, only: [:index, :create] diff --git a/db/migrate/20130325173941_add_notification_level_to_user.rb b/db/migrate/20130325173941_add_notification_level_to_user.rb deleted file mode 100644 index 9f466e38..00000000 --- a/db/migrate/20130325173941_add_notification_level_to_user.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddNotificationLevelToUser < ActiveRecord::Migration - def change - add_column :users, :notification_level, :integer, null: false, default: 1 - end -end diff --git a/db/schema.rb b/db/schema.rb index 3c8b9eae..e4349ac4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130325173941) do +ActiveRecord::Schema.define(:version => 20130318212250) do create_table "events", :force => true do |t| t.string "target_type" @@ -270,7 +270,6 @@ ActiveRecord::Schema.define(:version => 20130325173941) do 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 end add_index "users", ["admin"], :name => "index_users_on_admin" diff --git a/doc/install/databases.md b/doc/install/databases.md index fade5d4f..2c4fb9db 100644 --- a/doc/install/databases.md +++ b/doc/install/databases.md @@ -38,10 +38,10 @@ GitLab supports the following databases: sudo -u postgres psql -d template1 # Create a user for GitLab. (change $password to a real password) - template1=# CREATE USER git WITH PASSWORD '$password'; + template1=# CREATE USER gitlab WITH PASSWORD '$password'; # Create the GitLab production database & grant all privileges on database - template1=# CREATE DATABASE gitlabhq_production OWNER git; + template1=# CREATE DATABASE gitlabhq_production OWNER gitlab; # Quit the database session template1=# \q diff --git a/doc/install/installation.md b/doc/install/installation.md index a9ae4b1e..853a6e78 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -1,12 +1,19 @@ -# Important notes +This installation guide was created for Debian/Ubuntu and tested on it. Please read [`doc/install/requirements.md`](./requirements.md) for hardware and platform requirements. -This installation guide was created for and tested on **Debian/Ubuntu** operating systems. Please read [`doc/install/requirements.md`](./requirements.md) for hardware and operating system requirements. +This installation guide is recommended to set up a production server. If you want a development environment please use the [Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) since it makes it much easier to set up all the dependencies for integration testing. -This is the official installation guide to set up a production server. To set up a **development installation** or for many other installation options please consult [the installation section in the readme](https://github.com/gitlabhq/gitlabhq#installation). +**Important Note:** +The following steps have been known to work. +If you deviate from this guide, do it with caution and make sure you don't +violate any assumptions GitLab makes about its environment. +For things like AWS installation scripts, init scripts or config files for +alternative web server have a look at the [`Advanced Setup +Tips`](./installation.md#advanced-setup-tips) section. -The following steps have been known to work. Please **use caution when you deviate** from this guide. Make sure you don't violate any assumptions GitLab makes about its environment. -If you find a bug/error in this guide please **submit a pull request** following the [`contributing guide`](../../CONTRIBUTING.md). +**Important Note:** +If you find a bug/error in this guide please submit an issue or pull request +following the [`contribution guide`](../../CONTRIBUTING.md). - - - @@ -67,8 +74,8 @@ Make sure you have the right version of Python installed. Download and compile it: mkdir /tmp/ruby && cd /tmp/ruby - curl --progress http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p392.tar.gz | tar xz - cd ruby-1.9.3-p392 + curl --progress http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p327.tar.gz | tar xz + cd ruby-1.9.3-p327 ./configure make sudo make install @@ -99,6 +106,11 @@ GitLab Shell is a ssh access and repository management software developed specia git clone https://github.com/gitlabhq/gitlab-shell.git cd gitlab-shell + + # switch to right version for v5.0 + git checkout v1.1.0 + git checkout -b v1.1.0 + cp config.yml.example config.yml # Edit config and replace gitlab_url @@ -293,26 +305,7 @@ If you are running SSH on a non-standard port, you must change the gitlab user's You also need to change the corresponding options (e.g. ssh_user, ssh_host, admin_uri) in the `config\gitlab.yml` file. -## LDAP authentication +## User-contributed Configurations -You can configure LDAP authentication in config/gitlab.yml. Please restart GitLab after editing this file. - -## Using Custom Omniauth Providers - -GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already ships with a few providers preinstalled (e.g. LDAP, GitHub, Twitter). But sometimes that is not enough and you need to integrate with other authentication solutions. For these cases you can use the Omniauth provider. - -### Steps - -These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation. - -* Add `gem "omniauth-your-auth-provider"` to the [Gemfile](https://github.com/gitlabhq/gitlabhq/blob/master/Gemfile#L18) -* Run `sudo -u gitlab -H bundle install` to install the new gem(s) -* Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://github.com/gitlabhq/gitlabhq/blob/master/config/gitlab.yml.example#L53) as a reference) -* Add icons for the new provider into the [vendor/assets/images/authbuttons](https://github.com/gitlabhq/gitlabhq/tree/master/vendor/assets/images/authbuttons) directory (you can find some more popular ones over at https://github.com/intridea/authbuttons) -* Restart GitLab - -### Examples - -If you have successfully set up a provider that is not shipped with GitLab itself, please let us know. -You can help others by reporting successful configurations and probably share a few insights or provide warnings for common errors or pitfalls by sharing your experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Working-Custom-Omniauth-Provider-Configurations). -While we can't officially support every possible auth mechanism out there, we'd like to at least help those with special needs. +You can find things like AWS installation scripts, init scripts or config files +for alternative web server in our [recipes collection](https://github.com/gitlabhq/gitlab-recipes/). diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 9209fad5..ec5b013c 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -1,3 +1,13 @@ +# Hardware + +We recommend you to run GitLab on a server with at least 1GB RAM. + +The necessary hard disk space largely depends on the size of the repos you want +to use GitLab with. But as a *rule of thumb* you should have at least as much +free space as your all repos combined take up. + + + # Operating Systems ## Linux @@ -26,7 +36,8 @@ systems. This means you may get it to work on systems running FreeBSD or OS X. ## Windows GitLab does **not** run on Windows and we have no plans of supporting it in the -near future. Please consider using a virtual machine to run GitLab. +near future. + # Rubies @@ -37,24 +48,6 @@ While it is generally possible to use other Rubies (like some work on your part. -# Hardware requirements - -## CPU - -We recommend a processor with **4 cores**. At a minimum you need a processor with 2 cores to responsively run an unmodified installation. - -## Memory - -We recommend you to run GitLab on a server with at least **1GB of RAM** memory. You can use it with 512MB of memory but you need to setup unicorn to use only 1 worker and you need at least 200MB of swap. The minimal requirement for an unmodified installation is 768MB. With 1.5GB of memory you should be able to support 1000+ users. - -## Storage - -The necessary hard drive space largely depends on the size of the repos you want -to store in GitLab. But as a *rule of thumb* you should have at least twice as much -free space as your all repos combined take up. You need twice the storage because [GitLab satellites](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/structure.md) contain an extra copy of each repo. Apart from a local hard drive you can also mount a volume that supports the network file system (NFS) protocol. This volume might be located on a file server, a network attached storage (NAS) device, a storage area network (SAN) or on an Amazon Web Services (AWS) Elastic Block Store (EBS) volume. - -If you have enough RAM memory and a recent CPU the speed of GitLab is mainly limited by hard drive seek times. Having a fast drive (7200 RPM and up) or a solid state drive (SSD) will improve the responsiveness of GitLab. - # Installation troubles and reporting success or failure diff --git a/doc/raketasks/maintenance.md b/doc/raketasks/maintenance.md index b5514705..726cc083 100644 --- a/doc/raketasks/maintenance.md +++ b/doc/raketasks/maintenance.md @@ -15,7 +15,7 @@ System: Debian 6.0.6 Current User: gitlab Using RVM: yes RVM Version: 1.17.2 -Ruby Version: ruby-1.9.3-p392 +Ruby Version: ruby-1.9.3-p327 Gem Version: 1.8.24 Bundler Version:1.2.3 Rake Version: 10.0.1 diff --git a/features/admin/users.feature b/features/admin/users.feature index 4c951df9..03ac86a3 100644 --- a/features/admin/users.feature +++ b/features/admin/users.feature @@ -6,11 +6,3 @@ Feature: Admin Users Scenario: On Admin Users Given I visit admin users page Then I should see all users - - Scenario: Edit user and change username to non ascii char - When I visit admin users page - And Click edit - And Input non ascii char in username - And Click save - Then See username error message - And Not chenged form action url diff --git a/features/steps/admin/admin_projects.rb b/features/steps/admin/admin_projects.rb index b410b238..dd6b4e98 100644 --- a/features/steps/admin/admin_projects.rb +++ b/features/steps/admin/admin_projects.rb @@ -16,7 +16,9 @@ class AdminProjects < Spinach::FeatureSteps Then 'I should see project details' do project = Project.first current_path.should == admin_project_path(project) + page.should have_content(project.name_with_namespace) page.should have_content(project.creator.name) + page.should have_content('Add new team member') end end diff --git a/features/steps/admin/admin_users.rb b/features/steps/admin/admin_users.rb index 61b3ed91..1828ae70 100644 --- a/features/steps/admin/admin_users.rb +++ b/features/steps/admin/admin_users.rb @@ -8,27 +8,4 @@ class AdminUsers < Spinach::FeatureSteps page.should have_content user.name end end - - And 'Click edit' do - @user = User.first - find("#edit_user_#{@user.id}").click - end - - And 'Input non ascii char in username' do - fill_in 'user_username', with: "\u3042\u3044" - end - - And 'Click save' do - click_button("Save") - end - - Then 'See username error message' do - within "#error_explanation" do - page.should have_content "Username" - end - end - - And 'Not chenged form action url' do - page.should have_selector %(form[action="/admin/users/#{@user.username}"]) - end end diff --git a/features/steps/project/project_browse_commits.rb b/features/steps/project/project_browse_commits.rb index b4c595fa..3433c2ba 100644 --- a/features/steps/project/project_browse_commits.rb +++ b/features/steps/project/project_browse_commits.rb @@ -15,7 +15,7 @@ class ProjectBrowseCommits < Spinach::FeatureSteps end Then 'I see commits atom feed' do - commit = @project.repository.commit + commit = CommitDecorator.decorate(@project.repository.commit) page.response_headers['Content-Type'].should have_content("application/atom+xml") page.body.should have_selector("title", :text => "Recent commits to #{@project.name}") page.body.should have_selector("author email", :text => commit.author_email) diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb index 617a077b..446e3b9a 100644 --- a/features/steps/shared/active_tab.rb +++ b/features/steps/shared/active_tab.rb @@ -3,9 +3,9 @@ module SharedActiveTab def ensure_active_main_tab(content) if content == "Home" - page.find('.main-nav li.active').should have_css('i.icon-home') + page.find('ul.main_menu li.active').should have_css('i.icon-home') else - page.find('.main-nav li.active').should have_content(content) + page.find('ul.main_menu li.active').should have_content(content) end end @@ -14,7 +14,7 @@ module SharedActiveTab end And 'no other main tabs should be active' do - page.should have_selector('.main-nav li.active', count: 1) + page.should have_selector('ul.main_menu li.active', count: 1) end And 'no other sub tabs should be active' do diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index b16032a8..81863a54 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -3,14 +3,14 @@ module SharedProject # Create a project without caring about what it's called And "I own a project" do - @project = create(:project_with_code) + @project = create(:project) @project.team << [@user, :master] end # Create a specific project called "Shop" And 'I own project "Shop"' do @project = Project.find_by_name "Shop" - @project ||= create(:project_with_code, name: "Shop") + @project ||= create(:project, name: "Shop") @project.team << [@user, :master] end diff --git a/features/steps/userteams/userteams.rb b/features/steps/userteams/userteams.rb index 9a86572e..70ad3cca 100644 --- a/features/steps/userteams/userteams.rb +++ b/features/steps/userteams/userteams.rb @@ -132,7 +132,7 @@ class Userteams < Spinach::FeatureSteps team = UserTeam.last team.projects.each do |project| team.members.each do |member| - 3.times { create(:merge_request, assignee: member, project: project) } + 3.times { project.merge_requests << create(:merge_request, assignee: member) } end end end @@ -157,7 +157,7 @@ class Userteams < Spinach::FeatureSteps team = UserTeam.last team.projects.each do |project| team.members.each do |member| - 3.times { create(:merge_request, assignee: member, project: project) } + 3.times { project.merge_requests << create(:merge_request, assignee: member) } end end end diff --git a/features/support/env.rb b/features/support/env.rb index 08b627f5..90a61dd1 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -14,7 +14,7 @@ require 'spinach/capybara' require 'sidekiq/testing/inline' -%w(valid_commit select2_helper test_env).each do |f| +%w(stubbed_repository valid_commit select2_helper).each do |f| require Rails.root.join('spec', 'support', f) end @@ -35,8 +35,13 @@ Capybara.default_wait_time = 10 DatabaseCleaner.strategy = :truncation Spinach.hooks.before_scenario do - TestEnv.init - + # Use tmp dir for FS manipulations + Gitlab.config.gitlab_shell.stub(repos_path: Rails.root.join('tmp', 'test-git-base-path')) + Gitlab::Shell.any_instance.stub(:add_repository) do |path| + create_temp_repo("#{Rails.root}/tmp/test-git-base-path/#{path}.git") + end + FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path + FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path DatabaseCleaner.start end @@ -49,3 +54,9 @@ Spinach.hooks.before_run do include FactoryGirl::Syntax::Methods end + +def create_temp_repo(path) + FileUtils.mkdir_p path + command = "git init --quiet --bare #{path};" + system(command) +end diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index d5595d5f..234a005a 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -94,8 +94,6 @@ module Gitlab authorize! :modify_merge_request, merge_request - MergeRequestObserver.current_user = current_user - if merge_request.update_attributes attrs merge_request.reload_code merge_request.mark_as_unchecked diff --git a/lib/api/projects.rb b/lib/api/projects.rb index ce94c34b..d4f50fda 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -372,7 +372,7 @@ module Gitlab ref = params[:ref_name] || user_project.try(:default_branch) || 'master' commits = user_project.repository.commits(ref, nil, per_page, page * per_page) - present commits, with: Entities::RepoCommit + present CommitDecorator.decorate(commits), with: Entities::RepoCommit end # Get a project snippets diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb index 2e3ab1b2..66b2f450 100644 --- a/lib/extracts_path.rb +++ b/lib/extracts_path.rb @@ -8,7 +8,7 @@ module ExtractsPath included do if respond_to?(:before_filter) - before_filter :assign_ref_vars + before_filter :assign_ref_vars, only: [:show] end end @@ -33,7 +33,7 @@ module ExtractsPath # extract_ref("v2.0.0/README.md") # # => ['v2.0.0', 'README.md'] # - # extract_ref('master/app/models/project.rb') + # extract_ref('/gitlab/vagrant/tree/master/app/models/project.rb') # # => ['master', 'app/models/project.rb'] # # extract_ref('issues/1234/app/models/project.rb') @@ -45,12 +45,22 @@ module ExtractsPath # # Returns an Array where the first value is the tree-ish and the second is the # path - def extract_ref(id) + def extract_ref(input) pair = ['', ''] return pair unless @project - if id.match(/^([[:alnum:]]{40})(.+)/) + # Remove relative_url_root from path + input.gsub!(/^#{Gitlab.config.gitlab.relative_url_root}/, "") + # Remove project, actions and all other staff from path + input.gsub!(/^\/#{Regexp.escape(@project.path_with_namespace)}/, "") + input.gsub!(/^\/(tree|commits|blame|blob|refs|graph)\//, "") # remove actions + input.gsub!(/\?.*$/, "") # remove stamps suffix + input.gsub!(/.atom$/, "") # remove rss feed + input.gsub!(/.json$/, "") # remove json suffix + input.gsub!(/\/edit$/, "") # remove edit route part + + if input.match(/^([[:alnum:]]{40})(.+)/) # If the ref appears to be a SHA, we're done, just split the string pair = $~.captures else @@ -58,6 +68,7 @@ module ExtractsPath # branches and tags # Append a trailing slash if we only get a ref and no file path + id = input id += '/' unless id.ends_with?('/') valid_refs = @project.repository.ref_names @@ -85,8 +96,8 @@ module ExtractsPath # - @id - A string representing the joined ref and path # - @ref - A string representing the ref (e.g., the branch, tag, or commit SHA) # - @path - A string representing the filesystem path - # - @commit - A Commit representing the commit from the given ref - # - @tree - A Tree representing the tree at the given ref/path + # - @commit - A CommitDecorator representing the commit from the given ref + # - @tree - A TreeDecorator representing the tree at the given ref/path # # If the :id parameter appears to be requesting a specific response format, # that will be handled as well. @@ -94,15 +105,19 @@ module ExtractsPath # Automatically renders `not_found!` if a valid tree path could not be # resolved (e.g., when a user inserts an invalid path or ref). def assign_ref_vars - @id = params[:id] + path = CGI::unescape(request.fullpath.dup) - @ref, @path = extract_ref(@id) + @ref, @path = extract_ref(path) + + @id = File.join(@ref, @path) # It is used "@project.repository.commits(@ref, @path, 1, 0)", # because "@project.repository.commit(@ref)" returns wrong commit when @ref is tag name. - @commit = @project.repository.commits(@ref, @path, 1, 0).first + commits = @project.repository.commits(@ref, @path, 1, 0) + @commit = CommitDecorator.decorate(commits.first) @tree = Tree.new(@commit.tree, @ref, @path) + @tree = TreeDecorator.new(@tree) raise InvalidPathError if @tree.invalid? rescue RuntimeError, NoMethodError, InvalidPathError diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index bae87977..a230886b 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -65,72 +65,13 @@ module Gitlab system("#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-keys rm-key #{key_id} \"#{key_content}\"") end - # Add empty directory for storing repositories - # - # Ex. - # add_namespace("gitlab") - # - def add_namespace(name) - FileUtils.mkdir(full_path(name), mode: 0770) unless exists?(name) - end - - # Remove directory from repositories storage - # Every repository inside this directory will be removed too - # - # Ex. - # rm_namespace("gitlab") - # - def rm_namespace(name) - FileUtils.rm_r(full_path(name), force: true) - end - - # Move namespace directory inside repositories storage - # - # Ex. - # mv_namespace("gitlab", "gitlabhq") - # - def mv_namespace(old_name, new_name) - return false if exists?(new_name) || !exists?(old_name) - - FileUtils.mv(full_path(old_name), full_path(new_name)) - end - - # Remove GitLab Satellites for provided path (namespace or repo dir) - # - # Ex. - # rm_satellites("gitlab") - # - # rm_satellites("gitlab/gitlab-ci.git") - # - def rm_satellites(path) - raise ArgumentError.new("Path can't be blank") if path.blank? - - satellites_path = File.join(Gitlab.config.satellites.path, path) - FileUtils.rm_r(satellites_path, force: true) - end - def url_to_repo path Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git" end - protected - def gitlab_shell_user_home File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}") end - def repos_path - Gitlab.config.gitlab_shell.repos_path - end - - def full_path(dir_name) - raise ArgumentError.new("Directory name can't be blank") if dir_name.blank? - - File.join(repos_path, dir_name) - end - - def exists?(dir_name) - File.exists?(full_path(dir_name)) - end end end diff --git a/lib/gitlab/backend/shell_adapter.rb b/lib/gitlab/backend/shell_adapter.rb deleted file mode 100644 index f247f459..00000000 --- a/lib/gitlab/backend/shell_adapter.rb +++ /dev/null @@ -1,12 +0,0 @@ -# == GitLab Shell mixin -# -# Provide a shortcut to Gitlab::Shell instance by gitlab_shell -# -module Gitlab - module ShellAdapter - def gitlab_shell - Gitlab::Shell.new - end - end -end - diff --git a/lib/gitlab/git/blame.rb b/lib/gitlab/git/blame.rb deleted file mode 100644 index d6e988b6..00000000 --- a/lib/gitlab/git/blame.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Gitlab - module Git - class Blame - - attr_accessor :repository, :sha, :path - - def initialize(repository, sha, path) - @repository, @sha, @path = repository, sha, path - - end - - def each - raw_blame = Grit::Blob.blame(repository.repo, sha, path) - - raw_blame.each do |commit, lines| - commit = Gitlab::Git::Commit.new(commit) - yield(commit, lines) - end - end - end - end -end diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb deleted file mode 100644 index 35991a38..00000000 --- a/lib/gitlab/git/commit.rb +++ /dev/null @@ -1,153 +0,0 @@ -# Gitlab::Git::Commit is a wrapper around native Grit::Commit object -# We dont want to use grit objects inside app/ -# It helps us easily migrate to rugged in future -module Gitlab - module Git - class Commit - attr_accessor :raw_commit, :head, :refs - - delegate :message, :authored_date, :committed_date, :parents, :sha, - :date, :committer, :author, :diffs, :tree, :id, :stats, :to_patch, - to: :raw_commit - - class << self - def find_or_first(repo, commit_id = nil, root_ref) - commit = if commit_id - repo.commit(commit_id) - else - repo.commits(root_ref).first - end - - Commit.new(commit) if commit - end - - def fresh_commits(repo, n = 10) - commits = repo.heads.map do |h| - repo.commits(h.name, n).map { |c| Commit.new(c, h) } - end.flatten.uniq { |c| c.id } - - commits.sort! do |x, y| - y.committed_date <=> x.committed_date - end - - commits[0...n] - end - - def commits_with_refs(repo, n = 20) - commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) } - - commits.sort! do |x, y| - y.committed_date <=> x.committed_date - end - - commits[0..n] - end - - def commits_since(repo, date) - commits = repo.heads.map do |h| - repo.log(h.name, nil, since: date).each { |c| Commit.new(c, h) } - end.flatten.uniq { |c| c.id } - - commits.sort! do |x, y| - y.committed_date <=> x.committed_date - end - - commits - end - - def commits(repo, ref, path = nil, limit = nil, offset = nil) - if path - repo.log(ref, path, max_count: limit, skip: offset) - elsif limit && offset - repo.commits(ref, limit, offset) - else - repo.commits(ref) - end.map{ |c| Commit.new(c) } - end - - def commits_between(repo, from, to) - repo.commits_between(from, to).map { |c| Commit.new(c) } - end - end - - def initialize(raw_commit, head = nil) - raise "Nil as raw commit passed" unless raw_commit - - @raw_commit = raw_commit - @head = head - end - - def short_id(length = 10) - id.to_s[0..length] - end - - def safe_message - @safe_message ||= message - end - - def created_at - committed_date - end - - def author_email - author.email - end - - def author_name - author.name - end - - # Was this commit committed by a different person than the original author? - def different_committer? - author_name != committer_name || author_email != committer_email - end - - def committer_name - committer.name - end - - def committer_email - committer.email - end - - def prev_commit - @prev_commit ||= if parents.present? - Commit.new(parents.first) - else - nil - end - end - - def prev_commit_id - prev_commit.try :id - end - - # Shows the diff between the commit's parent and the commit. - # - # Cuts out the header and stats from #to_patch and returns only the diff. - def to_diff - # see Grit::Commit#show - patch = to_patch - - # discard lines before the diff - lines = patch.split("\n") - while !lines.first.start_with?("diff --git") do - lines.shift - end - lines.pop if lines.last =~ /^[\d.]+$/ # Git version - lines.pop if lines.last == "-- " # end of diff - lines.join("\n") - end - - def has_zero_stats? - stats.total.zero? - rescue - true - end - - def no_commit_message - "--no commit message" - end - end - end -end diff --git a/lib/gitlab/git/compare.rb b/lib/gitlab/git/compare.rb deleted file mode 100644 index 1fa43062..00000000 --- a/lib/gitlab/git/compare.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Gitlab - module Git - class Compare - attr_accessor :commits, :commit, :diffs, :same - - def initialize(repository, from, to) - @commits, @diffs = [], [] - @commit = nil - @same = false - - return unless from && to - - first = repository.commit(to.try(:strip)) - last = repository.commit(from.try(:strip)) - - return unless first && last - - if first.id == last.id - @same = true - return - end - - @commit = Commit.new(first) - - @commits = repository.commits_between(last.id, first.id) - @commits = @commits.map { |c| Commit.new(c) } - - @diffs = if @commits.size > 100 - [] - else - repository.repo.diff(last.id, first.id) rescue [] - end - end - end - end -end - diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb deleted file mode 100644 index 30344a3d..00000000 --- a/lib/gitlab/git/repository.rb +++ /dev/null @@ -1,185 +0,0 @@ -# Gitlab::Git::Gitlab::Git::Commit is a wrapper around native Grit::Repository object -# We dont want to use grit objects inside app/ -# It helps us easily migrate to rugged in future -module Gitlab - module Git - class Repository - include Gitlab::Popen - - class NoRepository < StandardError; end - - # Repository directory name with namespace direcotry - # Examples: - # gitlab/gitolite - # diaspora - # - attr_accessor :path_with_namespace - - # Grit repo object - attr_accessor :repo - - # Default branch in the repository - attr_accessor :root_ref - - def initialize(path_with_namespace, root_ref = 'master') - @root_ref = root_ref || "master" - @path_with_namespace = path_with_namespace - - # Init grit repo object - repo - end - - def raw - repo - end - - def path_to_repo - @path_to_repo ||= File.join(repos_path, "#{path_with_namespace}.git") - end - - def repos_path - Gitlab.config.gitlab_shell.repos_path - end - - def repo - @repo ||= Grit::Repo.new(path_to_repo) - rescue Grit::NoSuchPathError - raise NoRepository.new('no repository for such path') - end - - def commit(commit_id = nil) - Gitlab::Git::Commit.find_or_first(repo, commit_id, root_ref) - end - - def fresh_commits(n = 10) - Gitlab::Git::Commit.fresh_commits(repo, n) - end - - def commits_with_refs(n = 20) - Gitlab::Git::Commit.commits_with_refs(repo, n) - end - - def commits_since(date) - Gitlab::Git::Commit.commits_since(repo, date) - end - - def commits(ref, path = nil, limit = nil, offset = nil) - Gitlab::Git::Commit.commits(repo, ref, path, limit, offset) - end - - def last_commit_for(ref, path = nil) - commits(ref, path, 1).first - end - - def commits_between(from, to) - Gitlab::Git::Commit.commits_between(repo, from, to) - end - - # Returns an Array of branch names - # sorted by name ASC - def branch_names - branches.map(&:name) - end - - # Returns an Array of Branches - def branches - repo.branches.sort_by(&:name) - end - - # Returns an Array of tag names - def tag_names - repo.tags.collect(&:name).sort.reverse - end - - # Returns an Array of Tags - def tags - repo.tags.sort_by(&:name).reverse - end - - # Returns an Array of branch and tag names - def ref_names - [branch_names + tag_names].flatten - end - - def heads - @heads ||= repo.heads - end - - def tree(fcommit, path = nil) - fcommit = commit if fcommit == :head - tree = fcommit.tree - path ? (tree / path) : tree - end - - def has_commits? - !!commit - rescue Grit::NoSuchPathError - false - end - - def empty? - !has_commits? - end - - # Discovers the default branch based on the repository's available branches - # - # - If no branches are present, returns nil - # - If one branch is present, returns its name - # - If two or more branches are present, returns the one that has a name - # matching root_ref (default_branch or 'master' if default_branch is nil) - def discover_default_branch - if branch_names.length == 0 - nil - elsif branch_names.length == 1 - branch_names.first - else - branch_names.select { |v| v == root_ref }.first - end - end - - # Archive Project to .tar.gz - # - # Already packed repo archives stored at - # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz - # - def archive_repo(ref) - ref = ref || self.root_ref - commit = self.commit(ref) - return nil unless commit - - # Build file path - file_name = self.path_with_namespace.gsub("/","_") + "-" + commit.id.to_s + ".tar.gz" - storage_path = Rails.root.join("tmp", "repositories") - file_path = File.join(storage_path, self.path_with_namespace, file_name) - - # Put files into a directory before archiving - prefix = File.basename(self.path_with_namespace) + "/" - - # Create file if not exists - unless File.exists?(file_path) - FileUtils.mkdir_p File.dirname(file_path) - file = self.repo.archive_to_file(ref, prefix, file_path) - end - - file_path - end - - # Return repo size in megabytes - # Cached in redis - def size - Rails.cache.fetch(cache_key(:size)) do - size = popen('du -s', path_to_repo).first.strip.to_i - (size.to_f / 1024).round(2) - end - end - - def expire_cache - Rails.cache.delete(cache_key(:size)) - end - - def cache_key(type) - "#{type}:#{path_with_namespace}" - end - end - end -end diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index ad6ba3e8..762eb372 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -187,7 +187,7 @@ module Gitlab def reference_commit(identifier) if @project.valid_repo? && commit = @project.repository.commit(identifier) - link_to(identifier, project_commit_url(@project, commit), html_options.merge(title: commit.link_title, class: "gfm gfm-commit #{html_options[:class]}")) + link_to(identifier, project_commit_url(@project, commit), html_options.merge(title: CommitDecorator.new(commit).link_title, class: "gfm gfm-commit #{html_options[:class]}")) end end end diff --git a/lib/gitolited.rb b/lib/gitolited.rb new file mode 100644 index 00000000..a7fc4148 --- /dev/null +++ b/lib/gitolited.rb @@ -0,0 +1,11 @@ +# == Gitolited mixin +# +# Provide a shortcut to Gitlab::Shell instance by gitlab_shell +# +# Used by Project, UsersProject, etc +# +module Gitolited + def gitlab_shell + Gitlab::Shell.new + end +end diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake deleted file mode 100644 index 8320b9b2..00000000 --- a/lib/tasks/cache.rake +++ /dev/null @@ -1,6 +0,0 @@ -namespace :cache do - desc "GITLAB | Clear redis cache" - task :clear => :environment do - Rails.cache.clear - end -end diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index c8d8d531..855227fb 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -637,10 +637,10 @@ namespace :gitlab do def check_gitlab_shell print "GitLab Shell version? ... " - if gitlab_shell_version.strip == '1.2.0' - puts 'OK (1.2.0)'.green + if gitlab_shell_version.strip == '1.1.0' + puts 'OK (1.1.0)'.green else - puts 'FAIL. Please update gitlab-shell to v1.2.0'.red + puts 'FAIL. Please update gitlab-shell to v1.1.0'.red end end end diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb index 5fffbf0e..7bf13822 100644 --- a/spec/controllers/commit_controller_spec.rb +++ b/spec/controllers/commit_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe CommitController do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } let(:user) { create(:user) } let(:commit) { project.repository.last_commit_for("master") } diff --git a/spec/controllers/commits_controller_spec.rb b/spec/controllers/commits_controller_spec.rb index ce402917..99cbcd13 100644 --- a/spec/controllers/commits_controller_spec.rb +++ b/spec/controllers/commits_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe CommitsController do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } let(:user) { create(:user) } before do diff --git a/spec/controllers/merge_requests_controller_spec.rb b/spec/controllers/merge_requests_controller_spec.rb index e8dd9bf9..37e36efc 100644 --- a/spec/controllers/merge_requests_controller_spec.rb +++ b/spec/controllers/merge_requests_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe MergeRequestsController do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } let(:user) { create(:user) } let(:merge_request) { create(:merge_request_with_diffs, project: project, target_branch: "bcf03b5d~3", source_branch: "bcf03b5d") } diff --git a/spec/controllers/tree_controller_spec.rb b/spec/controllers/tree_controller_spec.rb index 8232f147..81c7656d 100644 --- a/spec/controllers/tree_controller_spec.rb +++ b/spec/controllers/tree_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe TreeController do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } let(:user) { create(:user) } before do diff --git a/spec/factories.rb b/spec/factories.rb index 3205cabd..41766859 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -34,10 +34,6 @@ FactoryGirl.define do issues_tracker_id { "project_name_in_redmine" } end - factory :project_with_code, parent: :project do - path { 'gitlabhq' } - end - factory :group do sequence(:name) { |n| "group#{n}" } path { name.downcase.gsub(/\s/, '_') } @@ -77,7 +73,7 @@ FactoryGirl.define do factory :merge_request do title author - project factory: :project_with_code + project source_branch "master" target_branch "stable" @@ -86,9 +82,9 @@ FactoryGirl.define do target_branch "master" # pretend bcf03b5d~3 source_branch "stable" # pretend bcf03b5d st_commits do - [Commit.new(project.repository.commit('bcf03b5d')), - Commit.new(project.repository.commit('bcf03b5d~1')), - Commit.new(project.repository.commit('bcf03b5d~2'))] + [Commit.new(project.repo.commit('bcf03b5d')), + Commit.new(project.repo.commit('bcf03b5d~1')), + Commit.new(project.repo.commit('bcf03b5d~2'))] end st_diffs do project.repo.diff("bcf03b5d~3", "bcf03b5d") @@ -120,7 +116,6 @@ FactoryGirl.define do factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff] trait :on_commit do - project factory: :project_with_code commit_id "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" noteable_type "Commit" end @@ -130,7 +125,6 @@ FactoryGirl.define do end trait :on_merge_request do - project factory: :project_with_code noteable_id 1 noteable_type "MergeRequest" end diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb index 102a1b92..bc0586b2 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/features/admin/admin_hooks_spec.rb @@ -12,7 +12,7 @@ describe "Admin::Hooks" do describe "GET /admin/hooks" do it "should be ok" do visit admin_root_path - within ".main-nav" do + within ".main_menu" do click_on "Hooks" end current_path.should == admin_hooks_path diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 23370891..c9ddf1f4 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -31,4 +31,46 @@ describe "Admin::Projects" do page.should have_content(@project.name) end end + + describe "GET /admin/projects/:id/edit" do + before do + visit admin_projects_path + click_link "edit_project_#{@project.id}" + end + + it "should have project edit page" do + page.should have_content("Edit project") + page.should have_button("Save Project") + end + + describe "Update project" do + before do + fill_in "project_name", with: "Big Bang" + click_button "Save Project" + @project.reload + end + + it "should show page with new data" do + page.should have_content("Big Bang") + end + + it "should change project entry" do + @project.name.should == "Big Bang" + end + end + end + + describe "Add new team member" do + before do + @new_user = create(:user) + visit admin_project_path(@project) + end + + it "should create new user" do + select @new_user.name, from: "user_ids" + expect { click_button "Add" }.to change { UsersProject.count }.by(1) + page.should have_content @new_user.name + current_path.should == admin_project_path(@project) + end + end end diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb index 653ff865..a57e34ac 100644 --- a/spec/features/gitlab_flavored_markdown_spec.rb +++ b/spec/features/gitlab_flavored_markdown_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe "Gitlab Flavored Markdown" do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } let(:issue) { create(:issue, project: project) } let(:merge_request) { create(:merge_request, project: project) } let(:fred) do diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index 82f44279..670762e8 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe "On a merge request", js: true do - let!(:project) { create(:project_with_code) } + let!(:project) { create(:project) } let!(:merge_request) { create(:merge_request, project: project) } before do @@ -21,6 +21,11 @@ describe "On a merge request", js: true do it { find(".js-main-target-form input[type=submit]").value.should == "Add Comment" } it { within(".js-main-target-form") { should_not have_link("Cancel") } } + # notifiactions + it { within(".js-main-target-form") { should have_unchecked_field("Notify team via email") } } + it { within(".js-main-target-form") { should_not have_checked_field("Notify commit author") } } + it { within(".js-main-target-form") { should_not have_unchecked_field("Notify commit author") } } + describe "without text" do it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } } end @@ -83,7 +88,7 @@ end describe "On a merge request diff", js: true, focus: true do - let!(:project) { create(:project_with_code) } + let!(:project) { create(:project) } let!(:merge_request) { create(:merge_request_with_diffs, project: project) } before do @@ -121,6 +126,9 @@ describe "On a merge request diff", js: true, focus: true do it { should have_button("Add Comment") } it { should have_css(".js-close-discussion-note-form", text: "Cancel") } + # notification options + it { should have_unchecked_field("Notify team via email") } + it "shouldn't add a second form for same row" do find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder .js-add-diff-note-button").trigger("click") diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index 51b95c25..c18d8f92 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -12,9 +12,8 @@ describe "Profile account page" do Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) visit account_profile_path end - it { page.should have_content("Remove account") } - + it "should delete the account", js: true do expect { click_link "Delete account" }.to change {User.count}.by(-1) current_path.should == new_user_session_path @@ -46,4 +45,4 @@ describe "Profile account page" do current_path.should == account_profile_path end end -end +end \ No newline at end of file diff --git a/spec/features/security/project_access_spec.rb b/spec/features/security/project_access_spec.rb index cfbb8f13..b8984401 100644 --- a/spec/features/security/project_access_spec.rb +++ b/spec/features/security/project_access_spec.rb @@ -14,7 +14,7 @@ describe "Application access" do end describe "Project" do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } let(:master) { create(:user) } let(:guest) { create(:user) } @@ -33,7 +33,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -44,7 +44,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -55,7 +55,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -66,7 +66,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -77,7 +77,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -88,7 +88,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -99,7 +99,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -114,7 +114,7 @@ describe "Application access" do it { @blob_path.should be_allowed_for master } it { @blob_path.should be_allowed_for reporter } - it { @blob_path.should be_allowed_for :admin } + it { @blob_path.should be_denied_for :admin } it { @blob_path.should be_denied_for guest } it { @blob_path.should be_denied_for :user } it { @blob_path.should be_denied_for :visitor } @@ -125,7 +125,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_denied_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -136,7 +136,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_denied_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -147,7 +147,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -158,7 +158,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -169,7 +169,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -180,7 +180,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -196,7 +196,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -212,7 +212,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } @@ -223,7 +223,7 @@ describe "Application access" do it { should be_allowed_for master } it { should be_allowed_for reporter } - it { should be_allowed_for :admin } + it { should be_denied_for :admin } it { should be_denied_for guest } it { should be_denied_for :user } it { should be_denied_for :visitor } diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index 4140ba48..b9025026 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -4,10 +4,10 @@ describe GitlabMarkdownHelper do include ApplicationHelper include IssuesHelper - let!(:project) { create(:project_with_code) } + let!(:project) { create(:project) } let(:user) { create(:user, username: 'gfm') } - let(:commit) { project.repository.commit } + let(:commit) { CommitDecorator.decorate(project.repository.commit) } let(:issue) { create(:issue, project: project) } let(:merge_request) { create(:merge_request, project: project) } let(:snippet) { create(:snippet, project: project) } diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index a1f23073..c9eb6591 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -76,31 +76,4 @@ describe IssuesHelper do url_for_issue(issue.id).should eq "" end end - - describe :url_for_new_issue do - let(:issues_url) { Gitlab.config.issues_tracker.redmine.new_issue_url} - let(:ext_expected) do - issues_url.gsub(':project_id', ext_project.id.to_s) - .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s) - end - let(:int_expected) { new_project_issue_path(project) } - - it "should return internal path if used internal tracker" do - @project = project - url_for_new_issue.should match(int_expected) - end - - it "should return path to external tracker" do - @project = ext_project - - url_for_new_issue.should match(ext_expected) - end - - it "should return empty string if project nil" do - @project = nil - - url_for_new_issue.should eq "" - end - end - end diff --git a/spec/helpers/notifications_helper_spec.rb b/spec/helpers/notifications_helper_spec.rb deleted file mode 100644 index f97959ee..00000000 --- a/spec/helpers/notifications_helper_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -# Specs in this file have access to a helper object that includes -# the NotificationsHelper. For example: -# -# describe NotificationsHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# helper.concat_strings("this","that").should == "this that" -# end -# end -# end -describe NotificationsHelper do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index aac72c63..ee20ae79 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -54,5 +54,47 @@ describe ExtractsPath do extract_ref('stable/CHANGELOG').should == ['stable', 'CHANGELOG'] end end + + context "with a fullpath" do + it "extracts a valid branch" do + extract_ref('/gitlab/gitlab-ci/tree/foo/bar/baz/CHANGELOG').should == ['foo/bar/baz', 'CHANGELOG'] + end + + it "extracts a valid tag" do + extract_ref('/gitlab/gitlab-ci/tree/v2.0.0/CHANGELOG').should == ['v2.0.0', 'CHANGELOG'] + end + + it "extracts a valid commit SHA" do + extract_ref('/gitlab/gitlab-ci/tree/f4b14494ef6abf3d144c28e4af0c20143383e062/CHANGELOG').should == + ['f4b14494ef6abf3d144c28e4af0c20143383e062', 'CHANGELOG'] + end + + it "extracts a timestamp" do + extract_ref('/gitlab/gitlab-ci/tree/v2.0.0/CHANGELOG?_=12354435').should == ['v2.0.0', 'CHANGELOG'] + end + end + + context "with a fullpath and a relative_url_root" do + before do + Gitlab.config.gitlab.stub(relative_url_root: '/relative') + end + + it "extracts a valid branch with relative_url_root" do + extract_ref('/relative/gitlab/gitlab-ci/tree/foo/bar/baz/CHANGELOG').should == ['foo/bar/baz', 'CHANGELOG'] + end + + it "extracts a valid tag" do + extract_ref('/relative/gitlab/gitlab-ci/tree/v2.0.0/CHANGELOG').should == ['v2.0.0', 'CHANGELOG'] + end + + it "extracts a valid commit SHA" do + extract_ref('/relative/gitlab/gitlab-ci/tree/f4b14494ef6abf3d144c28e4af0c20143383e062/CHANGELOG').should == + ['f4b14494ef6abf3d144c28e4af0c20143383e062', 'CHANGELOG'] + end + + it "extracts a timestamp" do + extract_ref('/relative/gitlab/gitlab-ci/tree/v2.0.0/CHANGELOG?_=12354435').should == ['v2.0.0', 'CHANGELOG'] + end + end end end diff --git a/spec/lib/git/commit_spec.rb b/spec/lib/git/commit_spec.rb deleted file mode 100644 index 475bc359..00000000 --- a/spec/lib/git/commit_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require "spec_helper" - -describe Gitlab::Git::Commit do - let(:commit) { create(:project_with_code).repository.commit } - - describe "Commit info" do - before do - @committer = double( - email: 'mike@smith.com', - name: 'Mike Smith' - ) - - @author = double( - email: 'john@smith.com', - name: 'John Smith' - ) - - @raw_commit = double( - id: "bcf03b5de6abcf03b5de6c", - author: @author, - committer: @committer, - committed_date: Date.yesterday, - message: 'Refactoring specs' - ) - - @commit = Gitlab::Git::Commit.new(@raw_commit) - end - - it { @commit.short_id.should == "bcf03b5de6a" } - it { @commit.safe_message.should == @raw_commit.message } - it { @commit.created_at.should == @raw_commit.committed_date } - it { @commit.author_email.should == @author.email } - it { @commit.author_name.should == @author.name } - it { @commit.committer_name.should == @committer.name } - it { @commit.committer_email.should == @committer.email } - it { @commit.different_committer?.should be_true } - end - - describe "Class methods" do - subject { Gitlab::Git::Commit } - - it { should respond_to(:find_or_first) } - it { should respond_to(:fresh_commits) } - it { should respond_to(:commits_with_refs) } - it { should respond_to(:commits_since) } - it { should respond_to(:commits_between) } - it { should respond_to(:commits) } - end -end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 84ce7e86..7867c4dd 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -5,7 +5,7 @@ describe Notify do include EmailSpec::Matchers let(:recipient) { create(:user, email: 'recipient@example.com') } - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } shared_examples 'a multiple recipients email' do it 'is sent to the given recipient' do @@ -107,7 +107,7 @@ describe Notify do let(:issue) { create(:issue, assignee: assignee, project: project ) } describe 'that are new' do - subject { Notify.new_issue_email(issue.assignee_id, issue.id) } + subject { Notify.new_issue_email(issue.id) } it_behaves_like 'an assignee email' @@ -172,7 +172,7 @@ describe Notify do let(:merge_request) { create(:merge_request, assignee: assignee, project: project) } describe 'that are new' do - subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) } + subject { Notify.new_merge_request_email(merge_request.id) } it_behaves_like 'an assignee email' @@ -277,7 +277,14 @@ describe Notify do end describe 'on a commit' do - let(:commit) { project.repository.commit } + let(:commit) do + mock(:commit).tap do |commit| + commit.stub(:id).and_return('fauxsha1') + commit.stub(:project).and_return(project) + commit.stub(:short_id).and_return('fauxsha1') + commit.stub(:safe_message).and_return('some message') + end + end before(:each) { note.stub(:noteable).and_return(commit) } @@ -290,7 +297,7 @@ describe Notify do end it 'contains a link to the commit' do - should have_body_text commit.short_id + should have_body_text /fauxsha1/ end end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 6cf777be..91301029 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -1,35 +1,83 @@ require 'spec_helper' describe Commit do - let(:commit) { create(:project_with_code).repository.commit } + let(:commit) { create(:project).repository.commit } + describe CommitDecorator do + let(:decorator) { CommitDecorator.new(commit) } - describe '#title' do - it "returns no_commit_message when safe_message is blank" do - commit.stub(:safe_message).and_return('') - commit.title.should == "--no commit message" + describe '#title' do + it "returns no_commit_message when safe_message is blank" do + decorator.stub(:safe_message).and_return('') + decorator.title.should == "--no commit message" + end + + it "truncates a message without a newline at 70 characters" do + message = commit.safe_message * 10 + + decorator.stub(:safe_message).and_return(message) + decorator.title.should == "#{message[0..69]}…" + end + + it "truncates a message with a newline before 80 characters at the newline" do + message = commit.safe_message.split(" ").first + + decorator.stub(:safe_message).and_return(message + "\n" + message) + decorator.title.should == message + end + + it "truncates a message with a newline after 80 characters at 70 characters" do + message = (commit.safe_message * 10) + "\n" + + decorator.stub(:safe_message).and_return(message) + decorator.title.should == "#{message[0..69]}…" + end + end + end + + describe "Commit info" do + before do + @committer = double( + email: 'mike@smith.com', + name: 'Mike Smith' + ) + + @author = double( + email: 'john@smith.com', + name: 'John Smith' + ) + + @raw_commit = double( + id: "bcf03b5de6abcf03b5de6c", + author: @author, + committer: @committer, + committed_date: Date.yesterday, + message: 'Refactoring specs' + ) + + @commit = Commit.new(@raw_commit) end - it "truncates a message without a newline at 70 characters" do - message = commit.safe_message * 10 + it { @commit.short_id.should == "bcf03b5de6a" } + it { @commit.safe_message.should == @raw_commit.message } + it { @commit.created_at.should == @raw_commit.committed_date } + it { @commit.author_email.should == @author.email } + it { @commit.author_name.should == @author.name } + it { @commit.committer_name.should == @committer.name } + it { @commit.committer_email.should == @committer.email } + it { @commit.different_committer?.should be_true } + end - commit.stub(:safe_message).and_return(message) - commit.title.should == "#{message[0..69]}…" - end + describe "Class methods" do + subject { Commit } - it "truncates a message with a newline before 80 characters at the newline" do - message = commit.safe_message.split(" ").first - - commit.stub(:safe_message).and_return(message + "\n" + message) - commit.title.should == message - end - - it "truncates a message with a newline after 80 characters at 70 characters" do - message = (commit.safe_message * 10) + "\n" - - commit.stub(:safe_message).and_return(message) - commit.title.should == "#{message[0..69]}…" - end + it { should respond_to(:find_or_first) } + it { should respond_to(:fresh_commits) } + it { should respond_to(:commits_with_refs) } + it { should respond_to(:commits_since) } + it { should respond_to(:commits_between) } + it { should respond_to(:commits) } + it { should respond_to(:compare) } end describe "delegation" do diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 85bdf08a..cc789939 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -72,7 +72,6 @@ describe Event do before { Event.should_receive :create - observer.stub(notification: stub.as_null_object) } describe "Joined project team" do diff --git a/spec/models/gollum_wiki_spec.rb b/spec/models/gollum_wiki_spec.rb index aa850dfd..87601683 100644 --- a/spec/models/gollum_wiki_spec.rb +++ b/spec/models/gollum_wiki_spec.rb @@ -81,7 +81,7 @@ describe GollumWiki do end it "raises CouldNotCreateWikiError if it can't create the wiki repository" do - GollumWiki.any_instance.stub(:init_repo).and_return(false) + Gitlab::Shell.any_instance.stub(:add_repository).and_return(false) expect { GollumWiki.new(project, user).wiki }.to raise_exception(GollumWiki::CouldNotCreateWikiError) end end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index b40a0795..412e42aa 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -60,7 +60,7 @@ describe Namespace do end it "should raise error when dirtory exists" do - expect { @namespace.move_dir }.to raise_error("namespace directory cannot be moved") + expect { @namespace.move_dir }.to raise_error("Already exists") end it "should move dir if path changed" do diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index cbc7f278..9423c7de 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -18,7 +18,6 @@ # public :boolean default(FALSE), not null # issues_tracker :string(255) default("gitlab"), not null # issues_tracker_id :string(255) -# snippets_enabled :boolean default(TRUE), not null # require 'spec_helper' @@ -119,7 +118,7 @@ describe Project do end describe :update_merge_requests do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } before do @merge_request = create(:merge_request, project: project) @@ -189,6 +188,10 @@ describe Project do it "should return valid repo" do project.repository.should be_kind_of(Repository) end + + it "should return nil" do + Project.new(path: "empty").repository.should be_nil + end end describe :issue_exists? do @@ -229,7 +232,7 @@ describe Project do it "should be true for projects with external issues tracker if issues enabled" do ext_project.can_have_issues_tracker_id?.should be_true - end + end it "should be false for projects with internal issue tracker if issues enabled" do project.can_have_issues_tracker_id?.should be_false @@ -243,15 +246,4 @@ describe Project do ext_project.can_have_issues_tracker_id?.should be_false end end - - describe :open_branches do - let(:project) { create(:project_with_code) } - - before do - project.protected_branches.create(name: 'master') - end - - it { project.open_branches.map(&:name).should include('bootstrap') } - it { project.open_branches.map(&:name).should_not include('master') } - end end diff --git a/spec/lib/git/repository_spec.rb b/spec/models/repository_spec.rb similarity index 96% rename from spec/lib/git/repository_spec.rb rename to spec/models/repository_spec.rb index b2b6f196..71f9b964 100644 --- a/spec/lib/git/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1,7 +1,8 @@ require "spec_helper" -describe Gitlab::Git::Repository do - let(:repository) { Gitlab::Git::Repository.new('gitlabhq', 'master') } +describe Repository do + let(:project) { create(:project) } + let(:repository) { project.repository } describe "Respond to" do subject { repository } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 4e276dea..7d061bf2 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -33,7 +33,6 @@ # can_create_team :boolean default(TRUE), not null # state :string(255) # color_scheme_id :integer default(1), not null -# notification_level :integer default(1), not null # require 'spec_helper' diff --git a/spec/models/user_team_spec.rb b/spec/models/user_team_spec.rb index d330bbf3..6cc2003e 100644 --- a/spec/models/user_team_spec.rb +++ b/spec/models/user_team_spec.rb @@ -14,24 +14,5 @@ require 'spec_helper' describe UserTeam do - let(:team) { FactoryGirl.create :user_team } - - context ".add_member" do - let(:user) { FactoryGirl.create :user } - - it "should work" do - team.add_member(user, UsersProject::DEVELOPER, false) - team.members.should include(user) - end - end - - context ".remove_member" do - let(:user) { FactoryGirl.create :user } - before { team.add_member(user, UsersProject::DEVELOPER, false) } - - it "should work" do - team.remove_member(user) - team.members.should_not include(user) - end - end + pending "add some examples to (or delete) #{__FILE__}" end diff --git a/spec/observers/issue_observer_spec.rb b/spec/observers/issue_observer_spec.rb index 539c6550..e4e66917 100644 --- a/spec/observers/issue_observer_spec.rb +++ b/spec/observers/issue_observer_spec.rb @@ -11,9 +11,7 @@ describe IssueObserver do let(:closed_unassigned_issue) { create(:closed_issue, author: author) } - before { subject.stub(:current_user).and_return(some_user) } - before { subject.stub(notification: mock('NotificationService').as_null_object) } - + before(:each) { subject.stub(:current_user).and_return(some_user) } subject { IssueObserver.instance } @@ -27,8 +25,15 @@ describe IssueObserver do end end - it 'trigger notification to send emails' do - subject.should_receive(:notification) + it 'sends an email to the assignee' do + Notify.should_receive(:new_issue_email).with(mock_issue.id) + + subject.after_create(mock_issue) + end + + it 'does not send an email to the assignee if assignee created the issue' do + subject.stub(:current_user).and_return(assignee) + Notify.should_not_receive(:new_issue_email) subject.after_create(mock_issue) end @@ -42,14 +47,16 @@ describe IssueObserver do assigned_issue.close end - it 'trigger notification to send emails' do - subject.should_receive(:notification) + it 'notification is delivered if the issue being closed' do + Notify.should_receive(:issue_status_changed_email).twice assigned_issue.close end - it 'creates a note' do + it 'notification is delivered only to author if the issue being closed' do + Notify.should_receive(:issue_status_changed_email).once Note.should_receive(:create_status_change_note).with(unassigned_issue, some_user, 'closed') + unassigned_issue.close end end @@ -61,13 +68,14 @@ describe IssueObserver do closed_assigned_issue.reopen end - it 'trigger notification to send emails' do - subject.should_receive(:notification) + it 'notification is delivered if the issue being reopened' do + Notify.should_receive(:issue_status_changed_email).twice closed_assigned_issue.reopen end - it 'create a note' do + it 'notification is delivered only to author if the issue being reopened' do + Notify.should_receive(:issue_status_changed_email).once Note.should_receive(:create_status_change_note).with(closed_unassigned_issue, some_user, 'reopened') closed_unassigned_issue.reopen @@ -90,20 +98,61 @@ describe IssueObserver do end end - context 'notification' do - it 'triggered if the issue is being reassigned' do + context 'a reassigned email' do + it 'is sent if the issue is being reassigned' do mock_issue.should_receive(:is_being_reassigned?).and_return(true) - subject.should_receive(:notification) + subject.should_receive(:send_reassigned_email).with(mock_issue) subject.after_update(mock_issue) end - it 'is not triggered if the issue is not being reassigned' do + it 'is not sent if the issue is not being reassigned' do mock_issue.should_receive(:is_being_reassigned?).and_return(false) - subject.should_not_receive(:notification) + subject.should_not_receive(:send_reassigned_email) subject.after_update(mock_issue) end end end + + describe '#send_reassigned_email' do + let(:previous_assignee) { double(:user, id: 3) } + + before(:each) do + mock_issue.stub(:assignee_id).and_return(assignee.id) + mock_issue.stub(:assignee_id_was).and_return(previous_assignee.id) + end + + def it_sends_a_reassigned_email_to(recipient) + Notify.should_receive(:reassigned_issue_email).with(recipient, mock_issue.id, previous_assignee.id) + end + + def it_does_not_send_a_reassigned_email_to(recipient) + Notify.should_not_receive(:reassigned_issue_email).with(recipient, mock_issue.id, previous_assignee.id) + end + + it 'sends a reassigned email to the previous and current assignees' do + it_sends_a_reassigned_email_to assignee.id + it_sends_a_reassigned_email_to previous_assignee.id + + subject.send(:send_reassigned_email, mock_issue) + end + + context 'does not send an email to the user who made the reassignment' do + it 'if the user is the assignee' do + subject.stub(:current_user).and_return(assignee) + it_sends_a_reassigned_email_to previous_assignee.id + it_does_not_send_a_reassigned_email_to assignee.id + + subject.send(:send_reassigned_email, mock_issue) + end + it 'if the user is the previous assignee' do + subject.stub(:current_user).and_return(previous_assignee) + it_sends_a_reassigned_email_to assignee.id + it_does_not_send_a_reassigned_email_to previous_assignee.id + + subject.send(:send_reassigned_email, mock_issue) + end + end + end end diff --git a/spec/observers/merge_request_observer_spec.rb b/spec/observers/merge_request_observer_spec.rb index 2593da72..9d702107 100644 --- a/spec/observers/merge_request_observer_spec.rb +++ b/spec/observers/merge_request_observer_spec.rb @@ -10,8 +10,7 @@ describe MergeRequestObserver do let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author) } let(:closed_unassigned_mr) { create(:closed_merge_request, author: author) } - before { subject.stub(:current_user).and_return(some_user) } - before { subject.stub(notification: mock('NotificationService').as_null_object) } + before(:each) { subject.stub(:current_user).and_return(some_user) } subject { MergeRequestObserver.instance } @@ -19,11 +18,21 @@ describe MergeRequestObserver do it 'is called when a merge request is created' do subject.should_receive(:after_create) - create(:merge_request, project: create(:project)) + + MergeRequest.observers.enable :merge_request_observer do + create(:merge_request, project: create(:project)) + end end - it 'trigger notification service' do - subject.should_receive(:notification) + it 'sends an email to the assignee' do + Notify.should_receive(:new_merge_request_email).with(mr_mock.id) + subject.after_create(mr_mock) + end + + it 'does not send an email to the assignee if assignee created the merge request' do + subject.stub(:current_user).and_return(assignee) + Notify.should_not_receive(:new_merge_request_email) + subject.after_create(mr_mock) end end @@ -43,21 +52,22 @@ describe MergeRequestObserver do end end - context 'a notification' do + context 'a reassigned email' do it 'is sent if the merge request is being reassigned' do mr_mock.should_receive(:is_being_reassigned?).and_return(true) - subject.should_receive(:notification) + subject.should_receive(:send_reassigned_email).with(mr_mock) subject.after_update(mr_mock) end it 'is not sent if the merge request is not being reassigned' do mr_mock.should_receive(:is_being_reassigned?).and_return(false) - subject.should_not_receive(:notification) + subject.should_not_receive(:send_reassigned_email) subject.after_update(mr_mock) end end + end context '#after_close' do @@ -91,4 +101,45 @@ describe MergeRequestObserver do end end end + + describe '#send_reassigned_email' do + let(:previous_assignee) { double(:user, id: 3) } + + before(:each) do + mr_mock.stub(:assignee_id).and_return(assignee.id) + mr_mock.stub(:assignee_id_was).and_return(previous_assignee.id) + end + + def it_sends_a_reassigned_email_to(recipient) + Notify.should_receive(:reassigned_merge_request_email).with(recipient, mr_mock.id, previous_assignee.id) + end + + def it_does_not_send_a_reassigned_email_to(recipient) + Notify.should_not_receive(:reassigned_merge_request_email).with(recipient, mr_mock.id, previous_assignee.id) + end + + it 'sends a reassigned email to the previous and current assignees' do + it_sends_a_reassigned_email_to assignee.id + it_sends_a_reassigned_email_to previous_assignee.id + + subject.send(:send_reassigned_email, mr_mock) + end + + context 'does not send an email to the user who made the reassignment' do + it 'if the user is the assignee' do + subject.stub(:current_user).and_return(assignee) + it_sends_a_reassigned_email_to previous_assignee.id + it_does_not_send_a_reassigned_email_to assignee.id + + subject.send(:send_reassigned_email, mr_mock) + end + it 'if the user is the previous assignee' do + subject.stub(:current_user).and_return(previous_assignee) + it_sends_a_reassigned_email_to assignee.id + it_does_not_send_a_reassigned_email_to previous_assignee.id + + subject.send(:send_reassigned_email, mr_mock) + end + end + end end diff --git a/spec/observers/note_observer_spec.rb b/spec/observers/note_observer_spec.rb index 9ada9270..8ad42c21 100644 --- a/spec/observers/note_observer_spec.rb +++ b/spec/observers/note_observer_spec.rb @@ -2,7 +2,6 @@ require 'spec_helper' describe NoteObserver do subject { NoteObserver.instance } - before { subject.stub(notification: mock('NotificationService').as_null_object) } let(:team_without_author) { (1..2).map { |n| double :user, id: n } } @@ -18,9 +17,116 @@ describe NoteObserver do end it 'sends out notifications' do - subject.should_receive(:notification) + subject.should_receive(:send_notify_mails).with(note) subject.after_create(note) end end + + describe "#send_notify_mails" do + let(:note) { double :note, notify: false, notify_author: false } + + it 'notifies team of new note when flagged to notify' do + note.stub(:notify).and_return(true) + subject.should_receive(:notify_team).with(note) + + subject.after_create(note) + end + + it 'does not notify team of new note when not flagged to notify' do + subject.should_not_receive(:notify_team).with(note) + + subject.after_create(note) + end + + it 'notifies the author of a commit when flagged to notify the author' do + note.stub(:notify_author).and_return(true) + note.stub(:noteable).and_return(double(author_email: 'test@test.com')) + note.stub(:id).and_return(42) + author = double :user, id: 1, email: 'test@test.com' + note.stub(:commit_author).and_return(author) + Notify.should_receive(:note_commit_email) + + subject.after_create(note) + end + + it 'does not notify the author of a commit when not flagged to notify the author' do + notify.should_not_receive(:note_commit_email) + + subject.after_create(note) + end + + it 'does nothing if no notify flags are set' do + subject.after_create(note).should be_nil + end + end + + describe '#notify_team' do + let(:note) { double :note, id: 1 } + + before :each do + subject.stub(:team_without_note_author).with(note).and_return(team_without_author) + end + + context 'notifies team of a new note on' do + it 'a commit' do + note.stub(:noteable_type).and_return('Commit') + notify.should_receive(:note_commit_email).twice + + subject.send(:notify_team, note) + end + + it 'an issue' do + note.stub(:noteable_type).and_return('Issue') + notify.should_receive(:note_issue_email).twice + + subject.send(:notify_team, note) + end + + it 'a wiki page' do + note.stub(:noteable_type).and_return('Wiki') + notify.should_receive(:note_wiki_email).twice + + subject.send(:notify_team, note) + end + + it 'a merge request' do + note.stub(:noteable_type).and_return('MergeRequest') + notify.should_receive(:note_merge_request_email).twice + + subject.send(:notify_team, note) + end + + it 'a wall' do + # Note: wall posts have #noteable_type of nil + note.stub(:noteable_type).and_return(nil) + notify.should_receive(:note_wall_email).twice + + subject.send(:notify_team, note) + end + end + + it 'does nothing for a new note on a snippet' do + note.stub(:noteable_type).and_return('Snippet') + + subject.send(:notify_team, note).should be_nil + end + end + + + describe '#team_without_note_author' do + let(:author) { double :user, id: 4 } + + let(:users) { team_without_author + [author] } + let(:project) { double :project, users: users } + let(:note) { double :note, project: project, author: author } + + it 'returns the projects user without the note author included' do + subject.send(:team_without_note_author, note).should == team_without_author + end + end + + def notify + Notify + end end diff --git a/spec/observers/user_observer_spec.rb b/spec/observers/user_observer_spec.rb index 5b735a8f..b58c5647 100644 --- a/spec/observers/user_observer_spec.rb +++ b/spec/observers/user_observer_spec.rb @@ -2,7 +2,6 @@ require 'spec_helper' describe UserObserver do subject { UserObserver.instance } - before { subject.stub(notification: mock('NotificationService').as_null_object) } it 'calls #after_create when new users are created' do new_user = build(:user) @@ -12,10 +11,15 @@ describe UserObserver do context 'when a new user is created' do it 'sends an email' do - subject.should_receive(:notification) + Notify.should_receive(:new_user_email) create(:user) end + it 'no email for external' do + Notify.should_not_receive(:new_user_email) + create(:user, extern_uid: '32442eEfsafada') + end + it 'trigger logger' do user = double(:user, id: 42, password: 'P@ssword!', name: 'John', email: 'u@mail.local', extern_uid?: false) Gitlab::AppLogger.should_receive(:info) diff --git a/spec/observers/users_project_observer_spec.rb b/spec/observers/users_project_observer_spec.rb index c034501e..068688b0 100644 --- a/spec/observers/users_project_observer_spec.rb +++ b/spec/observers/users_project_observer_spec.rb @@ -4,7 +4,6 @@ describe UsersProjectObserver do let(:user) { create(:user) } let(:project) { create(:project) } subject { UsersProjectObserver.instance } - before { subject.stub(notification: mock('NotificationService').as_null_object) } describe "#after_commit" do it "should called when UsersProject created" do @@ -13,7 +12,7 @@ describe UsersProjectObserver do end it "should send email to user" do - subject.should_receive(:notification) + Notify.should_receive(:project_access_granted_email).and_return(double(deliver: true)) Event.stub(:create => true) create(:users_project) @@ -37,7 +36,7 @@ describe UsersProjectObserver do end it "should send email to user" do - subject.should_receive(:notification) + Notify.should_receive(:project_access_granted_email) @users_project.update_attribute(:project_access, UsersProject::MASTER) end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 25bbd986..e7af056a 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::API do include ApiHelpers let(:user) { create(:user ) } - let!(:project) { create(:project_with_code, creator_id: user.id) } + let!(:project) { create(:project, namespace: user.namespace ) } let!(:merge_request) { create(:merge_request, author: user, assignee: user, project: project, title: "Test") } before { project.team << [user, :reporters] } diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index aca3919a..cddb7264 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -7,7 +7,7 @@ describe Gitlab::API do let(:user2) { create(:user) } let(:user3) { create(:user) } let(:admin) { create(:admin) } - let!(:project) { create(:project_with_code, creator_id: user.id) } + let!(:project) { create(:project, namespace: user.namespace ) } let!(:hook) { create(:project_hook, project: project, url: "http://example.com") } let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') } let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb index b6509fcb..3e0e4bb3 100644 --- a/spec/routing/admin_routing_spec.rb +++ b/spec/routing/admin_routing_spec.rb @@ -66,13 +66,33 @@ end # PUT /admin/projects/:id(.:format) admin/projects#update {:id=>/[^\/]+/} # DELETE /admin/projects/:id(.:format) admin/projects#destroy {:id=>/[^\/]+/} describe Admin::ProjectsController, "routing" do + it "to #team" do + get("/admin/projects/gitlab/team").should route_to('admin/projects#team', id: 'gitlab') + end + + it "to #team_update" do + put("/admin/projects/gitlab/team_update").should route_to('admin/projects#team_update', id: 'gitlab') + end + it "to #index" do get("/admin/projects").should route_to('admin/projects#index') end + it "to #edit" do + get("/admin/projects/gitlab/edit").should route_to('admin/projects#edit', id: 'gitlab') + end + it "to #show" do get("/admin/projects/gitlab").should route_to('admin/projects#show', id: 'gitlab') end + + it "to #update" do + put("/admin/projects/gitlab").should route_to('admin/projects#update', id: 'gitlab') + end + + it "to #destroy" do + delete("/admin/projects/gitlab").should route_to('admin/projects#destroy', id: 'gitlab') + end end # edit_admin_project_member GET /admin/projects/:project_id/members/:id/edit(.:format) admin/projects/members#edit {:id=>/[^\/]+/, :project_id=>/[^\/]+/} diff --git a/spec/routing/notifications_routing_spec.rb b/spec/routing/notifications_routing_spec.rb deleted file mode 100644 index 6880d281..00000000 --- a/spec/routing/notifications_routing_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -require "spec_helper" - -describe NotificationsController do - describe "routing" do - it "routes to #show" do - get("/profile/notifications").should route_to("notifications#show") - end - - it "routes to #update" do - put("/profile/notifications").should route_to("notifications#update") - end - end -end diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index 286a8cda..9fc5fd62 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe GitPushService do let (:user) { create :user } - let (:project) { create :project_with_code } + let (:project) { create :project } let (:service) { GitPushService.new } before do diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb deleted file mode 100644 index fa47a635..00000000 --- a/spec/services/notification_service_spec.rb +++ /dev/null @@ -1,247 +0,0 @@ -require 'spec_helper' - -describe NotificationService do - # Disable observers to prevent factory trigger notification service - before(:all) { ActiveRecord::Base.observers.disable :all } - after(:all) { ActiveRecord::Base.observers.enable :all } - - let(:notification) { NotificationService.new } - - describe 'Keys' do - describe :new_key do - let(:key) { create(:personal_key) } - - it { notification.new_key(key).should be_true } - - it 'should sent email to key owner' do - Notify.should_receive(:new_ssh_key_email).with(key.id) - notification.new_key(key) - end - end - end - - describe 'Notes' do - context 'issue note' do - let(:issue) { create(:issue, assignee: create(:user)) } - let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id) } - - before do - build_team(note.project) - end - - describe :new_note do - it do - should_email(@u_watcher.id) - should_email(note.noteable.author_id) - should_email(note.noteable.assignee_id) - should_not_email(note.author_id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.new_note(note) - end - - def should_email(user_id) - Notify.should_receive(:note_issue_email).with(user_id, note.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:note_issue_email).with(user_id, note.id) - end - end - end - - context 'commit note' do - let(:note) { create :note_on_commit } - - before do - build_team(note.project) - end - - describe :new_note do - it do - should_email(@u_watcher.id) - should_not_email(note.author_id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.new_note(note) - end - - it do - create(:note_on_commit, - author: @u_participating, - project_id: note.project_id, - commit_id: note.commit_id) - - should_email(@u_watcher.id) - should_email(@u_participating.id) - should_not_email(note.author_id) - should_not_email(@u_disabled.id) - notification.new_note(note) - end - - def should_email(user_id) - Notify.should_receive(:note_commit_email).with(user_id, note.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:note_commit_email).with(user_id, note.id) - end - end - end - end - - describe 'Issues' do - let(:issue) { create :issue, assignee: create(:user) } - - before do - build_team(issue.project) - end - - describe :new_issue do - it do - should_email(issue.assignee_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.new_issue(issue, @u_disabled) - end - - def should_email(user_id) - Notify.should_receive(:new_issue_email).with(user_id, issue.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:new_issue_email).with(user_id, issue.id) - end - end - - describe :reassigned_issue do - it 'should email new assignee' do - should_email(issue.assignee_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - - notification.reassigned_issue(issue, @u_disabled) - end - - def should_email(user_id) - Notify.should_receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id) - end - end - - describe :close_issue do - it 'should sent email to issue assignee and issue author' do - should_email(issue.assignee_id) - should_email(issue.author_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - - notification.close_issue(issue, @u_disabled) - end - - def should_email(user_id) - Notify.should_receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id) - end - end - end - - describe 'Merge Requests' do - let(:merge_request) { create :merge_request, assignee: create(:user) } - - before do - build_team(merge_request.project) - end - - describe :new_merge_request do - it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.new_merge_request(merge_request, @u_disabled) - end - - def should_email(user_id) - Notify.should_receive(:new_merge_request_email).with(user_id, merge_request.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:new_merge_request_email).with(user_id, merge_request.id) - end - end - - describe :reassigned_merge_request do - it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.reassigned_merge_request(merge_request, merge_request.author) - end - - def should_email(user_id) - Notify.should_receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id) - end - end - - describe :closed_merge_request do - it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.close_mr(merge_request, @u_disabled) - end - - def should_email(user_id) - Notify.should_receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) - end - end - - describe :merged_merge_request do - it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.merge_mr(merge_request) - end - - def should_email(user_id) - Notify.should_receive(:merged_merge_request_email).with(user_id, merge_request.id) - end - - def should_not_email(user_id) - Notify.should_not_receive(:merged_merge_request_email).with(user_id, merge_request.id) - end - end - end - - def build_team(project) - @u_watcher = create(:user, notification_level: Notification::N_WATCH) - @u_participating = create(:user, notification_level: Notification::N_PARTICIPATING) - @u_disabled = create(:user, notification_level: Notification::N_DISABLED) - - project.team << [@u_watcher, :master] - project.team << [@u_participating, :master] - project.team << [@u_disabled, :master] - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8a01c930..30518cc2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,58 +1,46 @@ -require 'rubygems' -require 'spork' +require 'simplecov' unless ENV['CI'] -Spork.prefork do - 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 - - # 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 do - TestEnv.init - 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. +# 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' +Capybara.javascript_driver = :poltergeist +# 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 + + # 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 do + # Use tmp dir for FS manipulations + temp_repos_path = Rails.root.join('tmp', 'test-git-base-path') + Gitlab.config.gitlab_shell.stub(repos_path: temp_repos_path) + FileUtils.rm_rf temp_repos_path + FileUtils.mkdir_p temp_repos_path + end end diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb index d423ccf8..4579c971 100644 --- a/spec/support/login_helpers.rb +++ b/spec/support/login_helpers.rb @@ -12,7 +12,7 @@ module LoginHelpers # user - User instance to login with def login_with(user) visit new_user_session_path - fill_in "user_login", with: user.email + fill_in "user_email", with: user.email fill_in "user_password", with: "123456" click_button "Sign in" end diff --git a/spec/support/stubbed_repository.rb b/spec/support/stubbed_repository.rb new file mode 100644 index 00000000..6376c8d5 --- /dev/null +++ b/spec/support/stubbed_repository.rb @@ -0,0 +1,75 @@ +require "repository" +require "project" +require "merge_request" +require "shell" + +# Stubs out all Git repository access done by models so that specs can run +# against fake repositories without Grit complaining that they don't exist. +class Project + def repository + if path == "empty" || !path + nil + else + GitLabTestRepo.new(path_with_namespace) + end + end + + def satellite + FakeSatellite.new + end + + class FakeSatellite + def exists? + true + end + + def destroy + true + end + + def create + true + end + end +end + +class MergeRequest + def check_if_can_be_merged + true + end +end + +class GitLabTestRepo < Repository + def repo + @repo ||= Grit::Repo.new(Rails.root.join('tmp', 'repositories', 'gitlabhq')) + end + + # patch repo size (in mb) + def size + 12.45 + end +end + +module Gitlab + class Shell + def add_repository name + true + end + + def mv_repository name, new_name + true + end + + def remove_repository name + true + end + + def add_key id, key + true + end + + def remove_key id, key + true + end + end +end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb deleted file mode 100644 index 370094d3..00000000 --- a/spec/support/test_env.rb +++ /dev/null @@ -1,63 +0,0 @@ -module TestEnv - extend self - - # Test environment - # - # all repositories and namespaces stored at - # RAILS_APP/tmp/test-git-base-path - # - # Next shell methods are stubbed and return true - # - mv_repository - # - remove_repository - # - add_key - # - remove_key - # - def init - # Use tmp dir for FS manipulations - repos_path = Rails.root.join('tmp', 'test-git-base-path') - Gitlab.config.gitlab_shell.stub(repos_path: repos_path) - - GollumWiki.any_instance.stub(:init_repo) do |path| - create_temp_repo(File.join(repos_path, "#{path}.git")) - end - - Gitlab::Shell.any_instance.stub( - add_repository: true, - mv_repository: true, - remove_repository: true, - add_key: true, - remove_key: true - ) - - Gitlab::Satellite::Satellite.any_instance.stub( - exists?: true, - destroy: true, - create: true - ) - - MergeRequest.any_instance.stub( - check_if_can_be_merged: true - ) - - Repository.any_instance.stub( - size: 12.45 - ) - - # Remove tmp/test-git-base-path - FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path - - # Recreate tmp/test-git-base-path - FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path - - # Symlink tmp/repositories/gitlabhq to tmp/test-git-base-path/gitlabhq - seed_repo = Rails.root.join('tmp', 'repositories', 'gitlabhq') - target_repo = File.join(repos_path, 'gitlabhq.git') - system("ln -s #{seed_repo} #{target_repo}") - end - - def create_temp_repo(path) - FileUtils.mkdir_p path - command = "git init --quiet --bare #{path};" - system(command) - end -end diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index 46e86dbe..a4751bd0 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -9,7 +9,7 @@ describe PostReceive do end context "web hook" do - let(:project) { create(:project_with_code) } + let(:project) { create(:project) } let(:key) { create(:key, user: project.owner) } let(:key_id) { key.shell_id }