-# Contact & support
+# Contribute to GitLab
-If you want quick help, head over to our [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq).
-Otherwise you can follow our [Issue Submission Guide](https://github.com/gitlabhq/gitlabhq/wiki/Issue-Submission-Guide) for a more systematic and thorough guide to solving your issues.
+If you have a question or want to contribute to GitLab this guide show you the appropriate channel to use.
+## Ruling out common errors
+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.
-# Contribute to GitLab
+## 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 ...
-## Recipes
+* 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
-We collect user submitted installation scripts and config file templates for platforms we don't support officially.
-We believe there is merit in allowing a certain amount of diversity.
-You can get and submit your solution to running/configuring GitLab with your favorite OS/distro, database, web server, cloud hoster, configuration management tool, etc.
+**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.
-Help us improve the collection of [GitLab Recipes](https://github.com/gitlabhq/gitlab-recipes/)
+## Paid support
+Community support in the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) is done by volunteers. Paid support is available from [GitLab.com](http://blog.gitlab.com/services/)
## Feature suggestions
-Follow the [Issue Submission Guide](https://github.com/gitlabhq/gitlabhq/wiki/Issue-Submission-Guide) and support other peoples ideas or propose your own.
+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
+
+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
+1. Write tests and code
+1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
+1. Push the commit to your fork
+1. Submit a pull request
+
+We will accept pull requests if:
+
+* The code has proper tests and all tests pass
+* It can be merged without problems (if not please use: git rebase master)
+* It doesn't break any existing functionality
+* It's quality code that conforms to the [Rails style guide](https://github.com/bbatsov/rails-style-guide) and best practices
+* The description includes a motive for your change and the method you used to achieve it
+* It keeps the GitLab code base clean and well structured
+* We think other users will need the same functionality
+* If it makes changes to the UI the pull request should include screenshots
+
+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).
+
+## Submitting via GitHub's issue tracker
+
+* 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.
+
+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).
+
+### 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.
+
+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
+* Describe your setup (use relevant parts from `sudo -u gitlab -H bundle exec rake gitlab:env:info`)
-## Code
+## Thank you!
-Follow our [Developer Guide](https://github.com/gitlabhq/gitlabhq/wiki/Developer-Guide) to set you up for hacking on GitLab.
+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.
gem "pg", group: :postgres
# Auth
-gem "devise", "~> 2.1.0"
-gem 'omniauth', "~> 1.1.1"
+gem "devise"
+gem 'omniauth', "~> 1.1.3"
gem 'omniauth-google-oauth2'
gem 'omniauth-twitter'
gem 'omniauth-github'
-# GITLAB patched libs
-gem "grit", git: "https://github.com/gitlabhq/grit.git", ref: '9e98418ce2d654485b967003726aa2706a10060b'
-gem 'grack', git: "https://github.com/gitlabhq/grack.git", ref: 'ba46f3b0845c6a09d488ae6abdce6ede37e227e8'
-gem 'grit_ext', git: "https://github.com/gitlabhq/grit_ext.git", ref: '8e6afc2da821354774aa4d1ee8a1aa2082f84a3e'
+# Extracting information from a git repository
+gem "gitlab-grit", '~> 1.0.0', require: 'grit'
+gem 'grit_ext', '~> 0.6.2'
+
+# Ruby/Rack Git Smart-HTTP Server Handler
+gem 'gitlab-grack', '~> 1.0.0', require: 'grack'
# LDAP Auth
gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap"
gem 'gitlab_yaml_db', '1.0.0', require: "yaml_db"
# Syntax highlighter
-gem "pygments.rb", git: "https://github.com/gitlabhq/pygments.rb.git", branch: "master"
+gem "gitlab-pygments.rb", '~> 0.3.2', require: 'pygments.rb'
# Language detection
gem "github-linguist", "~> 2.3.4" , require: "linguist"
# API
-gem "grape", "~> 0.2.1"
+gem "grape", "~> 0.3.1"
+gem "grape-entity", "~> 0.2.0"
# Format dates and times
# based on human-friendly examples
gem "stamp"
+# Enumeration fields
+gem 'enumerize'
+
# Pagination
gem "kaminari", "~> 0.14.1"
# HAML
-gem "haml-rails", "~> 0.3.5"
+gem "haml-rails"
# Files attachments
-gem "carrierwave", "~> 0.7.1"
+gem "carrierwave"
# Authorization
gem "six"
gem "github-markup", "~> 0.7.4", require: 'github/markup'
# Servers
-gem "unicorn", "~> 4.4.0"
+gem "unicorn"
# State machine
gem "state_machine"
gem "acts-as-taggable-on", "2.3.3"
# Decorators
-gem "draper", "~> 0.18.0"
+gem "draper"
# Background jobs
gem 'slim'
gem 'sinatra', require: nil
-gem 'sidekiq', '2.7.3'
+gem 'sidekiq'
# HTTP requests
gem "httparty"
gem 'bootstrap-sass', "2.2.1.1"
gem "font-awesome-sass-rails", "~> 3.0.0"
gem "gemoji", "~> 1.2.1", require: 'emoji/railtie'
+ gem "gon"
end
group :development do
gem "capybara", '2.0.2'
gem "pry"
gem "awesome_print"
- gem "database_cleaner", ref: "9f898fc50d87a5d51760f9dcf374bf5ffda21baf", git: "https://github.com/bmabey/database_cleaner.git"
+ gem "database_cleaner"
gem "launchy"
gem 'factory_girl_rails'
GIT
- remote: https://github.com/bmabey/database_cleaner.git
- revision: 9f898fc50d87a5d51760f9dcf374bf5ffda21baf
- ref: 9f898fc50d87a5d51760f9dcf374bf5ffda21baf
- specs:
- database_cleaner (0.9.1)
-
-GIT
remote: https://github.com/ctran/annotate_models.git
revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e
specs:
rake (>= 0.8.7)
GIT
- remote: https://github.com/gitlabhq/grack.git
- revision: ba46f3b0845c6a09d488ae6abdce6ede37e227e8
- ref: ba46f3b0845c6a09d488ae6abdce6ede37e227e8
- specs:
- grack (1.0.0)
- rack (~> 1.4.1)
-
-GIT
- remote: https://github.com/gitlabhq/grit.git
- revision: 9e98418ce2d654485b967003726aa2706a10060b
- ref: 9e98418ce2d654485b967003726aa2706a10060b
- specs:
- grit (2.5.0)
- diff-lcs (~> 1.1)
- mime-types (~> 1.15)
- posix-spawn (~> 0.3.6)
-
-GIT
- remote: https://github.com/gitlabhq/grit_ext.git
- revision: 8e6afc2da821354774aa4d1ee8a1aa2082f84a3e
- ref: 8e6afc2da821354774aa4d1ee8a1aa2082f84a3e
- specs:
- grit_ext (0.6.1)
- charlock_holmes (~> 0.6.9)
-
-GIT
- remote: https://github.com/gitlabhq/pygments.rb.git
- revision: db1da0343adf86b49bdc3add04d02d2e80438d38
- branch: master
- specs:
- pygments.rb (0.3.2)
- posix-spawn (~> 0.3.6)
- yajl-ruby (~> 1.1.0)
-
-GIT
remote: https://github.com/gitlabhq/raphael-rails.git
revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58
specs:
addressable (2.3.2)
arel (3.0.2)
awesome_print (1.1.0)
- backports (2.6.5)
+ backports (2.6.7)
bcrypt-ruby (3.0.1)
better_errors (0.3.2)
coderay (>= 1.0.0)
erubis (>= 2.7.0)
- binding_of_caller (0.6.8)
+ binding_of_caller (0.7.1)
+ debug_inspector (>= 0.0.1)
bootstrap-sass (2.2.1.1)
sass (~> 3.2)
builder (3.0.4)
rack-test (>= 0.5.4)
selenium-webdriver (~> 2.0)
xpath (~> 1.0.0)
- carrierwave (0.7.1)
+ carrierwave (0.8.0)
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
celluloid (0.12.4)
connection_pool (1.0.0)
crack (0.3.1)
daemons (1.1.9)
- devise (2.1.2)
+ database_cleaner (0.9.1)
+ debug_inspector (0.0.2)
+ descendants_tracker (0.0.1)
+ devise (2.2.3)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.1)
railties (~> 3.1)
warden (~> 1.2.1)
diff-lcs (1.1.3)
- draper (0.18.0)
- actionpack (~> 3.2)
- activesupport (~> 3.2)
+ draper (1.1.0)
+ actionpack (>= 3.0)
+ activesupport (>= 3.0)
+ request_store (~> 1.0.3)
email_spec (1.4.0)
launchy (~> 2.1)
mail (~> 2.2)
+ enumerize (0.5.1)
+ activesupport (>= 3.2)
erubis (2.7.0)
escape_utils (0.2.4)
eventmachine (1.0.0)
factory_girl_rails (4.1.0)
factory_girl (~> 4.1.0)
railties (>= 3.0.0)
- faraday (0.8.4)
+ faraday (0.8.6)
multipart-post (~> 1.1)
faye-websocket (0.4.7)
eventmachine (>= 0.12.0)
font-awesome-sass-rails (3.0.0.1)
railties (>= 3.1.1)
sass-rails (>= 3.1.1)
- foreman (0.60.2)
+ foreman (0.61.0)
thor (>= 0.13.6)
gemoji (1.2.1)
gherkin-ruby (0.2.1)
escape_utils (~> 0.2.3)
mime-types (~> 1.19)
pygments.rb (>= 0.2.13)
- github-markup (0.7.4)
+ github-markup (0.7.5)
+ gitlab-grack (1.0.0)
+ rack (~> 1.4.1)
+ gitlab-grit (1.0.0)
+ diff-lcs (~> 1.1)
+ mime-types (~> 1.15)
+ posix-spawn (~> 0.3.6)
+ gitlab-pygments.rb (0.3.2)
+ posix-spawn (~> 0.3.6)
+ yajl-ruby (~> 1.1.0)
gitlab_meta (5.0)
gitlab_omniauth-ldap (1.0.2)
net-ldap (~> 0.2.2)
pyu-ruby-sasl (~> 0.0.3.1)
rubyntlm (~> 0.1.1)
gitlab_yaml_db (1.0.0)
- grape (0.2.2)
+ gon (4.0.2)
+ grape (0.3.2)
activesupport
- hashie (~> 1.2)
+ builder
+ hashie (>= 1.2.0)
multi_json (>= 1.3.2)
- multi_xml
+ multi_xml (>= 0.5.2)
rack
rack-accept
rack-mount
virtus
+ grape-entity (0.2.0)
+ activesupport
+ multi_json (>= 1.3.2)
+ grit_ext (0.6.2)
+ charlock_holmes (~> 0.6.9)
growl (1.0.3)
guard (1.5.4)
listen (>= 0.4.2)
guard-spinach (0.0.2)
guard (>= 1.1)
spinach
- haml (3.1.7)
- haml-rails (0.3.5)
+ haml (4.0.0)
+ tilt
+ haml-rails (0.4)
actionpack (>= 3.1, < 4.1)
activesupport (>= 3.1, < 4.1)
- haml (~> 3.1)
+ haml (>= 3.1, < 4.1)
railties (>= 3.1, < 4.1)
hashie (1.2.0)
hike (1.2.1)
http_parser.rb (0.5.3)
- httparty (0.9.0)
+ httparty (0.10.2)
multi_json (~> 1.0)
- multi_xml
+ multi_xml (>= 0.5.2)
httpauth (0.2.0)
- i18n (0.6.1)
+ i18n (0.6.4)
journey (1.0.4)
jquery-atwho-rails (0.1.7)
jquery-rails (2.1.3)
kaminari (0.14.1)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
- kgio (2.7.4)
+ kgio (2.8.0)
launchy (2.1.2)
addressable (~> 2.3)
letter_opener (1.0.0)
modernizr (2.6.2)
sprockets (~> 2.0)
multi_json (1.6.1)
- multi_xml (0.5.1)
- multipart-post (1.1.5)
+ multi_xml (0.5.3)
+ multipart-post (1.2.0)
mysql2 (0.3.11)
net-ldap (0.2.2)
nokogiri (1.5.6)
oauth (0.4.7)
- oauth2 (0.8.0)
+ oauth2 (0.8.1)
faraday (~> 0.8)
httpauth (~> 0.1)
jwt (~> 0.1.4)
multi_json (~> 1.0)
rack (~> 1.2)
- omniauth (1.1.1)
+ omniauth (1.1.3)
hashie (~> 1.2)
rack
- omniauth-github (1.0.3)
+ omniauth-github (1.1.0)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1)
omniauth-google-oauth2 (0.1.13)
coderay (~> 1.0.5)
method_source (~> 0.8)
slop (~> 3.3.1)
+ pygments.rb (0.4.2)
+ posix-spawn (~> 0.3.6)
+ yajl-ruby (~> 1.1.0)
pyu-ruby-sasl (0.0.3.3)
quiet_assets (1.0.1)
railties (~> 3.1)
rack (>= 1.1.3)
rack-mount (0.8.3)
rack (>= 1.0.0)
- rack-protection (1.3.2)
+ rack-protection (1.4.0)
rack
rack-ssl (1.3.3)
rack
rb-fsevent (0.9.2)
rb-inotify (0.8.8)
ffi (>= 0.5.0)
- rdoc (3.12.1)
+ rdoc (3.12.2)
json (~> 1.4)
redcarpet (2.2.2)
redis (3.0.2)
redis-namespace (1.2.1)
redis (~> 3.0.0)
+ request_store (1.0.5)
rspec (2.12.0)
rspec-core (~> 2.12.0)
rspec-expectations (~> 2.12.0)
multi_json (~> 1.0)
rubyzip
websocket (~> 1.0.4)
- settingslogic (2.0.8)
+ settingslogic (2.0.9)
sexp_processor (4.1.3)
shoulda-matchers (1.3.0)
activesupport (>= 3.0.0)
- sidekiq (2.7.3)
+ sidekiq (2.7.5)
celluloid (~> 0.12.0)
connection_pool (~> 1.0)
multi_json (~> 1)
multi_json (~> 1.0)
simplecov-html (~> 0.7.1)
simplecov-html (0.7.1)
- sinatra (1.3.3)
- rack (~> 1.3, >= 1.3.6)
- rack-protection (~> 1.2)
+ sinatra (1.3.5)
+ rack (~> 1.4)
+ rack-protection (~> 1.3)
tilt (~> 1.3, >= 1.3.3)
six (0.2.0)
slim (1.3.6)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
- stamp (0.3.0)
+ stamp (0.5.0)
state_machine (1.1.2)
temple (0.5.5)
test_after_commit (0.0.1)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
thor (0.17.0)
- tilt (1.3.3)
+ tilt (1.3.4)
timers (1.1.0)
treetop (1.4.12)
polyglot
uglifier (1.3.0)
execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2)
- unicorn (4.4.0)
+ unicorn (4.6.2)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
- virtus (0.5.2)
+ virtus (0.5.4)
backports (~> 2.6.1)
+ descendants_tracker (~> 0.0.1)
warden (1.2.1)
rack (>= 1.0)
webmock (1.9.0)
binding_of_caller
bootstrap-sass (= 2.2.1.1)
capybara (= 2.0.2)
- carrierwave (~> 0.7.1)
+ carrierwave
chosen-rails (= 0.9.8)
coffee-rails (~> 3.2.2)
colored
- database_cleaner!
- devise (~> 2.1.0)
- draper (~> 0.18.0)
+ database_cleaner
+ devise
+ draper
email_spec
+ enumerize
factory_girl_rails
ffaker
font-awesome-sass-rails (~> 3.0.0)
git
github-linguist (~> 2.3.4)
github-markup (~> 0.7.4)
+ gitlab-grack (~> 1.0.0)
+ gitlab-grit (~> 1.0.0)
+ gitlab-pygments.rb (~> 0.3.2)
gitlab_meta (= 5.0)
gitlab_omniauth-ldap (= 1.0.2)
gitlab_yaml_db (= 1.0.0)
- grack!
- grape (~> 0.2.1)
- grit!
- grit_ext!
+ gon
+ grape (~> 0.3.1)
+ grape-entity (~> 0.2.0)
+ grit_ext (~> 0.6.2)
growl
guard-rspec
guard-spinach
- haml-rails (~> 0.3.5)
+ haml-rails
httparty
jquery-atwho-rails (= 0.1.7)
jquery-rails (= 2.1.3)
letter_opener
modernizr (= 2.6.2)
mysql2
- omniauth (~> 1.1.1)
+ omniauth (~> 1.1.3)
omniauth-github
omniauth-google-oauth2
omniauth-twitter
pg
poltergeist (= 1.1.0)
pry
- pygments.rb!
quiet_assets (~> 1.0.1)
rack-mini-profiler
rails (= 3.2.12)
seed-fu
settingslogic
shoulda-matchers (= 1.3.0)
- sidekiq (= 2.7.3)
+ sidekiq
simplecov
sinatra
six
therubyracer
thin
uglifier (~> 1.3.0)
- unicorn (~> 4.4.0)
+ unicorn
webmock
-# Welcome to GitLab [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://travis-ci.org/gitlabhq/gitlabhq) [![build status](https://secure.travis-ci.org/gitlabhq/grit.png)](https://travis-ci.org/gitlabhq/grit) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/gitlabhq/gitlabhq) [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq)
+## GitLab: self hosted Git management software
-GitLab is a free project and repository management application
+![logo](https://raw.github.com/gitlabhq/gitlabhq/master/public/gitlab_logo.png)
-[![CI](http://ci.gitlab.org/projects/1/status?ref=master)](http://ci.gitlab.org/projects/1?ref=master)
+### GitLab allows you to
+ * keep your code secure on your own server
+ * manage repositories, users and access permissions
+ * communicate though issues, line-comments and wiki's
+ * perform code reviews with merge requests
-## Application details
+### GitLab is
-* based on Ruby on Rails
-* distributed under the MIT License
-* works with gitolite
+* powered by Ruby on Rails
+* completely free and open source (MIT license)
+* used by 10.000 organization to keep their code secure
-## Requirements
+### Code status
-* Ubuntu/Debian
+* [![build status](http://ci.gitlab.org/projects/1/status?ref=master)](http://ci.gitlab.org/projects/1?ref=master) ci.gitlab.org (master branch)
+
+* [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://travis-ci.org/gitlabhq/gitlabhq) travis-ci.org (master branch)
+
+* [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.png)](https://codeclimate.com/github/gitlabhq/gitlabhq)
+
+* [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq)
+
+### Resources
+
+* GitLab.org community site: [Homepage](http://gitlab.org) [Screenshots](http://gitlab.org/screenshots/) [Blog](http://blog.gitlab.org/) [Demo](http://demo.gitlabhq.com/users/sign_in)
+
+* GitLab.com: [Homepage](http://blog.gitlab.com/) [Hosted pricing](http://blog.gitlab.com/pricing/) [Services](http://blog.gitlab.com/services/) [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
+
+### Requirements
+
+* Ubuntu/Debian*
* ruby 1.9.3+
* MySQL
* git
-* gitolite
+* gitlab-shell
* redis
-## Install
+* More details are in the [requirements doc](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/requirements.md)
+
+### Installation
+
+You can either follow the "ordinary" Installation guide to install it on a machine or use the Vagrant virtual machine. The Installation guide is recommended to set up a production server. The Vargrant virtual machine is recommended for development since it makes it much easier to set up all the dependencies for integration testing.
+
+* [Installation guide for latest stable release](https://github.com/gitlabhq/gitlabhq/blob/4-2-stable/doc/install/installation.md)
+
+* [Installation guide for the current master branch](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md)
+
+* [Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm)
+
+### 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 with:
+
+ sudo service gitlab start
+
+ or
+
+ sudo /etc/init.d/gitlab restart
+
+2. Start it with [Foreman](https://github.com/ddollar/foreman) in development model
+
+ bundle exec foreman start -p 3000
+
+3. Start it manually in development mode
+
+ bundle exec rails s
+ bundle exec rake sidekiq:start
+
+### Running the tests
+
+* Seed the database with
+
+ bundle exec rake db:setup RAILS_ENV=test
+ bundle exec rake db:seed_fu RAILS_ENV=test
+
+* Run all tests
+
+ bundle exec rake gitlab:test
+
+* Rspec unit and functional tests
+
+ bundle exec rake spec
+
+* Spinach integration tests
+
+ bundle exec rake spinach
+
+### Getting help
+
+* [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)
+
+* [Paid support](http://blog.gitlab.com/support/)
+
+* [Paid services](http://blog.gitlab.com/services/)
+
+### 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)
-Checkout wiki pages for installation information, migration, etc.
+### Other documentation
-## Community
+* [GitLab API](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/README.md)
-[Google Group](https://groups.google.com/group/gitlabhq)
+* [Rake tasks](https://github.com/gitlabhq/gitlabhq/tree/master/doc/raketasks)
-## Contacts
+* [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes)
-Twitter:
+### Getting in touch
- * @gitlabhq
- * @dzaporozhets
+* [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md)
-Email
+* [Core team](https://github.com/gitlabhq?tab=members)
- * m@gitlabhq.com
+* [Contributors](https://github.com/gitlabhq/gitlabhq/graphs/contributors)
-## Contribute
+* [Leader](https://github.com/randx)
-[Developer Guide](https://github.com/gitlabhq/gitlabhq/wiki/Developer-Guide)
-Want to help - send a pull request.
-We'll accept good pull requests.
+* [Contact page](http://gitlab.org/contact/)
* Replace gitolite with gitlab-shell
* Usability improvements
-* Notification improvements
-
-### v4.2 February 22
-
-* Teams
-
+* Notification improvements
\ No newline at end of file
$(@).parents('form').submit()
# Flash
- if (flash = $("#flash-container")).length > 0
- flash.click -> $(@).slideUp("slow")
- flash.slideDown "slow"
- setTimeout (-> flash.slideUp("slow")), 3000
+ if (flash = $(".flash-container")).length > 0
+ flash.click -> $(@).fadeOut()
+ flash.show()
+ setTimeout (-> flash.fadeOut()), 3000
# Disable form buttons while a form is submitting
$('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
# Ref switcher
$('.project-refs-select').on 'change', ->
$(@).parents('form').submit()
+
+ $('#project_issues_enabled').change ->
+ if ($(this).is(':checked') == true)
+ $('#project_issues_tracker').removeAttr('disabled')
+ else
+ $('#project_issues_tracker').attr('disabled', 'disabled')
+
+ $('#project_issues_tracker').change()
+
+ $('#project_issues_tracker').change ->
+ if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
+ $('#project_issues_tracker_id').attr('disabled', 'disabled')
+ else
+ $('#project_issues_tracker_id').removeAttr('disabled')
+
}
/** FLASH message **/
-#flash-container {
- height: 50px;
- position: fixed;
- z-index: 10001;
- top: 0px;
- width: 100%;
- margin-bottom: 15px;
- overflow: hidden;
- background: white;
- cursor: pointer;
- border-bottom: 1px solid #ccc;
- text-align: center;
+.flash-container {
display: none;
+ .alert {
+ cursor: pointer;
+ margin: 0;
+ text-align: center;
+ border-radius: 0;
- h4 {
- color: #666;
- font-size: 18px;
- line-height: 38px;
- padding-top: 5px;
- margin: 2px;
- font-weight: normal;
+ span {
+ font-size: 14px;
+ }
}
}
}
}
-input.git_clone_url {
- width: 325px;
-}
-
.merge-request-form-holder {
select {
width: 300px;
border-color: #DDD;
}
+.well { padding: 15px; }
+
/** HELPERS **/
.nothing_here_message {
text-align: center;
line-height: 36px;
font-weight: normal;
}
+
+@mixin md-typography {
+ code { padding: 0 4px; }
+ p { font-size: 13px; }
+ h1 { font-size: 26px; line-height: 40px; margin: 10px 0;}
+ h2 { font-size: 22px; line-height: 40px; margin: 10px 0;}
+ h3 { font-size: 18px; line-height: 40px; margin: 10px 0;}
+ h4 { font-size: 16px; line-height: 20px; margin: 10px 0;}
+ h5 { font-size: 14px; line-height: 20px; margin: 10px 0;}
+ h6 { font-size: 12px; line-height: 20px; margin: 10px 0;}
+}
*
*/
.wiki {
+ @include md-typography;
+
font-size: 13px;
+ line-height: 20px;
- code { padding: 0 4px; }
- p { font-size: 13px; }
- h1 { font-size: 32px; line-height: 40px; margin: 10px 0;}
- h2 { font-size: 26px; line-height: 40px; margin: 10px 0;}
- h3 { font-size: 22px; line-height: 40px; margin: 10px 0;}
- h4 { font-size: 18px; line-height: 20px; margin: 10px 0;}
- h5 { font-size: 14px; line-height: 20px; margin: 10px 0;}
- h6 { font-size: 12px; line-height: 20px; margin: 10px 0;}
.white .highlight pre { background: #f5f5f5; }
ul { margin: 0 0 9px 25px !important; }
}
+
+.md {
+ @include md-typography;
+}
.black .highlight {
- background-color: #333;
pre {
+ background-color: #333;
color: #eee;
- background: inherit;
}
.hll { display: block; background-color: darken($hover, 65%) }
}
}
.line_content {
+ display: block;
white-space: pre;
- height: 14px;
+ height: 18px;
margin: 0px;
padding: 0px;
border: none;
color: #666;
}
.event-note {
- padding-top: 5px;
- padding-left: 5px;
- display: inline-block;
color: #555;
+ margin-top: 5px;
+ margin-left: 40px;
.note-file-attach {
- margin-left: -25px;
- float: left;
.note-image-attach {
+ margin-top: 4px;
margin-left: 0px;
max-width: 200px;
}
color: #777;
float: left;
font-size: 16px;
- line-height: 18px;
- margin: 5px;
+ line-height: 16px;
+ margin-right: 5px;
}
}
.avatar {
/* Login Page */
body.login-page{
- padding-top: 7%;
- background: #666;
+ background: #EEE;
+ .container .content { padding-top: 5%; }
}
.login-box{
margin-top: -20px;
}
.note-body {
+ @include md-typography;
margin-left: 45px;
}
.note-header {
border: 1px solid #BBB;
box-shadow: none;
margin-left: -1px;
+ background: #FFF;
}
}
@issues = case params[:status]
when issues_filter[:all] then @project.issues
when issues_filter[:closed] then @project.issues.closed
- when issues_filter[:to_me] then @project.issues.opened.assigned(current_user)
+ when issues_filter[:to_me] then @project.issues.assigned(current_user)
+ when issues_filter[:by_me] then @project.issues.authored(current_user)
else @project.issues.opened
end
@issues = @issues.tagged_with(params[:label_name]) if params[:label_name].present?
- @issues = @issues.includes(:author, :project).order("updated_at")
+ @issues = @issues.includes(:author, :project)
# Filter by specific assignee_id (or lack thereof)?
if params[:assignee_id].present?
class TestHookContext < BaseContext
def execute
hook = project.hooks.find(params[:id])
- commits = project.repository.commits(project.default_branch, nil, 3)
- data = project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", current_user)
+ data = GitPushService.new.sample_data(project, current_user)
hook.execute(data)
end
end
class Admin::Teams::MembersController < Admin::Teams::ApplicationController
def new
@users = User.potential_team_members(user_team)
- @users = UserDecorator.decorate @users
+ @users = UserDecorator.decorate_collection @users
end
def create
before_filter :add_abilities
before_filter :dev_tools if Rails.env == 'development'
before_filter :default_headers
+ before_filter :add_gon_variables
protect_from_forgery
headers['X-Frame-Options'] = 'DENY'
headers['X-XSS-Protection'] = '1; mode=block'
end
+
+ def add_gon_variables
+ gon.default_issues_tracker = Project.issues_tracker.default_value
+ end
end
@limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
@commits = @repo.commits(@ref, @path, @limit, @offset)
- @commits = CommitDecorator.decorate(@commits)
+ @commits = CommitDecorator.decorate_collection(@commits)
respond_to do |format|
format.html # index.html.erb
@refs_are_same = result[:same]
@line_notes = []
- @commits = CommitDecorator.decorate(@commits)
+ @commits = CommitDecorator.decorate_collection(@commits)
end
def create
if @merge_request.unchecked?
@merge_request.check_if_can_be_merged
end
- render json: {merge_status: @merge_request.human_merge_status}
+ render json: {merge_status: @merge_request.merge_status_name}
rescue Gitlab::SatelliteNotExistError
render json: {merge_status: :no_satellite}
end
def automerge
- return access_denied! unless can?(current_user, :accept_mr, @project)
+ return access_denied! unless allowed_to_merge?
+
if @merge_request.opened? && @merge_request.can_be_merged?
@merge_request.should_remove_source_branch = params[:should_remove_source_branch]
@merge_request.automerge!(current_user)
# Get commits from repository
# or from cache if already merged
@commits = @merge_request.commits
- @commits = CommitDecorator.decorate(@commits)
+ @commits = CommitDecorator.decorate_collection(@commits)
+
+ @allowed_to_merge = allowed_to_merge?
+ @show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge
+ end
+
+ def allowed_to_merge?
+ action = if project.protected_branch?(@merge_request.target_branch)
+ :push_code_to_protected_branches
+ else
+ :push_code
+ end
+
+ can?(current_user, action, @project)
end
end
def show
@issues = @milestone.issues
- @users = UserDecorator.decorate(@milestone.participants)
+ @users = UserDecorator.decorate_collection(@milestone.participants)
@merge_requests = @milestone.merge_requests
respond_to do |format|
def new
@users = User.potential_team_members(user_team)
- @users = UserDecorator.decorate @users
+ @users = UserDecorator.decorate_collection @users
end
def create
-class ApplicationDecorator < Draper::Base
+class ApplicationDecorator < Draper::Decorator
+ delegate_all
# Lazy Helpers
# PRO: Call Rails helpers without the h. proxy
# ex: number_to_currency(model.price)
# CON: Add a bazillion methods into your decorator's namespace
# and probably sacrifice performance/memory
- #
+ #
# Enable them by uncommenting this line:
# lazy_helpers
# Shared Decorations
# Consider defining shared methods common to all your models.
- #
+ #
# Example: standardize the formatting of timestamps
#
# def formatted_timestamp(time)
- # h.content_tag :span, time.strftime("%a %m/%d/%y"),
- # class: 'timestamp'
+ # h.content_tag :span, time.strftime("%a %m/%d/%y"),
+ # class: 'timestamp'
# end
- #
+ #
# def created_at
# formatted_timestamp(model.created_at)
# end
- #
+ #
# def updated_at
# formatted_timestamp(model.updated_at)
# end
end
end
+ def each_diff_line_near(diff, index, expected_line_code)
+ max_number_of_lines = 16
+
+ prev_match_line = nil
+ prev_lines = []
+
+ each_diff_line(diff, index) do |full_line, type, line_code, line_new, line_old|
+ line = [full_line, type, line_code, line_new, line_old]
+ if line_code != expected_line_code
+ if type == "match"
+ prev_lines.clear
+ prev_match_line = line
+ else
+ prev_lines.push(line)
+ prev_lines.shift if prev_lines.length >= max_number_of_lines
+ end
+ else
+ yield(prev_match_line) if !prev_match_line.nil?
+ prev_lines.each { |ln| yield(ln) }
+ yield(line)
+ break
+ end
+ end
+ end
+
def image_diff_class(diff)
if diff.deleted_file
"deleted"
all: "all",
closed: "closed",
to_me: "assigned-to-me",
+ by_me: "created-by-me",
open: "open"
}
end
def issues_active_milestones
@project.milestones.active.order("id desc").all
end
+
+ def url_for_project_issues
+ return "" if @project.nil?
+
+ if @project.used_default_issues_tracker?
+ project_issues_filter_path(@project)
+ else
+ 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_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 = 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)
+ end
+ end
+
+ def title_for_issue(issue_id)
+ return "" if @project.nil?
+
+ if @project.used_default_issues_tracker? && issue = @project.issues.where(id: issue_id).first
+ issue.title
+ else
+ ""
+ end
+ end
end
:admin_team_member,
:admin_merge_request,
:admin_note,
- :accept_mr,
:admin_wiki,
:admin_project
]
#
# Table name: namespaces
#
-# id :integer not null, primary key
-# name :string(255) not null
-# path :string(255) not null
-# owner_id :integer not null
-# created_at :datetime not null
-# updated_at :datetime not null
-# type :string(255)
+# id :integer not null, primary key
+# name :string(255) not null
+# description :string(255) not null
+# path :string(255) not null
+# owner_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# type :string(255)
#
class Group < Namespace
where('assignee_id = :user', user: user.id)
end
+ def authored(user)
+ where('author_id = :user', user: user.id)
+ end
+
def open_for(user)
opened.assigned(user)
end
attr_accessible :key, :title
before_validation :strip_white_space
- before_save :set_identifier
validates :title, presence: true, length: { within: 0..255 }
validates :key, presence: true, length: { within: 0..5000 }, format: { :with => /ssh-.{3} / }, uniqueness: true
errors.add(:key, "can't be fingerprinted") if $?.exitstatus != 0
end
- def set_identifier
- if is_deploy_key
- self.identifier = "deploy_#{Digest::MD5.hexdigest(key)}"
- else
- self.identifier = "#{user.identifier}_#{Time.now.to_i}"
- end
- end
-
def is_deploy_key
!!project_id
end
class MergeRequest < ActiveRecord::Base
include Issuable
+ BROKEN_DIFF = "--broken-diff"
+
attr_accessible :title, :assignee_id, :target_branch, :source_branch, :milestone_id,
:author_id_of_changes, :state_event
state :merged
end
- BROKEN_DIFF = "--broken-diff"
+ state_machine :merge_status, initial: :unchecked do
+ event :mark_as_unchecked do
+ transition [:can_be_merged, :cannot_be_merged] => :unchecked
+ end
+
+ event :mark_as_mergeable do
+ transition unchecked: :can_be_merged
+ end
+
+ event :mark_as_unmergeable do
+ transition unchecked: :cannot_be_merged
+ end
+
+ state :unchecked
- UNCHECKED = 1
- CAN_BE_MERGED = 2
- CANNOT_BE_MERGED = 3
+ state :can_be_merged
+
+ state :cannot_be_merged
+ end
serialize :st_commits
serialize :st_diffs
validates :source_branch, presence: true
validates :target_branch, presence: true
- validate :validate_branches
+ validate :validate_branches
scope :merged, -> { with_state(:merged) }
+ scope :by_branch, ->(branch_name) { where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name) }
+ scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
+ scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
# Closed scope for merge request should return
# both merged and closed mr's
scope :closed, -> { with_states(:closed, :merged) }
- class << self
- def find_all_by_branch(branch_name)
- where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
- end
-
- def cared(user)
- where('assignee_id = :user OR author_id = :user', user: user.id)
- end
-
- def find_all_by_branch(branch_name)
- where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
- end
-
- def find_all_by_milestone(milestone)
- where("milestone_id = :milestone_id", milestone_id: milestone)
- end
- end
-
- def human_merge_status
- merge_statuses = {
- CAN_BE_MERGED => "can_be_merged",
- CANNOT_BE_MERGED => "cannot_be_merged",
- UNCHECKED => "unchecked"
- }
- merge_statuses[self.merge_status]
- end
-
def validate_branches
if target_branch == source_branch
errors.add :base, "You can not use same branch for source and target branches"
self.reloaded_diffs
end
- def unchecked?
- merge_status == UNCHECKED
- end
-
- def mark_as_unchecked
- self.merge_status = UNCHECKED
- self.save
- end
-
- def can_be_merged?
- merge_status == CAN_BE_MERGED
- end
-
def check_if_can_be_merged
- self.merge_status = if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?
- CAN_BE_MERGED
- else
- CANNOT_BE_MERGED
- end
- self.save
+ if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?
+ mark_as_mergeable
+ else
+ mark_as_unmergeable
+ end
end
def diffs
commits.any? && opened?
end
- def mark_as_unmergable
- self.merge_status = CANNOT_BE_MERGED
- self.save
- end
-
def reloaded_commits
if opened? && unmerged_commits.any?
self.st_commits = unmerged_commits
end
def merge!(user_id)
+ self.author_id_of_changes = user_id
self.merge
-
- Event.create(
- project: self.project,
- action: Event::MERGED,
- target_id: self.id,
- target_type: "MergeRequest",
- author_id: user_id
- )
end
def automerge!(current_user)
true
end
rescue
- self.mark_as_unmergable
+ mark_as_unmergeable
false
end
#
# Table name: namespaces
#
-# id :integer not null, primary key
-# name :string(255) not null
-# path :string(255) not null
-# owner_id :integer not null
-# created_at :datetime not null
-# updated_at :datetime not null
-# type :string(255)
+# id :integer not null, primary key
+# name :string(255) not null
+# description :string(255) not null
+# path :string(255) not null
+# owner_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# type :string(255)
#
class Namespace < ActiveRecord::Base
- attr_accessible :name, :path
+ attr_accessible :name, :description, :path
has_many :projects, dependent: :destroy
belongs_to :owner, class_name: "User"
length: { within: 0..255 },
format: { with: Gitlab::Regex.name_regex,
message: "only letters, digits, spaces & '_' '-' '.' allowed." }
-
+ validates :description, length: { within: 0..255 }
validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
# creator_id :integer
# default_branch :string(255)
# issues_enabled :boolean default(TRUE), not null
+# issues_tracker :string not null
# wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null
# wiki_enabled :boolean default(TRUE), not null
class Project < ActiveRecord::Base
include Gitolited
+ extend Enumerize
class TransferError < StandardError; end
- attr_accessible :name, :path, :description, :default_branch,
- :issues_enabled, :wall_enabled, :merge_requests_enabled,
+ attr_accessible :name, :path, :description, :default_branch, :issues_tracker,
+ :issues_enabled, :wall_enabled, :merge_requests_enabled, :issues_tracker_id,
:wiki_enabled, :public, :import_url, as: [:default, :admin]
attr_accessible :namespace_id, :creator_id, as: :admin
has_many :events, dependent: :destroy
has_many :merge_requests, dependent: :destroy
- has_many :issues, dependent: :destroy, order: "state, created_at DESC"
+ has_many :issues, dependent: :destroy, order: "state DESC, created_at DESC"
has_many :milestones, dependent: :destroy
has_many :users_projects, dependent: :destroy
has_many :notes, dependent: :destroy
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] }
+ validates :issues_tracker_id, length: { within: 0..255 }
validates_uniqueness_of :name, scope: :namespace_id
validates_uniqueness_of :path, scope: :namespace_id
scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
scope :public_only, -> { where(public: true) }
+ enumerize :issues_tracker, :in => (Gitlab.config.issues_tracker.keys).append(:gitlab), :default => :gitlab
+
class << self
def abandoned
project_ids = Event.select('max(created_at) as latest_date, project_id').
issues.tag_counts_on(:labels)
end
+ def issue_exists?(issue_id)
+ if used_default_issues_tracker?
+ self.issues.where(id: issue_id).first.present?
+ else
+ true
+ end
+ end
+
+ def used_default_issues_tracker?
+ self.issues_tracker == Project.issues_tracker.default_value
+ end
+
+ def can_have_issues_tracker_id?
+ self.issues_enabled && !self.used_default_issues_tracker?
+ end
+
def services
[gitlab_ci_service].compact
end
users_projects.find_by_user_id(user_id)
end
- def transfer(new_namespace)
- Project.transaction do
- old_namespace = namespace
- self.namespace = new_namespace
-
- old_dir = old_namespace.try(:path) || ''
- new_dir = new_namespace.try(:path) || ''
-
- old_repo = if old_dir.present?
- File.join(old_dir, self.path)
- else
- self.path
- end
-
- if Project.where(path: self.path, namespace_id: new_namespace.try(:id)).present?
- raise TransferError.new("Project with same path in target namespace already exists")
- end
-
- Gitlab::ProjectMover.new(self, old_dir, new_dir).execute
-
- save!
- end
- rescue Gitlab::ProjectMover::ProjectMoveError => ex
- raise Project::TransferError.new(ex.message)
- end
-
def name_with_namespace
@name_with_namespace ||= begin
if namespace
end
end
+ def transfer(new_namespace)
+ ProjectTransferService.new.transfer(self, new_namespace)
+ end
+
def execute_hooks(data)
hooks.each { |hook| hook.async_execute(data) }
end
c_ids = self.repository.commits_between(oldrev, newrev).map(&:id)
# Update code for merge requests
- mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all
+ mrs = self.merge_requests.opened.by_branch(branch_name).all
mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
# Close merge requests
members.masters.map(&:user)
end
- def repository_readers
- repository_members[UsersProject::REPORTER]
- end
-
- def repository_writers
- repository_members[UsersProject::DEVELOPER]
- end
-
- def repository_masters
- repository_members[UsersProject::MASTER]
- end
-
- def repository_members
- keys = Hash.new {|h,k| h[k] = [] }
- UsersProject.select("keys.identifier, project_access").
- joins(user: :keys).where(project_id: project.id).
- each {|row| keys[row.project_access] << [row.identifier] }
-
- keys[UsersProject::REPORTER] += project.deploy_keys.pluck(:identifier)
- keys
- end
-
def import(source_project)
target_project = project
has_many :team_projects, through: :user_team_project_relationships
validates :name, presence: true
+ validates :email, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }
validates :bio, length: { within: 0..255 }
validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
UsersProject.where(project_id: authorized_projects.map(&:id), user_id: self.id)
end
- # Returns a string for use as a Gitolite user identifier
- #
- # Note that Gitolite 2.x requires the following pattern for users:
- #
- # ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$
- def identifier
- # Replace non-word chars with underscores, then make sure it starts with
- # valid chars
- email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '')
- end
-
def is_admin?
admin
end
#
class UserTeam < ActiveRecord::Base
- attr_accessible :name, :owner_id, :path
+ attr_accessible :name, :description, :owner_id, :path
belongs_to :owner, class_name: User
length: { within: 0..255 },
format: { with: Gitlab::Regex.name_regex,
message: "only letters, digits, spaces & '_' '-' '.' allowed." }
+ validates :description, length: { within: 0..255 }
validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
author_id: record.author_id_of_changes
)
end
+
+ def after_merge(record, transition)
+ # Since MR can be merged via sidekiq
+ # to prevent event duplication do this check
+ return true if record.merge_event
+
+ Event.create(
+ project: record.project,
+ target_id: record.id,
+ target_type: record.class.name,
+ action: Event::MERGED,
+ author_id: record.author_id_of_changes
+ )
+ end
end
def create_note(issue)
Note.create_status_change_note(issue, current_user, issue.state)
- [issue.author, issue.assignee].compact.each do |recipient|
+ [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
# Collect data for this git push
@push_data = post_receive_data(oldrev, newrev, ref)
+ create_push_event
+
project.ensure_satellite_exists
project.discover_default_branch
project.execute_hooks(@push_data.dup)
project.execute_services(@push_data.dup)
end
+ end
- create_push_event
+ # This method provide a sample data
+ # generated with post_receive_data method
+ # for given project
+ #
+ def sample_data(project, user)
+ @project, @user = project, user
+ commits = project.repository.commits(project.default_branch, nil, 3)
+ post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}")
end
protected
--- /dev/null
+# ProjectTransferService class
+#
+# Used for transfer project to another namespace
+#
+class ProjectTransferService
+ attr_accessor :project
+
+ def transfer(project, new_namespace)
+ Project.transaction do
+ old_namespace = project.namespace
+ project.namespace = new_namespace
+
+ old_dir = old_namespace.try(:path) || ''
+ new_dir = new_namespace.try(:path) || ''
+
+ old_repo = if old_dir.present?
+ File.join(old_dir, project.path)
+ else
+ project.path
+ end
+
+ if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present?
+ raise TransferError.new("Project with same path in target namespace already exists")
+ end
+
+ Gitlab::ProjectMover.new(project, old_dir, new_dir).execute
+
+ project.save!
+ end
+ rescue Gitlab::ProjectMover::ProjectMoveError => ex
+ raise Project::TransferError.new(ex.message)
+ end
+end
+
-%h3.page_title Rename Group
+%h3.page_title Edit Group
%hr
= form_for [:admin, @group] do |f|
- if @group.errors.any?
.input
= f.text_field :name, placeholder: "Example Group", class: "xxlarge"
-
+ .clearfix.group-description-holder
+ = f.label :description, "Details"
+ .input
+ = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
.clearfix.group_name_holder
= f.label :path do
%li It will change the git path to repositories under this group.
.form-actions
- = f.submit 'Rename group', class: "btn btn-remove"
+ = f.submit 'Edit group', class: "btn btn-remove"
= link_to 'Cancel', admin_groups_path, class: "btn btn-cancel"
Name
%i.icon-sort-down
%th Path
+ %th Description
%th Projects
%th Owner
%th.cred Danger Zone!
%tr
%td
%strong= link_to group.name, [:admin, group]
+ %td= group.description
%td= group.path
%td= group.projects.count
%td
= link_to group.owner_name, admin_user_path(group.owner)
%td.bgred
- = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
+ = link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
= link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
= paginate @groups, theme: "admin"
Group name is
.input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
-
- = f.submit 'Create group', class: "btn btn-primary"
+ .clearfix.group-description-holder
+ = f.label :description, "Details"
+ .input
+ = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+ .form-actions
+ = f.submit 'Create group', class: "btn btn-primary"
+
%hr
.padded
%ul
= link_to edit_admin_group_path(@group), class: "btn btn-small pull-right" do
%i.icon-edit
- Rename
+ Edit
+ %tr
+ %td
+ %b
+ Description:
+ %td
+ = @group.description
%tr
%td
%b
= 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
-%h3.page_title Rename Team
+%h3.page_title Edit Team
%hr
= form_for @team, url: admin_team_path(@team), method: :put do |f|
- if @team.errors.any?
.input
= f.text_field :name, placeholder: "Example Team", class: "xxlarge"
+ .clearfix.team-description-holder
+ = f.label :description, "Details"
+ .input
+ = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
.clearfix.team_name_holder
= f.label :path do
%span.cred Team path is
%li It will change web url for access team and team projects.
.form-actions
- = f.submit 'Rename team', class: "btn btn-remove"
+ = f.submit 'Edit team', class: "btn btn-remove"
= link_to 'Cancel', admin_teams_path, class: "btn btn-cancel"
%th
Name
%i.icon-sort-down
+ %th Description
%th Path
%th Projects
%th Members
%tr
%td
%strong= link_to team.name, admin_team_path(team)
+ %td= team.description
%td= team.path
%td= team.projects.count
%td= team.members.count
%td
= link_to team.owner.name, admin_user_path(team.owner)
%td.bgred
- = link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
+ = link_to 'Edit', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
= link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
= paginate @teams, theme: "admin"
Team name is
.input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
-
- = f.submit 'Create team', class: "btn btn-primary"
+
+ .clearfix.team-description-holder
+ = f.label :description, "Details"
+ .input
+ = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+ .form-actions
+ = f.submit 'Create team', class: "btn btn-primary"
+
%hr
.padded
%ul
= link_to edit_admin_team_path(@team), class: "btn btn-small pull-right" do
%i.icon-edit
- Rename
+ Edit
+ %tr
+ %td
+ %b
+ Description:
+ %td
+ = @team.description
%tr
%td
%b
= event.project_name
.event-body
- %i.icon-comment-alt.event-note-icon
- %span.event-note
- = markdown truncate(event.target.note, length: 70)
+ .event-note
+ .md
+ %i.icon-comment-alt.event-note-icon
+ = sanitize(markdown(truncate(event.target.note, length: 150)), tags: %w(a img b pre p))
- note = event.target
- if note.attachment.url
= link_to note.attachment.secure_url, target: "_blank", class: 'note-file-attach' do
Group name is
.input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
-
- = f.submit 'Save group', class: "btn btn-save"
+
+ .clearfix.group-description-holder
+ = f.label :description, "Details"
+ .input
+ = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+ .form-actions
+ = f.submit 'Save group', class: "btn btn-save"
+
%hr
Group name is
.input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
-
- = f.submit 'Create group', class: "btn btn-create"
- %hr
+
+ .clearfix.group-description-holder
+ = f.label :description, "Details"
+ .input
+ = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+ .form-actions
+ = f.submit 'Create group', class: "btn btn-create"
+
+
.padded
%ul
%li Group is kind of directory for several projects
%p.nothing_here_message Project activity will be displayed here
.loading.hide
.side.span4
+ - if @group.description.present?
+ .description.well.light
+ = @group.description
= render "projects", projects: @projects
%div
%span.rss-icon
Open
%li{class: ("active" if params[:status] == 'assigned-to-me')}
= link_to project_issues_path(@project, status: 'assigned-to-me') do
- Assigned To Me
+ Assigned to me
+ %li{class: ("active" if params[:status] == 'created-by-me')}
+ = link_to project_issues_path(@project, status: 'created-by-me') do
+ Created by me
%li{class: ("active" if params[:status] == 'closed')}
= link_to project_issues_path(@project, status: 'closed') do
Closed
- else
= link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue"
- - if can?(current_user, :admin_issue, @issue)
= link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do
%i.icon-edit
- Edit
+ Edit
.pull-right
.span3#votes= render 'votes/votes_block', votable: @issue
-- if text = alert || notice
- #flash-container
- %h4= text
+.flash-container
+ - if alert
+ .alert
+ %span= alert
+
+ - elsif notice
+ .alert.alert-info
+ %span= notice
= stylesheet_link_tag "application"
= javascript_include_tag "application"
= csrf_meta_tags
+ = include_gon
-# Atom feed
- if current_user
%span.separator
%h1.project_name= title
%ul.nav
+ %li
+ = link_to public_root_path, title: "Public area", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
+ %i.icon-globe
- if current_user.is_admin?
%li
= link_to admin_root_path, title: "Admin area", class: 'has_bottom_tooltip', 'data-original-title' => 'Admin area' do
%html{ lang: "en"}
= render "layouts/head", title: "Admin area"
%body{class: "#{app_theme} admin"}
- = render "layouts/flash"
= render "layouts/head_panel", title: "Admin area"
+ = render "layouts/flash"
.container
%ul.main_menu
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
%html{ lang: "en"}
= render "layouts/head", title: "Dashboard"
%body{class: "#{app_theme} application"}
- = render "layouts/flash"
= render "layouts/head_panel", title: "Dashboard"
+ = render "layouts/flash"
.container
%ul.main_menu
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
= render "layouts/head"
%body.ui_basic.login-page
= render "layouts/flash"
- .container= yield
+ .container
+ .content
+ = yield
%html{ lang: "en"}
= render "layouts/head", title: "Error"
%body{class: "#{app_theme} application"}
- = render "layouts/flash"
= render "layouts/head_panel", title: ""
+ = render "layouts/flash"
.container
.content
%center.padded.prepend-top-20
%html{ lang: "en"}
= render "layouts/head", title: "#{@group.name}"
%body{class: "#{app_theme} application"}
- = render "layouts/flash"
= render "layouts/head_panel", title: "group: #{@group.name}"
+ = render "layouts/flash"
.container
%ul.main_menu
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
%html{ lang: "en"}
= render "layouts/head", title: "Profile"
%body{class: "#{app_theme} profile"}
- = render "layouts/flash"
= render "layouts/head_panel", title: "Profile"
+ = render "layouts/flash"
.container
%ul.main_menu
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
%html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} project"}
- = render "layouts/flash"
= render "layouts/head_panel", title: project_title(@project)
+ = render "layouts/flash"
- if can?(current_user, :download_code, @project)
= render 'shared/no_ssh'
= nav_link(controller: %w(graph)) do
= link_to "Network", project_graph_path(@project, @ref || @repository.root_ref)
- - if @project.issues_enabled
+ - if @project.issues_enabled
= nav_link(controller: %w(issues milestones labels)) do
- = link_to project_issues_filter_path(@project) do
+ = link_to url_for_project_issues do
Issues
- %span.count.issue_counter= @project.issues.opened.count
+ - 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
%html{ lang: "en"}
= render "layouts/head", title: "#{@team.name}"
%body{class: "#{app_theme} application"}
- = render "layouts/flash"
= render "layouts/head_panel", title: "team: #{@team.name}"
+ = render "layouts/flash"
.container
%ul.main_menu
= nav_link(path: 'teams#show', html_options: {class: 'home'}) do
$(function(){
merge_request = new MergeRequest({
url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}",
- check_enable: #{@merge_request.merge_status == MergeRequest::UNCHECKED ? "true" : "false"},
+ check_enable: #{@merge_request.unchecked? ? "true" : "false"},
url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}",
ci_enable: #{@project.gitlab_ci? ? "true" : "false"},
- current_status: "#{@merge_request.human_merge_status}",
+ current_status: "#{@merge_request.merge_status_name}",
action: "#{controller.action_name}"
});
});
-- unless can?(current_user, :accept_mr, @project)
+- unless @allowed_to_merge
.alert
- %strong Only masters can accept MR
+ %strong You don't have enough permissions to merge this MR
-- if @merge_request.opened? && @commits.any? && can?(current_user, :accept_mr, @project)
+- if @show_merge_controls
.automerge_widget.can_be_merged{style: "display:none"}
.alert.alert-success
%span
%br/
.content
%table
- - each_diff_line(diff, note.diff_file_index) do |line, type, line_code, line_new, line_old|
+ - each_diff_line_near(diff, note.diff_file_index, note.line_code) do |line, type, line_code, line_new, line_old|
%tr.line_holder{ id: line_code }
- if type == "match"
%td.old_line= "..."
- if line_code == note.line_code
= render "notes/diff_notes_with_reply", notes: discussion_notes
- - break # cut off diff after notes
--- /dev/null
+Issue was <%= @issue_status %> by <%= @updated_by.name %>
+
+Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %>
+
--- /dev/null
+New Issue was created and assigned to you.
+
+
+Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %>
--- /dev/null
+New Merge Request <%= @merge_request.id %>
+
+<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %>
+
+
+Branches: <%= @merge_request.source_branch %> to <%= @merge_request.target_branch %>
+Author: <%= @merge_request.author_name %>
+Asignee: <%= @merge_request.assignee_name %>
+
--- /dev/null
+Hi <%= @user.name %>!
+
+Administrator created account for you. Now you are a member of company GitLab application.
+
+login.................. <%= @user.email %>
+<% unless Gitlab.config.gitlab.signup_enabled %>
+ password............... <%= @password %>
+<% end %>
+
+Click here to login: <%= url_for(root_url) %>
--- /dev/null
+New comment for Commit <%= @commit.short_id %>
+
+<%= url_for(project_commit_url(@note.project, id: @commit.id, anchor: "note_#{@note.id}")) %>
+
+
+Author: <%= @note.author_name %>
+
+<%= @note.note %>
+
--- /dev/null
+New comment for Issue <%= @issue.id %>
+
+<%= url_for(project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}")) %>
+
+
+Author: <%= @note.author_name %>
+
+<%= @note.note %>
+
--- /dev/null
+New comment for Merge Request <%= @merge_request.id %>
+
+<%= url_for(project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}")) %>
+
+
+<%= @note.author_name %>
+
+<%= @note.note %>
+
--- /dev/null
+New message on the project wall <%= @note.project %>
+
+<%= url_for(wall_project_url(@note.project, anchor: "note_#{@note.id}")) %>
+
+
+<%= @note.author_name %>
+
+<%= @note.note %>
+
--- /dev/null
+
+You have been granted <%= @users_project.project_access_human %> access to project <%= @project.name_with_namespace %>
+
+<%= url_for(project_url(@project)) %>
--- /dev/null
+Project was moved to another location
+
+The project is now located under
+<%= url_for(link_to project_url(@project)) %>
+
+
+To update the remote url in your local repository run:
+ git remote set-url origin <%= @project.ssh_url_to_repo %>
--- /dev/null
+Reassigned Issue <%= @issue.id %>
+
+<%= url_for(project_issue_url(@issue.project, @issue)) %>
+
+
+Assignee changed from <%= @previous_assignee.name %> to <%= @issue.assignee_name %>
+
--- /dev/null
+Reassigned Merge Request <%= @merge_request.id %>
+
+<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %>
+
+
+Assignee changed from <%= @previous_assignee.name %> to <%= @merge_request.assignee_name %>
+
= f.check_box :issues_enabled
%span.descr Lightweight issue tracking system for this project
+ - if Project.issues_tracker.values.count > 1
+ .control-group
+ = 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?
+
.control-group
= f.label :merge_requests_enabled, "Merge Requests", class: 'control-label'
.controls
%fieldset
%legend Git global setup:
%pre.dark
- = preserve do
+ :preserve
git config --global user.name "#{current_user.name}"
git config --global user.email "#{current_user.email}"
%fieldset
%legend Create Repository
%pre.dark
- = preserve do
+ :preserve
mkdir #{@project.path}
cd #{@project.path}
git init
%fieldset
%legend Existing Git Repo?
%pre.dark
- = preserve do
+ :preserve
cd existing_git_repo
git remote add origin #{@project.url_to_repo}
git push -u origin master
.pull-right
%pre.dark.tiny git clone #{project.http_url_to_repo}
+ - unless @projects.present?
+ %h3.nothing_here_message No public projects
= paginate @projects, theme: "admin"
.input-prepend.project_clone_holder
%button{class: "btn active", :"data-clone" => @project.ssh_url_to_repo} SSH
%button{class: "btn", :"data-clone" => @project.http_url_to_repo}= Gitlab.config.gitlab.protocol.upcase
-
- = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select input-xxlarge"
+ = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select input-xxlarge", readonly: true
.input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xlarge left"
+ .clearfix.team-description-holder
+ = f.label :description, "Details"
+ .input
+ = f.text_area :description, maxlength: 250, class: "xlarge js-gfm-input", rows: 4
+
.clearfix
= f.label :path do
Team path is
.input
= f.text_field :path, placeholder: "opensource", class: "xlarge left"
+
.form-actions
- = f.submit 'Save team changes', class: "btn btn-save"
+ = f.submit 'Save team changes', class: "btn btn-primary"
.span5
.ui-box
%h5.title Remove team
%p
Removed team can not be restored!
= link_to 'Remove team', team_path(@team), method: :delete, confirm: "You are sure?", class: "btn btn-remove btn-small"
-
Team name is
.input
= f.text_field :name, placeholder: "Ex. Ruby Developers", class: "xxlarge left"
-
- = f.submit 'Create team', class: "btn btn-create"
- %hr
+
+ .clearfix.team-description-holder
+ = f.label :description, "Details"
+ .input
+ = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+ .form-actions
+ = f.submit 'Create team', class: "btn btn-create"
+
.padded
%ul
%li All created teams are public (users can view who enter into team and which project are assigned for this team)
%p.nothing_here_message Projects activity will be displayed here
.loading.hide
.side.span4
+ - if @team.description.present?
+ .description.well.light
+ = @team.description
= render "projects", projects: @projects
%div
%span.rss-icon
# # # # # # # # # # # # # # # # # #
-# Gitlab application config file #
+# GitLab application config file #
# # # # # # # # # # # # # # # # # #
#
# How to use:
# signup_enabled: true # default: false - Account passwords are not sent via the email if signup is enabled.
# username_changing_enabled: false # default: true - User can change her username/namespace
+
+ ## External issues trackers
+ issues_tracker:
+ redmine:
+ ## If not nil, link 'Issues' on project page will be replaced with this
+ ## Use placeholders:
+ ## :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"
+
## Gravatar
gravatar:
- enabled: true # Use user avatar images from Gravatar.com (default: true)
+ enabled: true # Use user avatar image from Gravatar.com (default: true)
# plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
# ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
- ## Omniauth settings
+ ## OmniAuth settings
omniauth:
- # Enable ability for users
- # Allow logging in via Twitter, Google, etc. using Omniauth providers
+ # Allow login via Twitter, Google, etc. using OmniAuth providers
enabled: false
# CAUTION!
- # This allows users to login without having a user account first (default: false)
+ # This allows users to login without having a user account first (default: false).
# User accounts will be created automatically when authentication was successful.
allow_single_sign_on: false
- # Locks down those users until they have been cleared by the admin (default: true)
+ # Locks down those users until they have been cleared by the admin (default: true).
block_auto_created_users: true
## Auth providers
- # Uncomment the lines and fill in the data of the auth provider you want to use
- # If your favorite auth provider is not listed you can user others:
+ # Uncomment the following lines and fill in the data of the auth provider you want to use
+ # If your favorite auth provider is not listed you can use others:
# see https://github.com/gitlabhq/gitlabhq/wiki/Using-Custom-Omniauth-Providers
# The 'app_id' and 'app_secret' parameters are always passed as the first two
# arguments, followed by optional 'args' which can be either a hash or an array.
upload_pack: true
receive_pack: true
- # If you use non-standart ssh port you need to specify it
+ # If you use non-standard ssh port you need to specify it
# ssh_port: 22
## Git settings
# Use the default values unless you really know what you are doing
git:
bin_path: /usr/bin/git
- # Max size of git object like commit, in bytes
- # This value can be increased if you have a very large commits
+ # Max size of a git object (e.g. a commit), in bytes
+ # This value can be increased if you have very large commits
max_size: 5242880 # 5.megabytes
- # Git timeout to read commit, in seconds
+ # Git timeout to read a commit, in seconds
timeout: 10
development:
test:
<<: *base
+ issues_tracker:
+ redmine:
+ project_url: "http://redmine/projects/:issues_tracker_id"
+ issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
staging:
<<: *base
Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil?
Settings.omniauth['providers'] ||= []
+Settings['issues_tracker'] ||= {}
+
#
# GitLab
#
# ==> Configuration for :validatable
# Range for password length. Default is 6..128.
- # config.password_length = 6..128
+ config.password_length = 6..128
# Email regex used to validate email formats. It simply asserts that
# an one (and only one) @ exists in the given string. This is mainly
unauthenticated: 'You need to sign in before continuing.'
unconfirmed: 'You have to confirm your account before continuing.'
locked: 'Your account is locked.'
+ not_found_in_database: 'Invalid email or password.'
invalid: 'Invalid email or password.'
invalid_token: 'Invalid authentication token.'
timeout: 'Your session expired, please sign in again to continue.'
#
# Attachments serving
#
- get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /[a-zA-Z.0-9_\-\+]+/ }
+ get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /.+/ }
#
# Admin Area
--- /dev/null
+class AddIssuesTrackerToProject < ActiveRecord::Migration
+ def change
+ add_column :projects, :issues_tracker, :string, default: :gitlab, null: false
+ end
+end
--- /dev/null
+class AddDescriptionToNamsespace < ActiveRecord::Migration
+ def change
+ add_column :namespaces, :description, :string, default: '', null: false
+ end
+end
--- /dev/null
+class AddDescriptionToTeams < ActiveRecord::Migration
+ def change
+ add_column :user_teams, :description, :string, default: '', null: false
+ end
+end
--- /dev/null
+class AddIssuesTrackerIdToProject < ActiveRecord::Migration
+ def change
+ add_column :projects, :issues_tracker_id, :string
+ end
+end
class ConvertClosedToStateInIssue < ActiveRecord::Migration
def up
Issue.transaction do
- Issue.where(closed: true).update_all("state = 'closed'")
- Issue.where(closed: false).update_all("state = 'opened'")
+ Issue.where(closed: true).update_all(state: :closed)
+ Issue.where(closed: false).update_all(state: :opened)
end
end
def down
Issue.transaction do
- Issue.where(state: :closed).update_all("closed = 1")
+ Issue.where(state: :closed).update_all(closed: true)
end
end
end
class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration
def up
MergeRequest.transaction do
- MergeRequest.where(closed: true, merged: true).update_all("state = 'merged'")
- MergeRequest.where(closed: true, merged: true).update_all("state = 'closed'")
- MergeRequest.where(closed: false).update_all("state = 'opened'")
+ MergeRequest.where(closed: true, merged: true).update_all(state: :merged)
+ MergeRequest.where(closed: true, merged: false).update_all(state: :closed)
+ MergeRequest.where(closed: false).update_all(state: :opened)
end
end
class ConvertClosedToStateInMilestone < ActiveRecord::Migration
def up
Milestone.transaction do
- Milestone.where(closed: false).update_all("state = 'opened'")
- Milestone.where(closed: false).update_all("state = 'active'")
+ Milestone.where(closed: true).update_all(state: :closed)
+ Milestone.where(closed: false).update_all(state: :active)
end
end
def down
Milestone.transaction do
- Milestone.where(state: :closed).update_all("closed = 1")
+ Milestone.where(state: :closed).update_all(closed: true)
end
end
end
--- /dev/null
+class AddNewMergeStatusToMergeRequest < ActiveRecord::Migration
+ def change
+ add_column :merge_requests, :new_merge_status, :string
+ end
+end
--- /dev/null
+class ConvertMergeStatusInMergeRequest < ActiveRecord::Migration
+ def up
+ MergeRequest.transaction do
+ MergeRequest.where(merge_status: 1).update_all("new_merge_status = 'unchecked'")
+ MergeRequest.where(merge_status: 2).update_all("new_merge_status = 'can_be_merged'")
+ MergeRequest.where(merge_status: 3).update_all("new_merge_status = 'cannot_be_merged'")
+ end
+ end
+
+ def down
+ MergeRequest.transaction do
+ MergeRequest.where(new_merge_status: :unchecked).update_all("merge_status = 1")
+ MergeRequest.where(new_merge_status: :can_be_merged).update_all("merge_status = 2")
+ MergeRequest.where(new_merge_status: :cannot_be_merged).update_all("merge_status = 3")
+ end
+ end
+end
--- /dev/null
+class RemoveMergeStatusFromMergeRequest < ActiveRecord::Migration
+ def up
+ remove_column :merge_requests, :merge_status
+ end
+
+ def down
+ add_column :merge_requests, :merge_status, :integer
+ end
+end
--- /dev/null
+class RenameNewMergeStatusToMergeStatusInMilestone < ActiveRecord::Migration
+ def change
+ rename_column :merge_requests, :new_merge_status, :merge_status
+ end
+end
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20130218141554) do
+ActiveRecord::Schema.define(:version => 20130220133245) do
create_table "events", :force => true do |t|
t.string "target_type"
add_index "keys", ["user_id"], :name => "index_keys_on_user_id"
create_table "merge_requests", :force => true do |t|
- t.string "target_branch", :null => false
- t.string "source_branch", :null => false
- t.integer "project_id", :null => false
+ t.string "target_branch", :null => false
+ t.string "source_branch", :null => false
+ t.integer "project_id", :null => false
t.integer "author_id"
t.integer "assignee_id"
t.string "title"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.text "st_commits", :limit => 2147483647
t.text "st_diffs", :limit => 2147483647
- t.integer "merge_status", :default => 1, :null => false
t.integer "milestone_id"
t.string "state"
+ t.string "merge_status"
end
add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id"
add_index "milestones", ["project_id"], :name => "index_milestones_on_project_id"
create_table "namespaces", :force => true do |t|
- t.string "name", :null => false
- t.string "path", :null => false
- t.integer "owner_id", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.string "name", :null => false
+ t.string "path", :null => false
+ t.integer "owner_id", :null => false
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.string "type"
+ t.string "description", :default => "", :null => false
end
add_index "namespaces", ["name"], :name => "index_namespaces_on_name"
t.string "name"
t.string "path"
t.text "description"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.integer "creator_id"
t.string "default_branch"
- t.boolean "issues_enabled", :default => true, :null => false
- t.boolean "wall_enabled", :default => true, :null => false
- t.boolean "merge_requests_enabled", :default => true, :null => false
- t.boolean "wiki_enabled", :default => true, :null => false
+ t.boolean "issues_enabled", :default => true, :null => false
+ t.boolean "wall_enabled", :default => true, :null => false
+ t.boolean "merge_requests_enabled", :default => true, :null => false
+ t.boolean "wiki_enabled", :default => true, :null => false
t.integer "namespace_id"
- t.boolean "public", :default => false, :null => false
+ t.boolean "public", :default => false, :null => false
+ t.string "issues_tracker", :default => "gitlab", :null => false
+ t.string "issues_tracker_id"
end
add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
t.string "name"
t.string "path"
t.integer "owner_id"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
+ t.string "description", :default => "", :null => false
end
create_table "users", :force => true do |t|
-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 Debian/Ubuntu and tested on it. Please read [`doc/install/requirements.md`](./requirements.md) for hardware and platform requirements.
+This installation guide is recommended to set up a production server. If you want a development environment please use the [Vargrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) since it makes it much easier to set up all the dependencies for integration testing.
**Important Note:**
The following steps have been known to work.
sudo adduser --disabled-login --gecos 'GitLab' git
+
# 4. GitLab shell
- # Login as git
+GitLab Shell is a ssh access and repository management software developed specially for GitLab.
+
+ # Login as git
sudo su git
- # Go to home directory
+ # Go to home directory
cd /home/git
# Clone gitlab shell
git clone https://github.com/gitlabhq/gitlab-shell.git
- # Setup
cd gitlab-shell
cp config.yml.example config.yml
- ./bin/install
+
+ # Edit config and replace gitlab_url
+ # with something like 'http://domain.com/'
+ vim config.yml
+
+ # Do setup
+ ./bin/install
# 5. Database
# Clone GitLab repository
sudo -u git -H git clone https://github.com/gitlabhq/gitlabhq.git gitlab
- # Go to gitlab dir
+ # Go to gitlab dir
cd /home/git/gitlab
-
+
# Checkout to stable release
sudo -u git -H git checkout 5-0-stable
# Create directory for pids and make sure GitLab can write to it
sudo -u git -H mkdir tmp/pids/
sudo chmod -R u+rwX tmp/pids/
-
+
# Copy the example Unicorn config
sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb
## Initialise Database and Activate Advanced Features
-
+
sudo -u git -H bundle exec rake db:setup RAILS_ENV=production
sudo -u git -H bundle exec rake db:seed_fu RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
## Custom SSH Connection
If you are running SSH on a non-standard port, you must change the gitlab user's SSH config.
-
+
# Add to /home/git/.ssh/config
host localhost # Give your setup a name (here: override localhost)
user git # Your remote git user
And 'submit form with new group info' do
fill_in 'group_name', :with => 'gitlab'
+ fill_in 'group_description', :with => 'Group description'
click_button "Create group"
end
Then 'I should see newly created group' do
page.should have_content "Group: gitlab"
+ page.should have_content "Group description"
end
Then 'I should be redirected to group page' do
And 'submit form with new team info' do
fill_in 'user_team_name', with: 'gitlab'
+ fill_in 'user_team_description', with: 'description'
click_button 'Create team'
end
And 'I should see newly created team' do
page.should have_content "Team: gitlab"
+ page.should have_content "description"
end
When 'I visit admin teams page' do
Then 'I should see merge requests from this group assigned to me' do
assigned_to_me(:merge_requests).each do |issue|
- page.should have_content issue.title
+ page.should have_content issue.title[0..80]
end
end
end
And 'submit form with new group info' do
- fill_in 'group_name', :with => 'Samurai'
+ fill_in 'group_name', with: 'Samurai'
+ fill_in 'group_description', with: 'Tokugawa Shogunate'
click_button "Create group"
end
Then 'I should see newly created group' do
page.should have_content "Samurai"
+ page.should have_content "Tokugawa Shogunate"
page.should have_content "You will only see events from projects in this group"
end
end
Then 'I should see closed merge request "Bug NS-04"' do
- mr = MergeRequest.find_by_title("Bug NS-04")
- mr.closed?.should be_true
+ merge_request = MergeRequest.find_by_title!("Bug NS-04")
+ merge_request.closed?.should be_true
page.should have_content "Closed by"
end
end
And 'project "Shop" have "Bug NS-04" open merge request' do
- project = Project.find_by_name("Shop")
create(:merge_request,
title: "Bug NS-04",
project: project,
end
And 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do
- project = Project.find_by_name("Shop")
create(:merge_request_with_diffs,
title: "Bug NS-05",
project: project,
end
And 'project "Shop" have "Feature NS-03" closed merge request' do
- project = Project.find_by_name("Shop")
create(:closed_merge_request,
title: "Feature NS-03",
project: project,
end
And 'I switch to the diff tab' do
- mr = MergeRequest.find_by_title("Bug NS-05")
- visit diffs_project_merge_request_path(mr.project, mr)
+ visit diffs_project_merge_request_path(project, merge_request)
end
And 'I switch to the merge request\'s comments tab' do
- mr = MergeRequest.find_by_title("Bug NS-05")
- visit project_merge_request_path(mr.project, mr)
+ visit project_merge_request_path(project, merge_request)
end
And 'I click on the first commit in the merge request' do
- mr = MergeRequest.find_by_title("Bug NS-05")
- click_link mr.commits.first.short_id(8)
+
+ click_link merge_request.commits.first.short_id(8)
end
And 'I leave a comment on the diff page' do
end
Then 'I should see a discussion has started on line 185' do
- mr = MergeRequest.find_by_title("Bug NS-05")
- first_commit = mr.commits.first
+ first_commit = merge_request.commits.first
first_diff = first_commit.diffs.first
page.should have_content "#{current_user.name} started a discussion on this merge request diff"
page.should have_content "#{first_diff.b_path}:L185"
end
Then 'I should see a discussion has started on commit bcf03b5de6c:L185' do
- mr = MergeRequest.find_by_title("Bug NS-05")
- first_commit = mr.commits.first
+ first_commit = merge_request.commits.first
first_diff = first_commit.diffs.first
page.should have_content "#{current_user.name} started a discussion on commit"
page.should have_content first_commit.short_id(8)
end
Then 'I should see a discussion has started on commit bcf03b5de6c' do
- mr = MergeRequest.find_by_title("Bug NS-05")
- first_commit = mr.st_commits.first
+ first_commit = merge_request.st_commits.first
first_diff = first_commit.diffs.first
page.should have_content "#{current_user.name} started a discussion on commit bcf03b5de6c"
page.should have_content first_commit.short_id(8)
page.should have_content "One comment to rule them all"
page.should have_content "#{first_diff.b_path}:L185"
end
+
+ def project
+ @project ||= Project.find_by_name!("Shop")
+ end
+
+ def merge_request
+ @merge_request ||= MergeRequest.find_by_title!("Bug NS-05")
+ end
end
And 'I submit form with new team info' do
fill_in 'name', with: 'gitlab'
+
+ fill_in 'user_team_description', with: 'team description'
click_button 'Create team'
end
+ And 'I should see newly created team' do
+ page.should have_content "gitlab"
+ page.should have_content "team description"
+ end
+
Then 'I should be redirected to new team page' do
team = UserTeam.last
current_path.should == team_path(team)
Gitlab.config.gitlab_shell.stub(repos_path: Rails.root.join('tmp', 'test-git-base-path'))
FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path
+ DatabaseCleaner.start
end
Spinach.hooks.after_scenario do
When I click to "New team" link
And I submit form with new team info
Then I should be redirected to new team page
+ Then I should see newly created team
Scenario: I should see team dashboard list
When I have teams with projects and members
end
format :json
- error_format :json
helpers APIHelpers
mount Groups
#
# Check if ssh key has access to project code
#
+ # Params:
+ # key_id - SSH Key id
+ # project - project path with namespace
+ # action - git action (git-upload-pack or git-receive-pack)
+ # ref - branch name
+ #
get "/allowed" do
key = Key.find(params[:key_id])
project = Project.find_with_namespace(params[:project])
:issues_enabled,
:wall_enabled,
:merge_requests_enabled,
- :wiki_enabled]
+ :wiki_enabled,
+ :namespace_id]
@project = ::Projects::CreateContext.new(current_user, attrs).execute
if @project.saved?
present @project, with: Entities::Project
authorize! :download_code, user_project
page = params[:page] || 0
- per_page = params[:per_page] || 20
+ per_page = (params[:per_page] || 20).to_i
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
commits = user_project.repository.commits(ref, nil, per_page, page * per_page)
@tree = TreeDecorator.new(@tree)
raise InvalidPathError if @tree.invalid?
- rescue NoMethodError, InvalidPathError
+ rescue RuntimeError, NoMethodError, InvalidPathError
not_found!
end
end
# >> gfm(":trollface:")
# => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
module Markdown
+ include IssuesHelper
+
attr_reader :html_options
# Public: Parse the provided text with GitLab-Flavored Markdown
end
def reference_issue(identifier)
- if issue = @project.issues.where(id: identifier).first
- link_to("##{identifier}", project_issue_url(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}"))
+ if @project.issue_exists? identifier
+ url = url_for_issue(identifier)
+ title = title_for_issue(identifier)
+
+ link_to("##{identifier}", url, html_options.merge(title: "Issue: #{title}", class: "gfm gfm-issue #{html_options[:class]}"))
end
end
namespace :sidekiq do
desc "GITLAB | Stop sidekiq"
task :stop do
- run "bundle exec sidekiqctl stop #{pidfile}"
+ system "bundle exec sidekiqctl stop #{pidfile}"
end
desc "GITLAB | Start sidekiq"
task :start do
- run "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &"
+ system "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &"
end
-
+
desc "GITLAB | Start sidekiq with launchd on Mac OS X"
task :launchd do
- run "bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1"
+ system "bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1"
end
-
+
def pidfile
Rails.root.join("tmp", "pids", "sidekiq.pid")
end
creator
end
+ factory :redmine_project, parent: :project do
+ issues_tracker { "redmine" }
+ issues_tracker_id { "project_name_in_redmine" }
+ end
+
factory :group do
sequence(:name) { |n| "group#{n}" }
path { name.downcase.gsub(/\s/, '_') }
FactoryGirl.define do
factory :user_team do
sequence(:name) { |n| "team#{n}" }
+ sequence(:description) { |n| "team_description#{n}" }
path { name.downcase.gsub(/\s/, '_') }
owner
end
user = User.last
email = ActionMailer::Base.deliveries.last
email.subject.should have_content("Account was created")
- email.body.should have_content(user.email)
- email.body.should have_content(@password)
+ email.text_part.body.should have_content(user.email)
+ email.text_part.body.should have_content(@password)
end
end
user = User.last
email = ActionMailer::Base.deliveries.last
email.subject.should have_content("Account was created")
- email.body.should have_content(user.email)
- email.body.should_not have_content(@password)
+ email.text_part.body.should have_content(user.email)
+ email.text_part.body.should_not have_content(@password)
end
end
end
describe GitlabMarkdownHelper do
include ApplicationHelper
+ include IssuesHelper
let!(:project) { create(:project) }
--- /dev/null
+require "spec_helper"
+
+describe IssuesHelper do
+ let(:project) { create :project }
+ let(:issue) { create :issue, project: project }
+ let(:ext_project) { create :redmine_project }
+
+ describe :title_for_issue do
+ it "should return issue title if used internal tracker" do
+ @project = project
+ title_for_issue(issue.id).should eq issue.title
+ end
+
+ it "should always return empty string if used external tracker" do
+ @project = ext_project
+ title_for_issue(rand(100)).should eq ""
+ end
+
+ it "should always return empty string if project nil" do
+ @project = nil
+
+ title_for_issue(rand(100)).should eq ""
+ end
+ end
+
+ describe :url_for_project_issues do
+ let(:project_url) { Gitlab.config.issues_tracker.redmine.project_url}
+ let(:ext_expected) do
+ project_url.gsub(':project_id', ext_project.id.to_s)
+ .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
+ end
+ let(:int_expected) { polymorphic_path([project]) }
+
+ it "should return internal path if used internal tracker" do
+ @project = project
+ url_for_project_issues.should match(int_expected)
+ end
+
+ it "should return path to external tracker" do
+ @project = ext_project
+
+ url_for_project_issues.should match(ext_expected)
+ end
+
+ it "should return empty string if project nil" do
+ @project = nil
+
+ url_for_project_issues.should eq ""
+ end
+ end
+
+ describe :url_for_issue do
+ let(:issue_id) { 3 }
+ let(:issues_url) { Gitlab.config.issues_tracker.redmine.issues_url}
+ let(:ext_expected) do
+ issues_url.gsub(':id', issue_id.to_s)
+ .gsub(':project_id', ext_project.id.to_s)
+ .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
+ end
+ let(:int_expected) { polymorphic_path([project, issue]) }
+
+ it "should return internal path if used internal tracker" do
+ @project = project
+ url_for_issue(issue.id).should match(int_expected)
+ end
+
+ it "should return path to external tracker" do
+ @project = ext_project
+
+ url_for_issue(issue_id).should match(ext_expected)
+ end
+
+ it "should return empty string if project nil" do
+ @project = nil
+
+ url_for_issue(issue.id).should eq ""
+ end
+ end
+end
--- /dev/null
+require 'spec_helper'
+
+describe 'Gitlab::Popen', no_db: true do
+ let (:path) { Rails.root.join('tmp').to_s }
+
+ before do
+ @klass = Class.new(Object)
+ @klass.send(:include, Gitlab::Popen)
+ end
+
+ context 'zero status' do
+ before do
+ @output, @status = @klass.new.popen('ls', path)
+ end
+
+ it { @status.should be_zero }
+ it { @output.should include('cache') }
+ end
+
+ context 'non-zero status' do
+ before do
+ @output, @status = @klass.new.popen('cat NOTHING', path)
+ end
+
+ it { @status.should == 1 }
+ it { @output.should include('No such file or directory') }
+ end
+end
+
it { should_not allow_mass_assignment_of(:project_id) }
end
+ describe "Respond to" do
+ it { should respond_to(:unchecked?) }
+ it { should respond_to(:can_be_merged?) }
+ it { should respond_to(:cannot_be_merged?) }
+ end
+
describe 'modules' do
it { should include_module(Issuable) }
end
it { should ensure_inclusion_of(:wall_enabled).in_array([true, false]) }
it { should ensure_inclusion_of(:merge_requests_enabled).in_array([true, false]) }
it { should ensure_inclusion_of(:wiki_enabled).in_array([true, false]) }
+ it { should ensure_length_of(:issues_tracker_id).is_within(0..255) }
it "should not allow new projects beyond user limits" do
project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1))
Project.new(path: "empty").repository.should be_nil
end
end
+
+ describe :issue_exists? do
+ let(:project) { create(:project) }
+ let(:existed_issue) { create(:issue, project: project) }
+ let(:not_existed_issue) { create(:issue) }
+ let(:ext_project) { create(:redmine_project) }
+
+ it "should be true or if used internal tracker and issue exists" do
+ project.issue_exists?(existed_issue.id).should be_true
+ end
+
+ it "should be false or if used internal tracker and issue not exists" do
+ project.issue_exists?(not_existed_issue.id).should be_false
+ end
+
+ it "should always be true if used other tracker" do
+ ext_project.issue_exists?(rand(100)).should be_true
+ end
+ end
+
+ describe :used_default_issues_tracker? do
+ let(:project) { create(:project) }
+ let(:ext_project) { create(:redmine_project) }
+
+ it "should be true if used internal tracker" do
+ project.used_default_issues_tracker?.should be_true
+ end
+
+ it "should be false if used other tracker" do
+ ext_project.used_default_issues_tracker?.should be_false
+ end
+ end
+
+ describe :can_have_issues_tracker_id? do
+ let(:project) { create(:project) }
+ let(:ext_project) { create(:redmine_project) }
+
+ 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
+
+ it "should be false for projects with internal issue tracker if issues enabled" do
+ project.can_have_issues_tracker_id?.should be_false
+ end
+
+ it "should be always false if issues disbled" do
+ project.issues_enabled = false
+ ext_project.issues_enabled = false
+
+ project.can_have_issues_tracker_id?.should be_false
+ ext_project.can_have_issues_tracker_id?.should be_false
+ end
+ end
end
it { should respond_to(:masters) }
it { should respond_to(:reporters) }
it { should respond_to(:guests) }
- it { should respond_to(:repository_writers) }
- it { should respond_to(:repository_masters) }
- it { should respond_to(:repository_readers) }
end
end
describe "Respond to" do
it { should respond_to(:is_admin?) }
- it { should respond_to(:identifier) }
it { should respond_to(:name) }
it { should respond_to(:private_token) }
end
- describe '#identifier' do
- it "should return valid identifier" do
- user = build(:user, email: "test@mail.com")
- user.identifier.should == "test_mail_com"
- end
-
- it "should return identifier without + sign" do
- user = build(:user, email: "test+foo@mail.com")
- user.identifier.should == "test_foo_mail_com"
- end
-
- it "should conform to Gitolite's required identifier pattern" do
- user = build(:user, email: "_test@example.com")
- user.identifier.should == 'test_example_com'
- end
- end
-
describe '#generate_password' do
it "should execute callback when force_random_password specified" do
user = build(:user, force_random_password: true)
--- /dev/null
+require 'spec_helper'
+
+describe Gitlab::API do
+ include ApiHelpers
+
+ let(:user) { create(:user) }
+ let(:key) { create(:key, user: user) }
+ let(:project) { create(:project) }
+
+ describe "GET /internal/check", no_db: true do
+ it do
+ get api("/internal/check")
+
+ response.status.should == 200
+ json_response['api_version'].should == Gitlab::API.version
+ end
+ end
+
+ describe "GET /internal/discover" do
+ it do
+ get(api("/internal/discover"), key_id: key.id)
+
+ response.status.should == 200
+
+ json_response['email'].should == user.email
+ end
+ end
+
+ describe "GET /internal/allowed" do
+ context "access granted" do
+ before do
+ project.team << [user, :developer]
+ end
+
+ context "git pull" do
+ it do
+ get(
+ api("/internal/allowed"),
+ ref: 'master',
+ key_id: key.id,
+ project: project.path_with_namespace,
+ action: 'git-upload-pack'
+ )
+
+ response.status.should == 200
+ response.body.should == 'true'
+ end
+ end
+
+ context "git push" do
+ it do
+ get(
+ api("/internal/allowed"),
+ ref: 'master',
+ key_id: key.id,
+ project: project.path_with_namespace,
+ action: 'git-receive-pack'
+ )
+
+ response.status.should == 200
+ response.body.should == 'true'
+ end
+ end
+ end
+
+ context "access denied" do
+ before do
+ project.team << [user, :guest]
+ end
+
+ context "git pull" do
+ it do
+ get(
+ api("/internal/allowed"),
+ ref: 'master',
+ key_id: key.id,
+ project: project.path_with_namespace,
+ action: 'git-upload-pack'
+ )
+
+ response.status.should == 200
+ response.body.should == 'false'
+ end
+ end
+
+ context "git push" do
+ it do
+ get(
+ api("/internal/allowed"),
+ ref: 'master',
+ key_id: key.id,
+ project: project.path_with_namespace,
+ action: 'git-receive-pack'
+ )
+
+ response.status.should == 200
+ response.body.should == 'false'
+ end
+ end
+ end
+
+ end
+end
DatabaseCleaner.strategy = :transaction
end
- DatabaseCleaner.start
+ unless example.metadata[:no_db]
+ DatabaseCleaner.start
+ end
end
config.after do
- DatabaseCleaner.clean
+ unless example.metadata[:no_db]
+ DatabaseCleaner.clean
+ end
end
end